From ac24fdcba8c6f00b4945b61b7a15efe152556e5e Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:59:53 -0500 Subject: [PATCH] release-2.4.0 (#913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: continued adding features to the release.yml * feat: added auto youtube submit * feat: add dry run to release workflow * feat: improve release workflow mode system with new dropdown and overwrite release mode * fix: make credentials start collapsed * fix: make hostname fill entire column and truncate at proper spot in dashboard * fix: guacamole display using an incorrect height * feat: add folder management (nested folders, folder icons, folder colors, improved folder selection, etc.) * feat: move ssh host config outside of top tab bar and into new tab bar visible when on SSH tab * fix: 2FA failing to disable * feat: Proxmox guest discovery and import (#881) * feat(db): add Proxmox host fields and schema migration Add enableProxmox flag and proxmoxConfig JSON to the hosts table so an SSH host can be marked as a Proxmox node and carry per-host discovery settings (default credential, Windows/Docker name patterns, preferred IP prefixes). Includes addColumnIfNotExists migration entries and the matching SSHHost / HostData / Host type definitions. * feat(api): add Proxmox guest discovery endpoint Add POST /proxmox/discover which connects to a Proxmox node over SSH using the host's existing stored credentials (no separate API token), runs pvesh to enumerate cluster guests, and returns VMs and LXC containers ready to import. Guest IPs are resolved with bounded concurrency (LXC from net config, QEMU via guest agent) to stay within the node's SSH MaxSessions; configured preferred prefixes pick the address when a guest has several. Name patterns flag Windows guests as RDP and enable Docker. Node names are validated before use in shell commands and the SSH connection is always closed via try/finally. The endpoint carries an OpenAPI annotation. Persists the new host fields across host create/update/select, the normalizers (transformHostResponse) and the bulk routes, with normalizer test coverage. * feat(ui): add Proxmox host settings and guest import Add a Proxmox tab to the host editor (following the Docker tab pattern) to enable the integration and configure the default credential, name patterns and preferred IP ranges. Proxmox-enabled hosts show a discover action in the sidebar that opens an import dialog listing the cluster's guests with multi-select; the import reuses the host's credential, maps Windows guests to RDP, and groups imported guests into a folder named after the Proxmox host. Also exposes the import via the hosts import/export menu. All user-facing strings are added to the en.json locale and rendered via i18n. Built with the existing shadcn Select, dialog and section-card components to match the dark theme. * feat(ui): add Proxmox to the bulk feature toggle Proxmox was the only host feature missing from the multi-select "Features" menu. Enabling it in bulk is special: unlike the other toggles it needs a credential to be useful, so each host now defaults its Proxmox credential to the credential already stored on that host (host.credentialId). Discovery then works right away without picking one by hand, and per-host settings can still be adjusted afterwards. - SidebarTree: Enable/Disable Proxmox entries (Boxes icon) in the bulk Features dropdown - host-bulk-routes: enabling Proxmox runs per-host so each host keeps its own default credential; existing proxmoxConfig is preserved; disabling stays a simple flag flip - en.json: enableProxmoxFeature / disableProxmoxFeature Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: LukeGus Co-authored-by: Claude Opus 4.8 * fix: reliable OIDC group syncing for admin roles (#882) - always fetch from the userinfo endpoint to get extra claims (like `groups` from Authelia) instead of skipping it if the id_token was already parsed - handle cases where the OIDC provider sends groups as a single string, a comma-separated string - add debug logging * fix: incorrect guacd screen height * fix: guacd black screen on connnection * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * feat: initial server stats rewrite (renamed to host metrics) * feat: continue renaming and improving host metrics features and UI * fix: clipboard copy fails in Brave/non-HTTPS, dashboard total credentials stuck at 0, host username ignored when credential attached, cloned host can't switch auth method, file-manager context menu off-screen, file delete affecting inactive tabs, silent delete failure on Windows hosts, iPad host tap does nothing, OIDC custom group claim support * chore: update dependabot for my app and add auto merge/branch update * chore: update dependabot for my app and add auto merge/branch update * Feat/docker release tag (#894) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * feat(docker): add version tag to production Docker image tags --------- Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus * fix: disable hardware acceleration on Windows to prevent startup crash (#895) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * fix: disable hardware acceleration on Windows to prevent startup crash --------- Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus * chore(deps-dev): bump the dev-patch-updates group across 1 directory with 23 updates (#893) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps-dev): bump the dev-patch-updates group across 1 directory with 23 updates Bumps the dev-patch-updates group with 22 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@codemirror/autocomplete](https://github.com/codemirror/autocomplete) | `6.20.2` | `6.20.3` | | [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `21.0.1` | `21.0.2` | | [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `21.0.1` | `21.0.2` | | [@radix-ui/react-accordion](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/accordion) | `1.2.12` | `1.2.13` | | [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog) | `1.1.15` | `1.1.16` | | [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox) | `1.3.3` | `1.3.4` | | [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.15` | `1.1.16` | | [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.16` | `2.1.17` | | [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.8` | `2.1.9` | | [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) | `1.1.15` | `1.1.16` | | [@radix-ui/react-progress](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/progress) | `1.1.8` | `1.1.9` | | [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/scroll-area) | `1.2.10` | `1.2.11` | | [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.8` | `1.1.9` | | [@radix-ui/react-tabs](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs) | `1.1.13` | `1.1.14` | | [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.8` | `1.2.9` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.1` | `25.9.2` | | [i18next](https://github.com/i18next/i18next) | `26.3.0` | `26.3.1` | | [lint-staged](https://github.com/lint-staged/lint-staged) | `17.0.5` | `17.0.7` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.6` | `19.2.7` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.15` | `19.2.17` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.6` | `19.2.7` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.14` | `8.0.16` | Updates `@codemirror/autocomplete` from 6.20.2 to 6.20.3 - [Changelog](https://github.com/codemirror/autocomplete/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/autocomplete/commits) Updates `@commitlint/cli` from 21.0.1 to 21.0.2 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/cli) Updates `@commitlint/config-conventional` from 21.0.1 to 21.0.2 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.0.2/@commitlint/config-conventional) Updates `@radix-ui/react-accordion` from 1.2.12 to 1.2.13 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/accordion/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/accordion) Updates `@radix-ui/react-alert-dialog` from 1.1.15 to 1.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/alert-dialog) Updates `@radix-ui/react-checkbox` from 1.3.3 to 1.3.4 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/checkbox) Updates `@radix-ui/react-dialog` from 1.1.15 to 1.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog) Updates `@radix-ui/react-dropdown-menu` from 2.1.16 to 2.1.17 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu) Updates `@radix-ui/react-label` from 2.1.8 to 2.1.9 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label) Updates `@radix-ui/react-popover` from 1.1.15 to 1.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover) Updates `@radix-ui/react-progress` from 1.1.8 to 1.1.9 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/progress/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/progress) Updates `@radix-ui/react-scroll-area` from 1.2.10 to 1.2.11 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/scroll-area) Updates `@radix-ui/react-separator` from 1.1.8 to 1.1.9 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator) Updates `@radix-ui/react-slot` from 1.2.4 to 1.2.5 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot) Updates `@radix-ui/react-tabs` from 1.1.13 to 1.1.14 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tabs) Updates `@radix-ui/react-tooltip` from 1.2.8 to 1.2.9 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip) Updates `@types/node` from 25.9.1 to 25.9.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `i18next` from 26.3.0 to 26.3.1 - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/i18next/compare/v26.3.0...v26.3.1) Updates `lint-staged` from 17.0.5 to 17.0.7 - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.0.5...v17.0.7) Updates `react` from 19.2.6 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react) Updates `@types/react` from 19.2.15 to 19.2.17 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.2.6 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react-dom) Updates `vite` from 8.0.14 to 8.0.16 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite) --- updated-dependencies: - dependency-name: "@codemirror/autocomplete" dependency-version: 6.20.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@commitlint/cli" dependency-version: 21.0.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@commitlint/config-conventional" dependency-version: 21.0.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-accordion" dependency-version: 1.2.13 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-alert-dialog" dependency-version: 1.1.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-checkbox" dependency-version: 1.3.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-dialog" dependency-version: 1.1.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.17 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-label" dependency-version: 2.1.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-popover" dependency-version: 1.1.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-progress" dependency-version: 1.1.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-scroll-area" dependency-version: 1.2.11 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-separator" dependency-version: 1.1.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-slot" dependency-version: 1.2.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.14 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-tooltip" dependency-version: 1.2.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@types/node" dependency-version: 25.9.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: i18next dependency-version: 26.3.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: lint-staged dependency-version: 17.0.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react dependency-version: 19.2.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@types/react" dependency-version: 19.2.17 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react-dom dependency-version: 19.2.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: vite dependency-version: 8.0.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump undici from 7.26.0 to 8.4.0 (#891) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump undici from 7.26.0 to 8.4.0 Bumps [undici](https://github.com/nodejs/undici) from 7.26.0 to 8.4.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.26.0...v8.4.0) --- updated-dependencies: - dependency-name: undici dependency-version: 8.4.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the prod-minor-updates group with 2 updates (#890) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump the prod-minor-updates group with 2 updates Bumps the prod-minor-updates group with 2 updates: [axios](https://github.com/axios/axios) and [js-yaml](https://github.com/nodeca/js-yaml). Updates `axios` from 1.16.1 to 1.17.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.16.1...v1.17.0) Updates `js-yaml` from 4.1.1 to 4.2.0 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/commits) --- updated-dependencies: - dependency-name: axios dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: js-yaml dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/setup-node from 4 to 6 (#887) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump actions/setup-node from 4 to 6 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 6 (#886) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/upload-artifact from 4 to 7 (#885) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump actions/upload-artifact from 4 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/download-artifact from 4 to 8 (#884) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * chore(deps): bump actions/download-artifact from 4 to 8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: automate rebasing dependabot * feat: add admin settings audit log feature * feat: add customizability of app rail tabs * fix: host metrics giving sudo password error * feat: add terminal session logging * feat: sso system redesign (multiple oidc, ldap support, etc.) * feat: tailscale auth support * feat: improve syntax highlighting system * feat: improve nested folder system/ui * feat: improve nested folder system/ui * fix: move tailscale i18n and add scrollbar to ssh host manager customization * feat: improve syntax highlighting, fix tailscale bugs and host manger UX * fix: admin page not loading user admin information * chore: update crowdin workflow * fix: guacd getting passed incorrect protocol for app view * fix: add SFTP jump-host fallback from host data (#902) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * fix: add SFTP jump-host fallback from host data --------- Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * feat(tmux-monitor): add per-host tmux session monitor (#896) * fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883) * Revert "fix(docker): use resolveTermixThemeColors for Docker console terminal background (#883)" This reverts commit 4d04559ca5a33c09846c534cf1bf0c28d1f27e21. * feat(tmux-monitor): add per-host tmux session monitor New isolated feature module following the docker/server-stats pattern: - Backend Express service on port 30010 (src/backend/ssh/tmux-monitor.ts) listing tmux sessions/windows/panes, pane capture, cross-session output search, and per-pane CPU/RAM/GPU metrics over pooled SSH connections. Read-only; reuses host-resolver, jump-host-chain, connection pool and RBAC access checks. - tmux_session_tags table for per-user project tags on sessions. - Frontend fullscreen app at ?view=tmux-monitor with host selector, session/window/pane tree, live pane preview (2s polling), search bar, metrics display, and tag editing. - nginx location blocks for /tmux_monitor in Docker deployments. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): live streaming backend and shell integration - WebSocket service on port 30011 (tmux-monitor-live.ts) bridging tmux control mode (-C attach-session) to the browser: subscribe per pane, decoded %output events streamed as JSON, structure-change and detach notifications. Pure control-mode parser with 17 unit tests. - Register tmux_monitor as a singleton shell tab: AppRail entry below Network Graph, global Command Palette action, and per-host palette action (gated on enableTerminal) that opens with the host preselected. - nginx WebSocket location /tmux_monitor/live/ for Docker deployments. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): xterm live preview and UX polish - Pane preview is now a read-only xterm.js terminal with ANSI colors (capture-pane -e via new ansi=1 param) fed by the live WebSocket; status chip shows live/connecting/polling and the original 2s REST polling remains as automatic fallback. - useTmuxLive hook: survives React StrictMode remounts (unmounted flag reset on mount), force-closes sockets stuck in CONNECTING after 8s so retry/fallback always engages, uses import.meta.env.DEV for dev detection. - UX: skeleton loading, error state with retry, actionable empty states, persisted host/expanded-session selection, "/" and Escape shortcuts, search match highlighting, fresh relative timestamps. Verified end-to-end in Chrome against a local backend: live chip goes green and pane output streams in real time over the control-mode bridge. Co-Authored-By: Claude Fable 5 * refactor(tmux-monitor): replace control-mode bridge with native PTY attach The pane preview now embeds the real Terminal component and attaches to the selected session through the existing terminal WebSocket (new tmuxAttachSession handshake field in ssh/terminal.ts), making the preview fully interactive — typing, mouse scrolling and tmux copy-mode behave exactly like a normal terminal tab. Pane clicks within a session use the new /focus endpoint (select-window/select-pane) instead of remounting the terminal. This removes the entire custom live-streaming stack: the control-mode WebSocket service on port 30011 (tmux-monitor-live.ts), the control sequence parser and its tests, the useTmuxLive hook, the nginx /tmux_monitor/live/ location blocks, and the now-unused live/polling status chip locale keys. Verified end-to-end against a dockerized SSH host (Alpine + tmux 3.6): session tree, interactive preview with ANSI colors, cross-session search with jump-to-pane, per-pane CPU/RAM metrics, session tags, and attach-in-new-tab. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): use printable field separator in tmux format strings The unit separator control character (\x1f) used to delimit fields in tmux -F output gets sanitized to "_" by tmux itself — tmux replaces control characters (and, under non-UTF-8 locales, any multibyte character) in expanded format output. On such hosts every list-sessions line came back as a single unsplittable field, so the monitor showed session names like "build_1781066459_1781066459_0" and windows/panes failed to parse entirely. Switch SEP to the printable ASCII token "<>", which survives every tmux version and locale. Reproduced and verified against tmux 3.6 on Alpine (C locale) in a docker container. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): create sessions and split panes from the monitor - POST /tmux_monitor/:hostId/sessions creates a detached session (tmux new-session -d), starting the tmux server if needed. Session names are validated against a conservative charset on both ends (tmux forbids ":" and "."), and a duplicate name returns 409 with tmux's own message surfaced in the toast. - POST /tmux_monitor/:hostId/split splits the window containing a pane (tmux split-window -h/-v), with the new pane starting in the source pane's working directory via -c '#{pane_current_path}'. - UI: a "+" button next to the host selector opens a name popover (Enter or Create); the pane preview header gains split-right and split-down buttons. Splits appear instantly in the attached preview terminal and a silent overview reload updates the tree; new sessions are auto-expanded. Command syntax verified against tmux 3.6 in a dockerized SSH host. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): VSCode tmux-manager style tree with window actions Restyle the session tree after the VSCode tmux manager view: - Header toolbar: new-session (+) popover and refresh moved next to the panel title; the host selector row is back to a plain full-width select. - Session rows gain hover actions — attach (play) and new window (+) — revealed with the opacity pattern used by docker's ContainerCard so they stay keyboard-focusable, with aria-labels on all icon buttons. - Window rows are collapsible (chevron, "index: name" with a window icon); panes show "index: command" plus the pane's working directory in muted text with a full-path tooltip. - New POST /tmux_monitor/:hostId/windows route runs tmux new-window against an exact-match ("=name") shell-escaped target; rejects names containing ":"/"."/newline and returns 404 for missing sessions. - Pane preview header groups the split buttons apart from close with a separator. Reviewed by SA/BA/UX subagent panel; keyboard accessibility and aria-label findings applied. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): rename/kill sessions, collapse-all, no-flash refresh - Session rows gain a "…" menu with Rename (dialog, validated name) and Kill (destructive confirmation). Rename migrates saved tags to the new name and re-attaches an open preview; kill closes the preview and drops the session's tags. Both routes use exact-match ("=name") shell-escaped targets and map tmux's "can't find session" / "duplicate session" errors to 404/409. - Header gains a VSCode-style collapse-all / expand-all toggle for the session tree. - Refresh UX: manual refresh no longer blanks the tree into skeletons — the skeleton is reserved for a host's first load; refresh keeps the tree, spins the icon (header and toolbar) and reports failures with a toast instead of silently keeping stale data. tmux command syntax verified against tmux 3.6 in the docker test host. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): single-row sessions, overlay hover actions, tag dialog UI cleanup after desktop testing showed the tree looked cluttered: - Sessions are now a single row. The permanently visible tags row (a mostly empty line with a lone tag icon under every session) is gone: tags render as inline badges after the name (max 2, then "+n" with a full-list tooltip) and editing moved to the "…" menu, which opens a dialog like rename. - Session metrics merged into the right-hand meta label ("just now · 0% · 7 MB"). Hover actions (attach, new window, …) are stacked over that label in a grid cell with an opacity cross-fade, so the row never shifts and the buttons remain keyboard-focusable (focus-within reveals them). - Dropdown items no longer wrap: short labels (Rename / Edit tags / Kill), whitespace-nowrap, min-w-36, align="end". Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): kill pane; fix session actions in the desktop app - Kill pane: hover ✕ on pane rows and a trash button in the preview header, both behind a confirmation dialog (kill-pane terminates the pane's process; the last pane closes its window). New POST /tmux_monitor/:hostId/kill-pane route with pane-id validation. - Fix attach (▷) doing nothing in the Electron build: window.open of internal file:// URLs is denied by the window-open handler, so on desktop the button now attaches inline — expands the session and selects its active pane in the embedded terminal. Browsers keep the open-in-new-tab behavior. - Enlarge the hover-action hit areas (-m-1 p-1) so clicks land on the buttons instead of toggling the session row. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): session hover actions were unclickable The meta label (time · cpu · mem) fades to opacity-0 on hover, which turns it into a stacking context painted above its plain-flow sibling — the invisible label sat on top of the attach/new-window/menu buttons and swallowed their clicks, so clicking them toggled the session row instead. Give the actions overlay z-10 and make the label pointer-events-none. Reproduced and verified in Chrome with elementFromPoint before/after; create → rename → kill all work end-to-end through the UI now. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): resizable session tree panel The tree panel was fixed at w-72; long session names and pane paths had no room. Its right edge is now a drag handle (200–520px, same pattern and styling as the AppShell host sidebar), double-click resets to the default width, and the width persists in localStorage. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): split buttons on pane rows Pane rows' hover actions gain split-right and split-down next to the kill button, so a pane can be split straight from the tree. splitPane now takes the target pane id instead of implicitly using the selected pane; the preview header passes its own pane through. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): repair garbled preview after layout changes External split / kill-pane / new-window actions can leave the attached preview client with stale borders (the redraw races the xterm size). Two-part fix: - After any layout-changing action the monitor nudges the embedded terminal (fit + notifyResize + refresh) — the PTY resize forces tmux to fully redraw the client. - The preview header gains a re-attach button that remounts the terminal with a fresh PTY as a one-click rescue when the view is garbled. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): kill window, compact mode, session status tooltip - Kill window: hover ✕ on window rows behind a confirmation dialog (kills every pane; the last window ends the session). New POST /tmux_monitor/:hostId/kill-window route with exact-match target and 404 mapping. - Narrow tree panel (<280px) hides the inline "time · cpu · mem" label, which used to crush long session names into "bui…". - Hovering a session row now shows a status board tooltip (attached state, activity, window/pane counts, CPU/RAM/GPU, tags) — the numbers stay reachable in compact mode and richer in normal mode. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): keep preview attached after killing the viewed pane Killing the selected pane (or its window) dumped the user back to the "select a pane" empty state even though the session was still alive and the attached terminal had already focused a sibling pane. After the kill the monitor now refetches the overview and selects the session's new active pane, so the preview stays on the same attach connection (same session key → no remount); the empty state only appears when the whole session died with the pane. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): require execute permission, audit destructive actions, persist tag mutations - Mutating endpoints (kill/kill-window/kill-pane/rename/split/focus, create session/window) now require "execute" host access like docker; read-only shared hosts could previously kill sessions and spawn shells - Log kill/rename actions to the audit log (same pattern as host.ts) - Trigger a DB save after tag updates in rename/kill handlers; the dirty flag was never set, so a crash could lose or resurrect tags - Add host_id -> ssh_data FK with ON DELETE CASCADE to tmux_session_tags (drizzle schema + SQL migration), with a rebuild migration for pre-release tables created without the FK Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): stop polling and key handling while the tab is hidden, guard stale host responses - Pass isVisible into TmuxMonitor (like DockerManager/HostMetricsTab); the global "/"+Escape keydown listener and both SSH polling loops now pause while the tab is mounted but hidden - Split the overview polling interval out of the host-change effect so resuming a tab does not reset state or flash the skeleton - Ignore overview responses for a host the user has switched away from; a slow response could overwrite the selected host's tree - Format SearchResults/SessionTree/TmuxMonitorApp/SidebarTree with prettier (pr-check was failing on these) Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): show an empty state when no SSH hosts exist With zero hosts the host dropdown opened an empty popover and the preview area showed the unrelated "select a pane" hint. Disable the select (its placeholder now says no hosts are available) and show a central no-hosts message with a pointer to the Host Manager. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): refresh the host list when hosts change elsewhere The list was fetched once on mount, but the tab stays mounted forever, so adding the first host in Host Manager left the monitor stuck on "no hosts" until the tab was closed and reopened. Listen to the app-wide termix:hosts-changed event (same as AppShell/HostManager), keep the current selection when still valid, auto-select the first host when one appears, and clear the session tree when the selected host is deleted. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): keep pane/window actions reachable on a narrow tree Pane rows kept the split/kill buttons and the "index: command" label at fixed width, so dragging the panel toward the 200px minimum pushed the buttons past the right edge; Radix ScrollArea's display:table viewport wrapper made it worse by stretching every row to the widest pane path, clipping the window kill button too. Stack the cpu label and the hover actions in one grid cell (the same overlay pattern as the session rows), let the command and path labels truncate, and force the viewport wrapper back to display:block. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): reskin the session status tooltip as a popover card The base TooltipContent is the inverted one-line chip (bg-foreground, sharp corners, arrow), so the multi-row session board inherited a light surface that washed out its muted-foreground labels and clashed with every other floating card in the app. Restyle this one instance with the popover surface tokens and hide the chip arrow (important is needed because Radix sets display on the arrow svg inline). Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): disable new-session and search when tmux is missing On a host without tmux the + button still opened the create popover (submitting just surfaced the backend TMUX_NOT_INSTALLED toast) and search silently returned "0 matches". Gate both on overview.available like the neighboring refresh/expand buttons; the search input carries the unavailable message as its tooltip. Verified against a real no-tmux host (alpine+sshd container): both controls disable on it and stay enabled on a host with tmux. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): restyle floating surfaces to match the square UI Review feedback on #896: the host selector, the new-session popover, the session status tooltip and the three kill confirmations rendered with the stock rounded shadcn look while the rest of the app moved to square corners, ring borders and blurred dialog overlays. Convert the kill confirmations from AlertDialog to the already-restyled Dialog and override the remaining surfaces with rounded-none + ring-border. Co-Authored-By: Claude Fable 5 * style(host-editor): format the tailscale device options with project prettier The block arrived unformatted in the dev-2.4.0 merge; committing the reformat separately keeps the tmux-monitor commits free of unrelated churn. Co-Authored-By: Claude Fable 5 * feat(tmux-monitor): per-host opt-in flag, disabled by default Review feedback on #896: the tmux actions appeared on every SSH host's sidebar entry even though most users do not use tmux. Add an enable_tmux_monitor column (default off) mirroring the enableDocker plumbing, expose it as an "Enable Tmux Monitor" toggle in the host editor's Terminal tab, and gate every per-host tmux surface on it: the sidebar hover action and copy-URL menu item, the command palette entry, the monitor's host selector, and the tmux-monitor API itself (refusing like docker.ts does when the feature is off). Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): drop the redundant navbar rail entry The monitor is per-host opt-in, so the global rail item opened a host-less view. Hosts with enableTmuxMonitor still expose it via the host action in the sidebar tree and the command palette. Co-Authored-By: Claude Fable 5 * fix(tmux-monitor): fill the status popover card width TooltipContent is an inline-flex container, so the inner block sized to its natural content width and left a dead gutter on the right of the w-56 card. Stretch it with w-full so the values align to the edge. Co-Authored-By: Claude Fable 5 * feat: remove unused dropdown, seperate 3 line menu, and fix a few small UI bugs --------- Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: LukeGus Co-authored-by: Claude Fable 5 * fix: add i18n for tmux * feat: continue improving terminal highlighint with more options and more consistency * feat: add per host configuration of history tracking with overarching admin toggle * feat: add storage preference to user profile settings (local or DB) * chore: update release notes * fix: return user password reset code to logs * fix: missing tailscale errors and i18n * fix: sso/browser preference fixes * fix: remove SSL_ENABLED references * feat: add filter/sort to credentials list and fixed some JSDoc comments * chore: update release notes * chore: update readmes * chore: update readmes and add docs links * feat: continue improving SSO system * fix: host metrics bugs * fix: session logging api endpoint fail * chore: make electron.yml auto deliver to macos app store * chore: fix linting issues * chore: fix linting issues * chore: lint, format, and bump version to 2.4.0 * chore: crowdin using incorrect api key name * chore: sync Crowdin translations for 2.4.0 --------- Signed-off-by: dependabot[bot] Co-authored-by: XtraLarge Co-authored-by: Claude Opus 4.8 Co-authored-by: verybadsoldier <364469+verybadsoldier@users.noreply.github.com> Co-authored-by: i/o agent <273482092+claw-io@users.noreply.github.com> Co-authored-by: Félix MARQUET <72651575+BreizhHardware@users.noreply.github.com> Co-authored-by: Rabindra Kumar Meher Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jose Tenggren Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Kang <78076796+khengyun@users.noreply.github.com> --- .github/dependabot.yml | 51 +- .github/workflows/dependabot-retarget.yml | 157 + .github/workflows/docker.yml | 16 +- .github/workflows/electron.yml | 126 +- .github/workflows/openapi.yml | 6 +- .github/workflows/pr-check.yml | 4 +- .github/workflows/release.yml | 409 +- README.md | 27 +- RELEASE_NOTES.md | 74 +- crowdin.yml | 3 + docker/nginx-https.conf | 66 +- docker/nginx.conf | 78 +- electron/main.cjs | 4 + package-lock.json | 3707 +++++++++++-- package.json | 54 +- readme/README-AR.md | 11 +- readme/README-CN.md | 11 +- readme/README-DE.md | 11 +- readme/README-ES.md | 11 +- readme/README-FR.md | 11 +- readme/README-HI.md | 11 +- readme/README-IT.md | 11 +- readme/README-JA.md | 11 +- readme/README-KO.md | 11 +- readme/README-PT.md | 11 +- readme/README-RU.md | 11 +- readme/README-TR.md | 11 +- readme/README-VI.md | 11 +- repo-images/Image 8.png | Bin 325838 -> 546706 bytes scripts/crowdin-pretranslate.cjs | 121 + scripts/latest-dev-branch.cjs | 43 + scripts/latest-dev-branch.test.ts | 33 + scripts/parse-dev-branch.cjs | 36 + scripts/parse-dev-branch.test.ts | 31 + scripts/publish-youtube.cjs | 105 + scripts/publish-youtube.test.ts | 55 + scripts/sync-version.cjs | 79 + scripts/sync-version.test.ts | 80 + src/backend/dashboard.ts | 169 +- src/backend/database/database.ts | 8 + src/backend/database/db/index.ts | 241 +- src/backend/database/db/schema.ts | 125 +- .../database/routes/audit-log-routes.ts | 156 + src/backend/database/routes/credentials.ts | 58 + .../database/routes/delete-user-data.ts | 4 - .../database/routes/host-bulk-routes.ts | 62 +- .../database/routes/host-folder-routes.ts | 72 +- .../database/routes/host-normalizers.test.ts | 55 + .../database/routes/host-normalizers.ts | 28 + src/backend/database/routes/host.ts | 228 +- .../database/routes/ldap-auth-routes.ts | 428 ++ src/backend/database/routes/open-tabs.ts | 14 +- src/backend/database/routes/proxmox.ts | 565 ++ .../routes/session-log-routes.test.ts | 124 + .../database/routes/session-log-routes.ts | 316 ++ src/backend/database/routes/snippets.ts | 55 + .../database/routes/sso-provider-routes.ts | 452 ++ .../database/routes/tailscale-routes.ts | 115 + src/backend/database/routes/terminal.ts | 32 +- .../database/routes/user-admin-routes.ts | 50 +- .../database/routes/user-api-key-routes.ts | 44 + .../database/routes/user-oidc-utils.test.ts | 52 +- .../database/routes/user-oidc-utils.ts | 180 +- .../routes/user-password-reset-routes.ts | 2 +- .../database/routes/user-preferences.ts | 164 +- .../database/routes/user-session-routes.ts | 44 + .../database/routes/user-settings-routes.ts | 271 + .../database/routes/user-totp-routes.test.ts | 83 + .../database/routes/user-totp-routes.ts | 110 +- src/backend/database/routes/users.ts | 446 +- src/backend/ssh/credential-username.test.ts | 28 + src/backend/ssh/credential-username.ts | 26 + src/backend/ssh/docker.ts | 22 +- .../ssh/file-manager-operation-routes.ts | 18 +- src/backend/ssh/file-manager.ts | 52 +- ...s.test.ts => host-metrics-helpers.test.ts} | 2 +- ...ats-helpers.ts => host-metrics-helpers.ts} | 0 ...mp-hosts.ts => host-metrics-jump-hosts.ts} | 0 .../ssh/host-metrics-preferences-routes.ts | 245 + .../ssh/host-metrics-preferences.test.ts | 85 + ...s-sessions.ts => host-metrics-sessions.ts} | 0 ...tes.ts => host-metrics-settings-routes.ts} | 14 +- ...r-stats-state.ts => host-metrics-state.ts} | 0 ...outes.ts => host-metrics-viewer-routes.ts} | 12 +- .../ssh/{server-stats.ts => host-metrics.ts} | 113 +- src/backend/ssh/host-resolver.ts | 25 +- src/backend/ssh/managers/cron.ts | 127 + .../ssh/managers/exec-elevated.test.ts | 108 + src/backend/ssh/managers/exec-elevated.ts | 169 + src/backend/ssh/managers/firewall.ts | 129 + src/backend/ssh/managers/health.ts | 257 + src/backend/ssh/managers/index.ts | 61 + src/backend/ssh/managers/logs.ts | 92 + src/backend/ssh/managers/managers.test.ts | 416 ++ src/backend/ssh/managers/packages.ts | 148 + src/backend/ssh/managers/platform.ts | 61 + src/backend/ssh/managers/processes.ts | 130 + src/backend/ssh/managers/route-helpers.ts | 60 + src/backend/ssh/managers/services.ts | 135 + src/backend/ssh/managers/simple-reads.ts | 138 + src/backend/ssh/managers/ssl.ts | 317 ++ src/backend/ssh/managers/types.ts | 27 + src/backend/ssh/managers/users.ts | 151 + src/backend/ssh/managers/validation.ts | 115 + src/backend/ssh/opkssh-auth.ts | 10 +- .../ssh/terminal-session-manager.test.ts | 150 + src/backend/ssh/terminal-session-manager.ts | 98 +- src/backend/ssh/terminal.ts | 81 +- src/backend/ssh/tmux-monitor-helpers.test.ts | 225 + src/backend/ssh/tmux-monitor-helpers.ts | 240 + src/backend/ssh/tmux-monitor.ts | 928 ++++ src/backend/ssh/tunnel.ts | 15 + src/backend/starter.ts | 7 +- src/backend/swagger.ts | 4 +- src/backend/utils/audit-logger.test.ts | 132 + src/backend/utils/audit-logger.ts | 68 + src/main.tsx | 31 +- src/types/host-metrics.ts | 137 + src/types/index.ts | 98 +- src/types/proxmox.ts | 16 + src/types/ui-types.ts | 24 +- src/ui/AppShell.tsx | 195 +- src/ui/api/audit-log-api.ts | 66 + src/ui/api/credentials-api.ts | 4 +- src/ui/api/dashboard-preferences-api.ts | 26 - src/ui/api/host-metrics-api.ts | 100 + ...tats-api.ts => host-metrics-status-api.ts} | 0 src/ui/api/open-tabs-api.ts | 11 + src/ui/api/session-log-api.ts | 42 + src/ui/api/settings-api.ts | 48 + src/ui/api/ssh-host-management-api.ts | 23 +- src/ui/api/sso-provider-api.ts | 76 + src/ui/api/tmux-monitor-api.ts | 173 + src/ui/api/user-management-api.ts | 24 + src/ui/auth/Auth.tsx | 345 +- src/ui/auth/LoginPage.tsx | 653 +-- src/ui/auth/LoginScreen.tsx | 37 + .../components/card-grid/CardGridCanvas.tsx | 503 ++ .../components/card-grid/layout-utils.test.ts | 131 + src/ui/components/card-grid/layout-utils.ts | 105 + src/ui/components/card-grid/types.ts | 33 + src/ui/components/charts/BarSeries.tsx | 55 + src/ui/components/charts/RadialGauge.tsx | 94 + src/ui/components/charts/Sparkline.tsx | 89 + src/ui/components/charts/StatRow.tsx | 58 + src/ui/components/charts/geometry.test.ts | 75 + src/ui/components/charts/geometry.ts | 66 + src/ui/components/charts/index.ts | 8 + src/ui/components/folder-style.tsx | 248 + .../proxmox/ProxmoxDiscoverDialog.tsx | 378 ++ src/ui/components/section-card.tsx | 2 +- src/ui/dashboard/Dashboard.tsx | 887 ---- src/ui/dashboard/DashboardTab.tsx | 38 +- src/ui/dashboard/cards/NetworkGraphCard.tsx | 24 +- src/ui/dashboard/cards/QuickActionsCard.tsx | 141 - src/ui/dashboard/cards/RecentActivityCard.tsx | 128 - src/ui/dashboard/cards/ServerOverviewCard.tsx | 156 - src/ui/dashboard/cards/ServerStatsCard.tsx | 82 - .../components/DashboardSettingsDialog.tsx | 159 - .../hooks/useDashboardPreferences.ts | 138 - src/ui/features/FullScreenAppWrapper.tsx | 7 +- src/ui/features/docker/DockerManager.tsx | 17 +- .../docker/components/ConsoleTerminal.tsx | 36 +- src/ui/features/file-manager/FileManager.tsx | 13 +- .../file-manager/FileManagerContextMenu.tsx | 2 + .../features/file-manager/FileManagerGrid.tsx | 12 + src/ui/features/guacamole/GuacamoleApp.tsx | 117 +- .../features/guacamole/GuacamoleDisplay.tsx | 24 +- .../HostMetricsApp.tsx} | 18 +- .../HostMetricsTab.tsx} | 628 ++- .../features/host-metrics/cards/CpuCard.tsx | 52 + .../features/host-metrics/cards/DiskCard.tsx | 51 + .../host-metrics/cards/FirewallCard.tsx | 56 + .../host-metrics/cards/LoginStatsCard.tsx | 62 + .../host-metrics/cards/MemoryCard.tsx | 54 + .../host-metrics/cards/MetricCard.tsx | 53 + .../host-metrics/cards/NetworkCard.tsx | 66 + .../features/host-metrics/cards/PortsCard.tsx | 101 + .../host-metrics/cards/ProcessesCard.tsx | 54 + .../host-metrics/cards/SystemCard.tsx | 37 + .../host-metrics/cards/UptimeCard.tsx | 28 + src/ui/features/host-metrics/cards/index.tsx | 191 + .../cards/managers/CronManagerCard.tsx | 162 + .../cards/managers/FirewallManagerCard.tsx | 230 + .../cards/managers/HealthCheckCard.tsx | 310 ++ .../cards/managers/LogViewerCard.tsx | 183 + .../cards/managers/ManagerCardShell.tsx | 75 + .../cards/managers/ManagerToolbar.tsx | 42 + .../cards/managers/PackageManagerCard.tsx | 115 + .../cards/managers/ProcessInspectorCard.tsx | 190 + .../cards/managers/ServiceManagerCard.tsx | 154 + .../cards/managers/SimpleManagerCards.tsx | 165 + .../cards/managers/SslManagerCard.tsx | 301 ++ .../cards/managers/UserManagerCard.tsx | 145 + .../cards/managers/useManagerData.ts | 119 + .../hooks/useHostMetricsPreferences.ts | 91 + .../server-stats/widgets/CpuWidget.tsx | 107 - .../server-stats/widgets/DiskWidget.tsx | 102 - .../server-stats/widgets/FirewallWidget.tsx | 148 - .../server-stats/widgets/LoginStatsWidget.tsx | 89 - .../server-stats/widgets/MemoryWidget.tsx | 113 - .../server-stats/widgets/NetworkWidget.tsx | 86 - .../server-stats/widgets/PortsWidget.tsx | 70 - .../server-stats/widgets/ProcessesWidget.tsx | 67 - .../server-stats/widgets/SystemWidget.tsx | 51 - .../server-stats/widgets/UptimeWidget.tsx | 37 - src/ui/features/server-stats/widgets/index.ts | 10 - src/ui/features/terminal/Terminal.tsx | 83 +- src/ui/features/terminal/TerminalApp.tsx | 13 +- src/ui/features/tmux-monitor/PanePreview.tsx | 168 + .../features/tmux-monitor/SearchResults.tsx | 85 + src/ui/features/tmux-monitor/SessionTree.tsx | 451 ++ src/ui/features/tmux-monitor/TmuxMonitor.tsx | 1276 +++++ .../features/tmux-monitor/TmuxMonitorApp.tsx | 19 + src/ui/features/tmux-monitor/format.ts | 20 + src/ui/features/tmux-monitor/types.ts | 5 + src/ui/features/tunnel/TunnelTab.tsx | 10 + src/ui/lib/clipboard.test.ts | 86 + src/ui/lib/clipboard.ts | 48 + .../lib/terminal-syntax-highlighter.test.ts | 181 +- src/ui/lib/terminal-syntax-highlighter.ts | 478 +- src/ui/locales/en.json | 442 +- src/ui/locales/translated/af_ZA.json | 4473 ++++++++-------- src/ui/locales/translated/ar_SA.json | 4443 ++++++++-------- src/ui/locales/translated/bg_BG.json | 4547 ++++++++-------- src/ui/locales/translated/bn_BD.json | 4575 +++++++++-------- src/ui/locales/translated/ca_ES.json | 4425 ++++++++-------- src/ui/locales/translated/cs_CZ.json | 4367 +++++++++------- src/ui/locales/translated/da_DK.json | 4203 ++++++++------- src/ui/locales/translated/de_DE.json | 4217 ++++++++------- src/ui/locales/translated/el_GR.json | 4411 ++++++++-------- src/ui/locales/translated/es_ES.json | 4367 +++++++++------- src/ui/locales/translated/fi_FI.json | 4407 ++++++++-------- src/ui/locales/translated/fr_FR.json | 4335 +++++++++------- src/ui/locales/translated/he_IL.json | 4545 ++++++++-------- src/ui/locales/translated/hi_IN.json | 4557 ++++++++-------- src/ui/locales/translated/hu_HU.json | 4475 ++++++++-------- src/ui/locales/translated/id_ID.json | 4409 ++++++++-------- src/ui/locales/translated/it_IT.json | 4247 ++++++++------- src/ui/locales/translated/ja_JP.json | 4361 +++++++++------- src/ui/locales/translated/ko_KR.json | 4529 ++++++++-------- src/ui/locales/translated/nl_NL.json | 4267 ++++++++------- src/ui/locales/translated/no_NO.json | 4393 +++++++++------- src/ui/locales/translated/pl_PL.json | 4353 +++++++++------- src/ui/locales/translated/pt_BR.json | 4389 +++++++++------- src/ui/locales/translated/pt_PT.json | 4387 +++++++++------- src/ui/locales/translated/ro_RO.json | 4343 +++++++++------- src/ui/locales/translated/ru_RU.json | 4449 ++++++++-------- src/ui/locales/translated/sr_SP.json | 4557 ++++++++-------- src/ui/locales/translated/sv_SE.json | 4315 +++++++++------- src/ui/locales/translated/th_TH.json | 4569 ++++++++-------- src/ui/locales/translated/tr_TR.json | 4489 ++++++++-------- src/ui/locales/translated/uk_UA.json | 4451 ++++++++-------- src/ui/locales/translated/vi_VN.json | 4491 ++++++++-------- src/ui/locales/translated/zh_CN.json | 4433 ++++++++-------- src/ui/locales/translated/zh_TW.json | 4501 ++++++++-------- src/ui/main-axios.ts | 115 +- src/ui/shell/CommandPalette.tsx | 34 +- src/ui/shell/Tab.tsx | 43 +- src/ui/shell/TabContext.tsx | 14 +- src/ui/shell/tabUtils.tsx | 25 +- src/ui/sidebar/AdminApiKeysSection.tsx | 13 +- src/ui/sidebar/AdminAuditLogSection.tsx | 412 ++ src/ui/sidebar/AdminManagementSections.tsx | 5 +- src/ui/sidebar/AdminSettingsPanel.tsx | 226 +- src/ui/sidebar/AdminSettingsSections.tsx | 237 +- src/ui/sidebar/AdminUserDialogs.tsx | 67 +- src/ui/sidebar/AppRail.tsx | 62 +- src/ui/sidebar/ConnectionsPanel.tsx | 4 +- src/ui/sidebar/CredentialEditorView.tsx | 3 +- src/ui/sidebar/CredentialsPanel.tsx | 246 +- src/ui/sidebar/FolderMetadataDialog.tsx | 146 + src/ui/sidebar/FolderPathPicker.test.ts | 52 + src/ui/sidebar/FolderPathPicker.tsx | 207 + src/ui/sidebar/HistoryPanel.tsx | 17 +- src/ui/sidebar/HostCredentialList.tsx | 14 +- src/ui/sidebar/HostEditor.tsx | 378 +- src/ui/sidebar/HostEditorData.test.ts | 81 + src/ui/sidebar/HostEditorData.ts | 56 +- src/ui/sidebar/HostEditorFeatureTabs.tsx | 148 +- src/ui/sidebar/HostEditorGeneralTab.tsx | 81 +- src/ui/sidebar/HostEditorGuacamoleTabs.tsx | 61 + src/ui/sidebar/HostEditorStatsTab.tsx | 72 +- src/ui/sidebar/HostManager.tsx | 105 +- src/ui/sidebar/HostManagerData.ts | 5 + src/ui/sidebar/HostManagerTabs.tsx | 142 +- src/ui/sidebar/HostShareModal.tsx | 10 + src/ui/sidebar/HostsPanel.tsx | 742 ++- src/ui/sidebar/SSOProviderDialog.tsx | 675 +++ src/ui/sidebar/SessionLogsPanel.tsx | 439 ++ src/ui/sidebar/SidebarTree.tsx | 506 +- src/ui/sidebar/SnippetsPanel.tsx | 16 +- src/ui/sidebar/UserProfilePanel.tsx | 537 +- src/ui/ssh/connection-log/ConnectionLog.tsx | 10 +- .../ssh/dialogs/HostKeyVerificationDialog.tsx | 11 +- src/ui/ssh/dialogs/WarpgateDialog.tsx | 7 +- vitest.config.ts | 8 + 297 files changed, 111507 insertions(+), 74240 deletions(-) create mode 100644 .github/workflows/dependabot-retarget.yml create mode 100644 scripts/crowdin-pretranslate.cjs create mode 100644 scripts/latest-dev-branch.cjs create mode 100644 scripts/latest-dev-branch.test.ts create mode 100644 scripts/parse-dev-branch.cjs create mode 100644 scripts/parse-dev-branch.test.ts create mode 100644 scripts/publish-youtube.cjs create mode 100644 scripts/publish-youtube.test.ts create mode 100644 scripts/sync-version.cjs create mode 100644 scripts/sync-version.test.ts create mode 100644 src/backend/database/routes/audit-log-routes.ts create mode 100644 src/backend/database/routes/ldap-auth-routes.ts create mode 100644 src/backend/database/routes/proxmox.ts create mode 100644 src/backend/database/routes/session-log-routes.test.ts create mode 100644 src/backend/database/routes/session-log-routes.ts create mode 100644 src/backend/database/routes/sso-provider-routes.ts create mode 100644 src/backend/database/routes/tailscale-routes.ts create mode 100644 src/backend/database/routes/user-totp-routes.test.ts create mode 100644 src/backend/ssh/credential-username.test.ts create mode 100644 src/backend/ssh/credential-username.ts rename src/backend/ssh/{server-stats-helpers.test.ts => host-metrics-helpers.test.ts} (98%) rename src/backend/ssh/{server-stats-helpers.ts => host-metrics-helpers.ts} (100%) rename src/backend/ssh/{server-stats-jump-hosts.ts => host-metrics-jump-hosts.ts} (100%) create mode 100644 src/backend/ssh/host-metrics-preferences-routes.ts create mode 100644 src/backend/ssh/host-metrics-preferences.test.ts rename src/backend/ssh/{server-stats-sessions.ts => host-metrics-sessions.ts} (100%) rename src/backend/ssh/{server-stats-settings-routes.ts => host-metrics-settings-routes.ts} (95%) rename src/backend/ssh/{server-stats-state.ts => host-metrics-state.ts} (100%) rename src/backend/ssh/{server-stats-viewer-routes.ts => host-metrics-viewer-routes.ts} (97%) rename src/backend/ssh/{server-stats.ts => host-metrics.ts} (95%) create mode 100644 src/backend/ssh/managers/cron.ts create mode 100644 src/backend/ssh/managers/exec-elevated.test.ts create mode 100644 src/backend/ssh/managers/exec-elevated.ts create mode 100644 src/backend/ssh/managers/firewall.ts create mode 100644 src/backend/ssh/managers/health.ts create mode 100644 src/backend/ssh/managers/index.ts create mode 100644 src/backend/ssh/managers/logs.ts create mode 100644 src/backend/ssh/managers/managers.test.ts create mode 100644 src/backend/ssh/managers/packages.ts create mode 100644 src/backend/ssh/managers/platform.ts create mode 100644 src/backend/ssh/managers/processes.ts create mode 100644 src/backend/ssh/managers/route-helpers.ts create mode 100644 src/backend/ssh/managers/services.ts create mode 100644 src/backend/ssh/managers/simple-reads.ts create mode 100644 src/backend/ssh/managers/ssl.ts create mode 100644 src/backend/ssh/managers/types.ts create mode 100644 src/backend/ssh/managers/users.ts create mode 100644 src/backend/ssh/managers/validation.ts create mode 100644 src/backend/ssh/terminal-session-manager.test.ts create mode 100644 src/backend/ssh/tmux-monitor-helpers.test.ts create mode 100644 src/backend/ssh/tmux-monitor-helpers.ts create mode 100644 src/backend/ssh/tmux-monitor.ts create mode 100644 src/backend/utils/audit-logger.test.ts create mode 100644 src/backend/utils/audit-logger.ts create mode 100644 src/types/host-metrics.ts create mode 100644 src/types/proxmox.ts create mode 100644 src/ui/api/audit-log-api.ts delete mode 100644 src/ui/api/dashboard-preferences-api.ts create mode 100644 src/ui/api/host-metrics-api.ts rename src/ui/api/{server-stats-api.ts => host-metrics-status-api.ts} (100%) create mode 100644 src/ui/api/session-log-api.ts create mode 100644 src/ui/api/sso-provider-api.ts create mode 100644 src/ui/api/tmux-monitor-api.ts create mode 100644 src/ui/auth/LoginScreen.tsx create mode 100644 src/ui/components/card-grid/CardGridCanvas.tsx create mode 100644 src/ui/components/card-grid/layout-utils.test.ts create mode 100644 src/ui/components/card-grid/layout-utils.ts create mode 100644 src/ui/components/card-grid/types.ts create mode 100644 src/ui/components/charts/BarSeries.tsx create mode 100644 src/ui/components/charts/RadialGauge.tsx create mode 100644 src/ui/components/charts/Sparkline.tsx create mode 100644 src/ui/components/charts/StatRow.tsx create mode 100644 src/ui/components/charts/geometry.test.ts create mode 100644 src/ui/components/charts/geometry.ts create mode 100644 src/ui/components/charts/index.ts create mode 100644 src/ui/components/folder-style.tsx create mode 100644 src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx delete mode 100644 src/ui/dashboard/Dashboard.tsx delete mode 100644 src/ui/dashboard/cards/QuickActionsCard.tsx delete mode 100644 src/ui/dashboard/cards/RecentActivityCard.tsx delete mode 100644 src/ui/dashboard/cards/ServerOverviewCard.tsx delete mode 100644 src/ui/dashboard/cards/ServerStatsCard.tsx delete mode 100644 src/ui/dashboard/components/DashboardSettingsDialog.tsx delete mode 100644 src/ui/dashboard/hooks/useDashboardPreferences.ts rename src/ui/features/{server-stats/ServerStatsApp.tsx => host-metrics/HostMetricsApp.tsx} (65%) rename src/ui/features/{server-stats/ServerStats.tsx => host-metrics/HostMetricsTab.tsx} (56%) create mode 100644 src/ui/features/host-metrics/cards/CpuCard.tsx create mode 100644 src/ui/features/host-metrics/cards/DiskCard.tsx create mode 100644 src/ui/features/host-metrics/cards/FirewallCard.tsx create mode 100644 src/ui/features/host-metrics/cards/LoginStatsCard.tsx create mode 100644 src/ui/features/host-metrics/cards/MemoryCard.tsx create mode 100644 src/ui/features/host-metrics/cards/MetricCard.tsx create mode 100644 src/ui/features/host-metrics/cards/NetworkCard.tsx create mode 100644 src/ui/features/host-metrics/cards/PortsCard.tsx create mode 100644 src/ui/features/host-metrics/cards/ProcessesCard.tsx create mode 100644 src/ui/features/host-metrics/cards/SystemCard.tsx create mode 100644 src/ui/features/host-metrics/cards/UptimeCard.tsx create mode 100644 src/ui/features/host-metrics/cards/index.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/CronManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/FirewallManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/HealthCheckCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/LogViewerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/ManagerCardShell.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/ManagerToolbar.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/PackageManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/ProcessInspectorCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/ServiceManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/SimpleManagerCards.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/SslManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/UserManagerCard.tsx create mode 100644 src/ui/features/host-metrics/cards/managers/useManagerData.ts create mode 100644 src/ui/features/host-metrics/hooks/useHostMetricsPreferences.ts delete mode 100644 src/ui/features/server-stats/widgets/CpuWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/DiskWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/FirewallWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/LoginStatsWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/MemoryWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/NetworkWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/PortsWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/ProcessesWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/SystemWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/UptimeWidget.tsx delete mode 100644 src/ui/features/server-stats/widgets/index.ts create mode 100644 src/ui/features/tmux-monitor/PanePreview.tsx create mode 100644 src/ui/features/tmux-monitor/SearchResults.tsx create mode 100644 src/ui/features/tmux-monitor/SessionTree.tsx create mode 100644 src/ui/features/tmux-monitor/TmuxMonitor.tsx create mode 100644 src/ui/features/tmux-monitor/TmuxMonitorApp.tsx create mode 100644 src/ui/features/tmux-monitor/format.ts create mode 100644 src/ui/features/tmux-monitor/types.ts create mode 100644 src/ui/lib/clipboard.test.ts create mode 100644 src/ui/lib/clipboard.ts create mode 100644 src/ui/sidebar/AdminAuditLogSection.tsx create mode 100644 src/ui/sidebar/FolderMetadataDialog.tsx create mode 100644 src/ui/sidebar/FolderPathPicker.test.ts create mode 100644 src/ui/sidebar/FolderPathPicker.tsx create mode 100644 src/ui/sidebar/HostEditorData.test.ts create mode 100644 src/ui/sidebar/SSOProviderDialog.tsx create mode 100644 src/ui/sidebar/SessionLogsPanel.tsx diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8bb2f443..a207de01 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,9 +1,19 @@ version: 2 updates: + # npm dependencies (single root package.json, no workspaces) - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + open-pull-requests-limit: 15 + labels: + - "dependencies" + - "npm" + commit-message: + prefix: "chore" + prefix-development: "chore" + include: "scope" groups: dev-patch-updates: dependency-type: "development" @@ -21,20 +31,53 @@ updates: dependency-type: "production" update-types: - "minor" + # Major bumps grouped so they land as a single reviewable PR instead of + # one-per-package noise. These often need manual follow-up (Electron, + # React, Vite, Tailwind, Express 5, etc.). + major-updates: + update-types: + - "major" + # Docker base images (docker/Dockerfile + docker-compose / compose-dev) - package-ecosystem: "docker" directory: "/docker" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "docker" + commit-message: + prefix: "chore" + include: "scope" groups: - patch-updates: + docker-patch-updates: update-types: - "patch" - minor-updates: + docker-minor-updates: update-types: - "minor" + docker-major-updates: + update-types: + - "major" + # GitHub Actions used across the workflows in .github/workflows - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "github-actions" + commit-message: + prefix: "ci" + include: "scope" + groups: + github-actions: + update-types: + - "patch" + - "minor" + - "major" diff --git a/.github/workflows/dependabot-retarget.yml b/.github/workflows/dependabot-retarget.yml new file mode 100644 index 00000000..9a01d138 --- /dev/null +++ b/.github/workflows/dependabot-retarget.yml @@ -0,0 +1,157 @@ +name: Retarget and Merge Dependabot PRs + +on: + schedule: + - cron: "0 6 * * *" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + retarget-and-merge: + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 1 + + - name: Resolve newest dev branch + id: dev + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + REFS=$(gh api "repos/${{ github.repository }}/branches" --paginate -q '.[].name') + # The helper exits non-zero when no dev-X.Y.Z branch exists; treat that + # as "nothing to do" rather than a workflow failure. + if DEV_BRANCH=$(printf '%s\n' "$REFS" | node scripts/latest-dev-branch.cjs 2>/dev/null); then + echo "Newest dev branch: $DEV_BRANCH" + echo "branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT" + else + echo "No dev-X.Y.Z branch open; nothing to retarget." + echo "branch=" >> "$GITHUB_OUTPUT" + fi + + - name: Retarget and merge Dependabot PRs + if: ${{ steps.dev.outputs.branch != '' }} + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + DEV_BRANCH: ${{ steps.dev.outputs.branch }} + REPO: ${{ github.repository }} + run: | + set -uo pipefail + + CONFLICT_LABEL="dependabot-rebase-requested" + + # Ensure the bookkeeping label exists (no-op if it already does). + gh label create "$CONFLICT_LABEL" --repo "$REPO" \ + --color "D93F0B" --description "Retarget workflow asked Dependabot to rebase a conflicting PR" \ + 2>/dev/null || true + + # True if the PR already carries the conflict label. + has_conflict_label() { + gh pr view "$1" --repo "$REPO" --json labels \ + -q '.labels[].name' | grep -qx "$CONFLICT_LABEL" + } + + # Wait until GitHub has a definite mergeable verdict for a PR (it + # returns UNKNOWN while recomputing after a base change or a push). + # Echoes " ". + wait_for_verdict() { + local pr="$1" mergeable state + for _ in $(seq 1 30); do + read -r mergeable state < <(gh pr view "$pr" --repo "$REPO" \ + --json mergeable,mergeStateStatus \ + -q '.mergeable + " " + .mergeStateStatus') + if [ "$mergeable" != "UNKNOWN" ] && [ "$state" != "UNKNOWN" ]; then + echo "$mergeable $state" + return 0 + fi + sleep 20 + done + echo "$mergeable $state" + } + + # Phase 1: retarget every open Dependabot PR from main onto the dev + # branch. This kicks off a Dependabot rebase for each. + PR_NUMBERS=$(gh pr list --repo "$REPO" \ + --author "app/dependabot" \ + --base main \ + --state open \ + --json number -q '.[].number') + + # Pick up PRs already sitting on the dev branch from a previous run too. + PR_NUMBERS="$PR_NUMBERS $(gh pr list --repo "$REPO" \ + --author "app/dependabot" \ + --base "$DEV_BRANCH" \ + --state open \ + --json number -q '.[].number')" + + PR_NUMBERS=$(printf '%s\n' $PR_NUMBERS | sort -un) + + if [ -z "$PR_NUMBERS" ]; then + echo "No open Dependabot PRs to process." + exit 0 + fi + + for PR in $PR_NUMBERS; do + BASE=$(gh pr view "$PR" --repo "$REPO" --json baseRefName -q .baseRefName) + if [ "$BASE" != "$DEV_BRANCH" ]; then + echo "Retargeting PR #$PR ($BASE -> $DEV_BRANCH)" + gh pr edit "$PR" --repo "$REPO" --base "$DEV_BRANCH" + fi + done + + # Phase 2: merge one at a time. Each merge can make the remaining npm + # PRs stale, so re-check immediately before merging and rebase stragglers. + for PR in $PR_NUMBERS; do + echo "::group::PR #$PR" + + read -r MERGEABLE STATE < <(wait_for_verdict "$PR") + echo " mergeable=$MERGEABLE mergeStateStatus=$STATE" + + # BEHIND = clean but needs the latest base; ask Dependabot to rebase + # and skip for now (next run merges it once it is up to date). + if [ "$STATE" = "BEHIND" ]; then + echo " PR #$PR is behind $DEV_BRANCH; asking Dependabot to rebase." + gh pr comment "$PR" --repo "$REPO" --body "@dependabot rebase" + echo "::endgroup::" + continue + fi + + # DIRTY / CONFLICTING = a real conflict. Try a rebase once (label it so + # we can tell next time); if it is STILL conflicting on a later run + # despite already being labelled, the rebase failed for good - close it + # so Dependabot reopens a fresh PR against the current dev branch. + if [ "$MERGEABLE" = "CONFLICTING" ] || [ "$STATE" = "DIRTY" ]; then + if has_conflict_label "$PR"; then + echo " PR #$PR still conflicts after a prior rebase request; closing so Dependabot reopens it fresh." + gh pr close "$PR" --repo "$REPO" --delete-branch \ + --comment "Closing: this PR still conflicts with $DEV_BRANCH after a rebase attempt (its changes are likely already merged). Dependabot will reopen a fresh PR computed against the current $DEV_BRANCH." + else + echo " PR #$PR conflicts with $DEV_BRANCH; requesting a rebase and labelling it." + gh pr edit "$PR" --repo "$REPO" --add-label "$CONFLICT_LABEL" + gh pr comment "$PR" --repo "$REPO" --body "@dependabot rebase" + fi + echo "::endgroup::" + continue + fi + + # A clean PR that was previously flagged has recovered - drop the label. + if has_conflict_label "$PR"; then + gh pr edit "$PR" --repo "$REPO" --remove-label "$CONFLICT_LABEL" || true + fi + + echo " Squash-merging PR #$PR" + if gh pr merge "$PR" --repo "$REPO" --squash --admin; then + echo " Merged PR #$PR" + # Give GitHub a moment to mark the now-stale siblings BEHIND. + sleep 15 + else + echo " Could not merge PR #$PR now; it will be retried next run." + fi + + echo "::endgroup::" + done diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4851ec5c..b8fb0e7f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -24,13 +24,18 @@ on: description: "Build type (Development or Production)" required: true type: string + dry_run: + description: "Build the image but do not push to any registry" + required: false + type: boolean + default: false jobs: build: runs-on: blacksmith-8vcpu-ubuntu-2404 steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 @@ -52,7 +57,7 @@ jobs: ALL_TAGS=() if [ "$BUILD_TYPE" = "Production" ]; then - TAGS+=("release-$VERSION" "latest") + TAGS+=("release-$VERSION" "$VERSION" "latest") for tag in "${TAGS[@]}"; do ALL_TAGS+=("ghcr.io/lukegus/termix:$tag") ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag") @@ -67,6 +72,7 @@ jobs: echo "ALL_TAGS=$(IFS=,; echo "${ALL_TAGS[*]}")" >> $GITHUB_ENV - name: Login to GHCR + if: ${{ !inputs.dry_run }} uses: docker/login-action@v4 with: registry: ghcr.io @@ -74,7 +80,7 @@ jobs: password: ${{ secrets.GHCR_TOKEN }} - name: Login to Docker Hub (prod only) - if: ${{ inputs.build_type == 'Production' }} + if: ${{ inputs.build_type == 'Production' && !inputs.dry_run }} uses: docker/login-action@v4 with: username: bugattiguy527 @@ -85,7 +91,7 @@ jobs: with: context: . file: ./docker/Dockerfile - push: true + push: ${{ !inputs.dry_run }} platforms: linux/amd64,linux/arm64 tags: ${{ env.ALL_TAGS }} build-args: | @@ -96,7 +102,7 @@ jobs: org.opencontainers.image.created=${{ github.run_id }} cache-from: type=gha cache-to: type=gha,mode=max - outputs: type=registry,compression=zstd + outputs: ${{ inputs.dry_run && 'type=cacheonly' || 'type=registry,compression=zstd' }} - name: Cleanup Docker if: always() diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index c822dc14..c6c18963 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -52,12 +52,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" @@ -77,7 +77,7 @@ jobs: run: npm run build && npx electron-builder --win --x64 --ia32 - name: Upload Windows x64 NSIS Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_x64_nsis @@ -85,7 +85,7 @@ jobs: retention-days: 30 - name: Upload Windows ia32 NSIS Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_windows_ia32_nsis.exe') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_ia32_nsis @@ -93,7 +93,7 @@ jobs: retention-days: 30 - name: Upload Windows x64 MSI Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_windows_x64_msi.msi') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_x64_msi @@ -101,7 +101,7 @@ jobs: retention-days: 30 - name: Upload Windows ia32 MSI Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_windows_ia32_msi.msi') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_ia32_msi @@ -119,7 +119,7 @@ jobs: Compress-Archive -Path "release\win-ia32-unpacked\*" -DestinationPath "termix_windows_ia32_portable.zip" - name: Upload Windows x64 Portable - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('termix_windows_x64_portable.zip') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_x64_portable @@ -127,7 +127,7 @@ jobs: retention-days: 30 - name: Upload Windows ia32 Portable - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('termix_windows_ia32_portable.zip') != '' && inputs.artifact_destination != 'none' with: name: termix_windows_ia32_portable @@ -142,12 +142,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" @@ -190,7 +190,7 @@ jobs: cd .. - name: Upload Linux x64 AppImage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_x64_appimage @@ -198,7 +198,7 @@ jobs: retention-days: 30 - name: Upload Linux arm64 AppImage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_arm64_appimage.AppImage') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_arm64_appimage @@ -206,7 +206,7 @@ jobs: retention-days: 30 - name: Upload Linux armv7l AppImage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_armv7l_appimage.AppImage') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_armv7l_appimage @@ -214,7 +214,7 @@ jobs: retention-days: 30 - name: Upload Linux x64 DEB - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_x64_deb.deb') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_x64_deb @@ -222,7 +222,7 @@ jobs: retention-days: 30 - name: Upload Linux arm64 DEB - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_arm64_deb.deb') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_arm64_deb @@ -230,7 +230,7 @@ jobs: retention-days: 30 - name: Upload Linux armv7l DEB - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_armv7l_deb.deb') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_armv7l_deb @@ -238,7 +238,7 @@ jobs: retention-days: 30 - name: Upload Linux x64 tar.gz - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_x64_portable.tar.gz') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_x64_portable @@ -246,7 +246,7 @@ jobs: retention-days: 30 - name: Upload Linux arm64 tar.gz - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_arm64_portable.tar.gz') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_arm64_portable @@ -254,7 +254,7 @@ jobs: retention-days: 30 - name: Upload Linux armv7l tar.gz - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_armv7l_portable.tar.gz') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_armv7l_portable @@ -326,7 +326,7 @@ jobs: sed -i "s|VERSION_PLACEHOLDER|release-${VERSION}-tag|g" release/com.karmaa.termix.flatpakref - name: Upload Flatpak bundle - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_linux_flatpak.flatpak') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_flatpak @@ -334,7 +334,7 @@ jobs: retention-days: 30 - name: Upload Flatpakref - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/com.karmaa.termix.flatpakref') != '' && inputs.artifact_destination != 'none' with: name: termix_linux_flatpakref @@ -352,12 +352,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" @@ -480,7 +480,7 @@ jobs: - name: Upload macOS MAS PKG if: steps.check_certs.outputs.has_certs == 'true' && hashFiles('release/termix_macos_universal_mas.pkg') != '' && (inputs.artifact_destination == 'file' || inputs.artifact_destination == 'release' || inputs.artifact_destination == 'submit') - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: termix_macos_universal_mas path: release/termix_macos_universal_mas.pkg @@ -488,7 +488,7 @@ jobs: if-no-files-found: warn - name: Upload macOS Universal DMG - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && inputs.artifact_destination != 'none' with: name: termix_macos_universal_dmg @@ -496,7 +496,7 @@ jobs: retention-days: 30 - name: Upload macOS x64 DMG - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_macos_x64_dmg.dmg') != '' && inputs.artifact_destination != 'none' with: name: termix_macos_x64_dmg @@ -504,7 +504,7 @@ jobs: retention-days: 30 - name: Upload macOS arm64 DMG - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('release/termix_macos_arm64_dmg.dmg') != '' && inputs.artifact_destination != 'none' with: name: termix_macos_arm64_dmg @@ -542,7 +542,7 @@ jobs: sed -i '' "s|release-[0-9.]*-tag|release-$VERSION-tag|g" homebrew-generated/termix.rb - name: Upload Homebrew Cask as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: hashFiles('homebrew-generated/termix.rb') != '' && inputs.artifact_destination == 'file' with: name: termix_macos_homebrew_cask @@ -578,7 +578,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 @@ -669,7 +669,7 @@ jobs: } - name: Upload Chocolatey package as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: chocolatey-package path: choco-build/*.nupkg @@ -684,7 +684,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 @@ -756,7 +756,7 @@ jobs: sed -i "s/DATE_PLACEHOLDER/$RELEASE_DATE/g" flatpak-submission/com.karmaa.termix.metainfo.xml - name: Upload Flatpak submission as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: flatpak-submission path: flatpak-submission/* @@ -771,7 +771,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 @@ -825,7 +825,7 @@ jobs: ruby -c homebrew-submission/Casks/t/termix.rb - name: Upload Homebrew submission as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: homebrew-submission path: homebrew-submission/* @@ -840,7 +840,7 @@ jobs: steps: - name: Download all artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: artifacts @@ -872,7 +872,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} - submit-to-testflight: + submit-to-app-store: runs-on: blacksmith-6vcpu-macos-latest if: inputs.artifact_destination == 'submit' && (inputs.build_type == 'all' || inputs.build_type == 'macos') needs: [] @@ -881,12 +881,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" @@ -943,9 +943,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=4096 run: | - CURRENT_VERSION=$(node -p "require('./package.json').version") BUILD_VERSION="${{ github.run_number }}" - npm run build && npx electron-builder --mac mas --universal --config.buildVersion="$BUILD_VERSION" - name: Check for App Store Connect API credentials @@ -956,36 +954,56 @@ jobs: fi - name: Setup Ruby for Fastlane - if: steps.check_asc_creds.outputs.has_credentials == 'true' + if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true' uses: ruby/setup-ruby@v1 with: ruby-version: "3.2" bundler-cache: false - name: Install Fastlane - if: steps.check_asc_creds.outputs.has_credentials == 'true' - run: | - gem install fastlane -N + if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true' + run: gem install fastlane -N - - name: Deploy to App Store Connect (TestFlight) - if: steps.check_asc_creds.outputs.has_credentials == 'true' + - name: Upload and submit to Mac App Store + if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true' + env: + APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} + APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} + APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} run: | PKG_FILE=$(find release -name "termix_macos_universal_mas.pkg" -type f | head -n 1) if [ -z "$PKG_FILE" ]; then - echo "PKG file not found, exiting." + echo "PKG file not found in release/; aborting." exit 1 fi - mkdir -p ~/private_keys - echo "${{ secrets.APPLE_KEY_CONTENT }}" | base64 --decode > ~/private_keys/AuthKey_${{ secrets.APPLE_KEY_ID }}.p8 + VERSION=$(node -p "require('./package.json').version") - xcrun altool --upload-app -f "$PKG_FILE" \ - --type macos \ - --apiKey "${{ secrets.APPLE_KEY_ID }}" \ - --apiIssuer "${{ secrets.APPLE_ISSUER_ID }}" - continue-on-error: true + # Write API key JSON that Fastlane deliver expects + mkdir -p /tmp/asc_keys + KEY_P8_PATH="/tmp/asc_keys/AuthKey_${APPLE_KEY_ID}.p8" + API_KEY_JSON="/tmp/asc_keys/api_key.json" + + echo "$APPLE_KEY_CONTENT" | base64 --decode > "$KEY_P8_PATH" + + printf '{\n "key_id": "%s",\n "issuer_id": "%s",\n "key": "%s",\n "in_house": false\n}\n' \ + "$APPLE_KEY_ID" \ + "$APPLE_ISSUER_ID" \ + "$(tr -d '\n' < "$KEY_P8_PATH")" \ + > "$API_KEY_JSON" + + fastlane deliver \ + --pkg "$PKG_FILE" \ + --api_key_path "$API_KEY_JSON" \ + --app_version "$VERSION" \ + --skip_metadata true \ + --skip_screenshots true \ + --submit_for_review true \ + --automatic_release true \ + --force true - name: Clean up keychains if: always() run: | security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true + rm -rf /tmp/asc_keys diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index 04383210..2ea9b852 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -10,10 +10,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" @@ -25,7 +25,7 @@ jobs: run: npm run generate:openapi - name: Upload OpenAPI artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: openapi-spec path: openapi.json diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 19eab8b6..6c40ebae 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -13,10 +13,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" cache: "npm" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 94853c8b..968847a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,6 +2,17 @@ name: Release on: workflow_dispatch: + inputs: + mode: + description: "Release mode" + required: true + default: Everything + type: choice + options: + - Everything + - Overwrite release + - Dry run + - Skip submit permissions: contents: write @@ -13,20 +24,27 @@ jobs: version: ${{ steps.info.outputs.version }} release_tag: ${{ steps.info.outputs.release_tag }} mobile_version: ${{ steps.info.outputs.mobile_version }} + dev_branch: ${{ steps.info.outputs.dev_branch }} steps: - - name: Guard branch - if: github.ref != 'refs/heads/main' + - name: Guard branch (release modes) + if: ${{ inputs.mode != 'Overwrite release' && !startsWith(github.ref, 'refs/heads/dev-') }} run: | - echo "Releases must be run from main (got ${{ github.ref }})." + echo "This mode must be run from a dev branch (got ${{ github.ref }})." + exit 1 + + - name: Guard branch (overwrite mode) + if: ${{ inputs.mode == 'Overwrite release' && github.ref != 'refs/heads/main' }} + run: | + echo "Overwrite release must be run from main (got ${{ github.ref }})." exit 1 - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 1 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" @@ -35,7 +53,14 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - VERSION=$(node -p "require('./package.json').version") + if [ "${{ inputs.mode }}" = "Overwrite release" ]; then + DEV_BRANCH="" + VERSION=$(node -p "require('./package.json').version") + else + DEV_BRANCH="${GITHUB_REF#refs/heads/}" + VERSION=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH") + fi + echo "dev_branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT" echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "release_tag=release-$VERSION-tag" >> "$GITHUB_OUTPUT" @@ -54,21 +79,217 @@ jobs: --mobile-version "${{ steps.info.outputs.mobile_version }}" \ --notes RELEASE_NOTES.md > /dev/null - docker: + normalize: needs: [prep] + if: ${{ inputs.mode != 'Overwrite release' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout dev branch + uses: actions/checkout@v5 + with: + ref: ${{ needs.prep.outputs.dev_branch }} + fetch-depth: 0 + token: ${{ secrets.GHCR_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Lint and format + run: | + npm run lint:fix || true + npm run format + + - name: Run unit tests + run: npm run test + + - name: Sync version + run: node scripts/sync-version.cjs --version "${{ needs.prep.outputs.version }}" + + - name: Commit changes to dev branch + run: | + git config user.name "LukeGus" + git config user.email "bugattiguy527@gmail.com" + + if git diff --quiet; then + echo "No lint/format/version changes to commit." + exit 0 + fi + + if [ "${{ inputs.mode }}" = "Dry run" ]; then + echo "DRY RUN: would commit and push the following changes to ${{ needs.prep.outputs.dev_branch }}:" + git --no-pager diff --stat + exit 0 + fi + + git add -A + git commit -m "chore: lint, format, and bump version to ${{ needs.prep.outputs.version }}" + git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}" + + crowdin: + needs: [prep, normalize] + if: ${{ inputs.mode != 'Dry run' && inputs.mode != 'Overwrite release' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout dev branch + uses: actions/checkout@v5 + with: + ref: ${{ needs.prep.outputs.dev_branch }} + fetch-depth: 0 + token: ${{ secrets.GHCR_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + + - name: Clean up stale Crowdin git integration branch + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head i18n_translate --state open --json number -q '.[0].number' || true) + if [ -n "$PR_NUMBER" ]; then + gh pr close "$PR_NUMBER" --repo ${{ github.repository }} --delete-branch || true + fi + git push origin --delete i18n_translate || true + + - name: Upload sources to Crowdin + uses: crowdin/github-action@v2 + with: + upload_sources: true + upload_translations: false + download_translations: false + create_pull_request: false + push_translations: false + token: ${{ secrets.CROWDIN_API_KEY }} + project_id: "858252" + env: + CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }} + + - name: Machine pre-translate untranslated strings + env: + CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }} + run: node scripts/crowdin-pretranslate.cjs + + - name: Download translations from Crowdin + uses: crowdin/github-action@v2 + with: + upload_sources: false + upload_translations: false + download_translations: true + create_pull_request: false + push_translations: false + token: ${{ secrets.CROWDIN_API_KEY }} + project_id: "858252" + env: + CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }} + + - name: Commit translations to dev branch + run: | + git config user.name "LukeGus" + git config user.email "bugattiguy527@gmail.com" + + git add src/ui/locales/translated + if git diff --cached --quiet; then + echo "No translation changes to commit." + exit 0 + fi + git commit -m "chore: sync Crowdin translations for ${{ needs.prep.outputs.version }}" + git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}" + + merge-to-main: + needs: [prep, normalize, crowdin] + if: ${{ always() && needs.prep.result == 'success' && (needs.normalize.result == 'success' || needs.normalize.result == 'skipped') && (needs.crowdin.result == 'success' || needs.crowdin.result == 'skipped') }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + outputs: + main_sha: ${{ steps.merge.outputs.main_sha }} + build_ref: ${{ steps.merge.outputs.build_ref }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version-file: ".nvmrc" + + - name: Generate PR body + id: body + run: | + node scripts/generate-release-body.cjs \ + --version "${{ needs.prep.outputs.version }}" \ + --mobile-version "${{ needs.prep.outputs.mobile_version }}" \ + --notes RELEASE_NOTES.md > PR_BODY.md + + - name: Open and squash-merge PR to main + id: merge + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + DEV_BRANCH="${{ needs.prep.outputs.dev_branch }}" + TITLE="release-${{ needs.prep.outputs.version }}" + + if [ "${{ inputs.mode }}" = "Overwrite release" ]; then + echo "OVERWRITE: no merge; building from main as-is." + echo "build_ref=main" >> "$GITHUB_OUTPUT" + echo "main_sha=$(gh api repos/${{ github.repository }}/commits/main -q .sha)" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [ "${{ inputs.mode }}" = "Dry run" ]; then + echo "DRY RUN: would open and squash-merge a PR from $DEV_BRANCH into main." + echo "DRY RUN: downstream jobs will build from $DEV_BRANCH instead of main." + echo "build_ref=$DEV_BRANCH" >> "$GITHUB_OUTPUT" + echo "main_sha=" >> "$GITHUB_OUTPUT" + exit 0 + fi + + PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head "$DEV_BRANCH" --base main --state open --json number -q '.[0].number' || true) + if [ -z "$PR_NUMBER" ]; then + PR_NUMBER=$(gh pr create --repo ${{ github.repository }} \ + --base main --head "$DEV_BRANCH" \ + --title "$TITLE" --body-file PR_BODY.md \ + | grep -oE '[0-9]+$') + fi + + gh pr merge "$PR_NUMBER" --repo ${{ github.repository }} --squash --admin + + STATE=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state -q .state) + if [ "$STATE" != "MERGED" ]; then + echo "PR #$PR_NUMBER did not merge (state: $STATE)." + exit 1 + fi + + MAIN_SHA=$(gh api repos/${{ github.repository }}/commits/main -q .sha) + echo "main_sha=$MAIN_SHA" >> "$GITHUB_OUTPUT" + echo "build_ref=main" >> "$GITHUB_OUTPUT" + + docker: + needs: [prep, merge-to-main] uses: ./.github/workflows/docker.yml with: version: ${{ needs.prep.outputs.version }} build_type: Production + dry_run: ${{ inputs.mode == 'Dry run' }} secrets: inherit create-release: - needs: [prep, docker] + needs: [prep, merge-to-main, docker] + if: ${{ inputs.mode != 'Dry run' }} runs-on: blacksmith-2vcpu-ubuntu-2404 steps: - - name: Checkout repository + - name: Checkout main uses: actions/checkout@v5 with: + ref: main fetch-depth: 1 - name: Setup Node.js @@ -76,7 +297,23 @@ jobs: with: node-version-file: ".nvmrc" + - name: Clear existing release artifacts (overwrite mode) + if: ${{ inputs.mode == 'Overwrite release' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG="${{ needs.prep.outputs.release_tag }}" + if ! gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then + echo "Overwrite release: no existing release $TAG to overwrite." + exit 1 + fi + echo "Clearing existing assets from $TAG..." + gh release view "$TAG" --repo ${{ github.repository }} --json assets -q '.assets[].name' | while read -r ASSET; do + [ -n "$ASSET" ] && gh release delete-asset "$TAG" "$ASSET" --repo ${{ github.repository }} --yes || true + done + - name: Generate release body + if: ${{ inputs.mode != 'Overwrite release' }} run: | node scripts/generate-release-body.cjs \ --version "${{ needs.prep.outputs.version }}" \ @@ -84,6 +321,7 @@ jobs: --notes RELEASE_NOTES.md > RELEASE_BODY.md - name: Create or update GitHub release + if: ${{ inputs.mode != 'Overwrite release' }} env: GH_TOKEN: ${{ github.token }} run: | @@ -92,20 +330,22 @@ jobs: if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then gh release edit "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md else - gh release create "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md --target "${{ github.sha }}" + gh release create "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md --target "${{ needs.merge-to-main.outputs.main_sha }}" fi electron-release: - needs: [prep, create-release] + needs: [prep, merge-to-main, create-release] + if: ${{ always() && needs.merge-to-main.result == 'success' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped') }} uses: ./.github/workflows/electron.yml with: build_type: all - artifact_destination: release + artifact_destination: ${{ inputs.mode == 'Dry run' && 'file' || 'release' }} release_tag: ${{ needs.prep.outputs.release_tag }} secrets: inherit electron-submit: - needs: [electron-release] + needs: [prep, electron-release] + if: ${{ inputs.mode != 'Dry run' && inputs.mode != 'Skip submit' }} uses: ./.github/workflows/electron.yml with: build_type: all @@ -114,12 +354,13 @@ jobs: cask-commit-back: needs: [prep, electron-release] + if: ${{ inputs.mode != 'Dry run' }} runs-on: blacksmith-2vcpu-ubuntu-2404 permissions: contents: write steps: - name: Checkout main - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: main fetch-depth: 1 @@ -145,6 +386,146 @@ jobs: git config user.name "LukeGus" git config user.email "bugattiguy527@gmail.com" + git pull --rebase origin main git add Casks/termix.rb git commit -m "chore: bump Homebrew cask to $VERSION" git push origin HEAD:main + + docs: + needs: [prep, merge-to-main] + if: ${{ always() && needs.merge-to-main.result == 'success' && inputs.mode != 'Overwrite release' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout Termix + uses: actions/checkout@v5 + with: + ref: ${{ needs.merge-to-main.outputs.build_ref }} + fetch-depth: 1 + path: termix + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: "termix/.nvmrc" + cache: "npm" + cache-dependency-path: termix/package-lock.json + + - name: Generate OpenAPI spec + working-directory: termix + run: | + npm ci + npm run generate:openapi + + - name: Checkout Docs repository + uses: actions/checkout@v5 + with: + repository: Termix-SSH/Docs + ref: main + fetch-depth: 0 + token: ${{ secrets.GHCR_TOKEN }} + path: docs-repo + + - name: Create docs release branch + working-directory: docs-repo + run: git checkout -B "dev-${{ needs.prep.outputs.version }}" + + - name: Overwrite OpenAPI spec and regenerate API docs + working-directory: docs-repo + run: | + cp ../termix/openapi.json static/openapi.json + npm ci + npm run docusaurus gen-api-docs termix + + - name: Commit and push docs branch + working-directory: docs-repo + run: | + git config user.name "LukeGus" + git config user.email "bugattiguy527@gmail.com" + + if git diff --quiet; then + echo "No docs changes to commit." + exit 0 + fi + + if [ "${{ inputs.mode }}" = "Dry run" ]; then + echo "DRY RUN: would commit and push the following docs changes to dev-${{ needs.prep.outputs.version }}:" + git --no-pager diff --stat + exit 0 + fi + + git add -A + git commit -m "feat: update API docs for ${{ needs.prep.outputs.version }}" + git push origin "dev-${{ needs.prep.outputs.version }}" + + - name: Open and squash-merge docs PR + if: ${{ inputs.mode != 'Dry run' }} + working-directory: docs-repo + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + BRANCH="dev-${{ needs.prep.outputs.version }}" + if ! git ls-remote --exit-code origin "refs/heads/$BRANCH" >/dev/null 2>&1; then + echo "No docs branch pushed (nothing changed); skipping PR." + exit 0 + fi + + PR_NUMBER=$(gh pr list --repo Termix-SSH/Docs --head "$BRANCH" --base main --state open --json number -q '.[0].number' || true) + if [ -z "$PR_NUMBER" ]; then + PR_NUMBER=$(gh pr create --repo Termix-SSH/Docs \ + --base main --head "$BRANCH" \ + --title "release-${{ needs.prep.outputs.version }}" \ + --body "API docs for ${{ needs.prep.outputs.version }}" \ + | grep -oE '[0-9]+$') + fi + + gh pr merge "$PR_NUMBER" --repo Termix-SSH/Docs --squash --admin + + publish-youtube: + needs: [prep, electron-release] + if: ${{ always() && inputs.mode != 'Dry run' && inputs.mode != 'Overwrite release' && needs.electron-release.result == 'success' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Checkout main + uses: actions/checkout@v5 + with: + ref: main + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + + - name: Set release video to public + env: + YOUTUBE_CLIENT_ID: ${{ secrets.YOUTUBE_CLIENT_ID }} + YOUTUBE_CLIENT_SECRET: ${{ secrets.YOUTUBE_CLIENT_SECRET }} + YOUTUBE_REFRESH_TOKEN: ${{ secrets.YOUTUBE_REFRESH_TOKEN }} + run: node scripts/publish-youtube.cjs + + cleanup: + needs: + [ + prep, + merge-to-main, + electron-release, + cask-commit-back, + docs, + publish-youtube, + ] + if: ${{ always() && (inputs.mode == 'Everything' || inputs.mode == 'Skip submit') && needs.merge-to-main.result == 'success' && needs.electron-release.result == 'success' && needs.cask-commit-back.result == 'success' && needs.docs.result == 'success' && needs.publish-youtube.result == 'success' }} + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Delete dev branch in Termix + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + git push https://x-access-token:${{ secrets.GHCR_TOKEN }}@github.com/${{ github.repository }}.git \ + --delete "${{ needs.prep.outputs.dev_branch }}" || true + + - name: Delete dev branch in Docs + env: + GH_TOKEN: ${{ secrets.GHCR_TOKEN }} + run: | + git push https://x-access-token:${{ secrets.GHCR_TOKEN }}@github.com/Termix-SSH/Docs.git \ + --delete "dev-${{ needs.prep.outputs.version }}" || true diff --git a/README.md b/README.md index 6e29eba2..5184a732 100644 --- a/README.md +++ b/README.md @@ -94,59 +94,67 @@ Start, stop, pause, remove containers. View container stats. Control container u **SSH Host Manager:** -Save, organize, and manage your SSH connections with tags and folders, and easily save reusable login info while being able to automate the deployment of SSH keys. +Save, organize, and manage your SSH connections with tags and folders (folder customization and nested folder support), and easily save reusable login info while being able to automate the deployment of SSH keys. -**Server Stats:** -View CPU, memory, and disk usage along with network, uptime, system information, firewall, port monitor, on most Linux based servers. +**Host Metrics:** +View CPU, memory, disk usage, network, uptime, system information, firewall, port monitor, log viewer, users/permissions, certificates, and many more which work on most Linux based servers. **User Authentication:** -Secure user management with admin controls and OIDC (with access control) and 2FA (TOTP) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together. +Secure user management with admin controls and OIDC/LDAP/SSO (with access control) and 2FA (TOTP) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together. View audit log of all users actions. +**Tailscale Integration:** +List devices from your tailnet to quickly add them as hosts, and connect using Tailscale SSH as an authentication method, letting your tailnet ACLs handle authorization without storing credentials. + + + + **RBAC:** Create roles and share hosts across users/roles. + + **Database Encryption:** Backend stored as encrypted SQLite database files. View [docs](https://docs.termix.site/security) for more. - - **Network Graph:** Customize your Dashboard to visualize your homelab based off your SSH connections with status support. + + **SSH Tools:** Create reusable command snippets that execute with a single click. Run one command simultaneously across multiple open terminals. - - **Persistent Tabs:** SSH sessions and tabs stay open across devices/refreshes if enabled in user profile. + + **Languages:** @@ -170,7 +178,8 @@ Built-in support ~30 languages (managed by [Crowdin](https://docs.termix.site/tr - **Command History** - Auto-complete and view previously ran SSH commands - **Quick Connect** - Connect to a server without having to save the connection data - **Command Palette** - Double tap left shift to quickly access SSH connections with your keyboard -- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, etc. +- **Proxmox Integration** - Auto-add hosts into Termix from your Proxmox instance +- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal logging, etc. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9c9b2a48..dbf12138 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,55 +1,53 @@ -Bug fixes and new features including host-to-host file transfer, OIDC improvements, UI/UX updates, and numerous stability and security patches. +Bug fixes and new features, including Proxmox integration, SSO/OIDC redesign, revamped host metrics, tmux session management, and numerous UI and stability improvements. -https://youtu.be/At8iDk6-Q_s +https://youtu.be/ImwAbm4hW-k -- Added in-line buttons on host name row when using click to expand hosts in hosts list -- Expose admin_group via OIDC_ADMIN_GROUP env var -- Sync appearance preferences -- Support native OIDC callbacks -- Add portal Desktop DBUS permission for Flatpak URL opening -- Restore host password copy -- Support for single-host direct tunnels (ssh -L) -- Host to host file transfer in file manager -- Show ip/username without having to hover over hosts -- Updated credential list UI to match UI/UX of host list -- Restore rename host folder UI +- Improved terminal syntax highlighting with more customizability and reliability (toggle setting moved from user profile to host editor) +- Tmux session monitor/management +- Tailscale SSH authentication and device listing support +- SSO/OIDC redesign (multiple OIDC providers, LDAP, Google, GitHub support) +- Terminal session logging +- Customize side rail tab visibility +- Admin audit log +- Added `x.x.x` version tag alongside `release-x.x.x` for Docker +- OIDC custom group claim support +- Renamed server stats to host metrics +- Fully revamped host metrics page with new cards and dashboard like organizing system (services, process inspector, log viewer, cron jobs, packages, ssl cert management, firewall, user/permissions, health checks, disk breakdown, timers, and top by memory) +- Proxmox guest discovery and import integration +- Moved ssh host config outside of top tab bar and into new tab bar visible on SSH tab +- Improved folder management (nested folders, folder icons, folder colors, better folder selection, etc.) +- Storage preference to user profile settings (store/load toggles locally or in the DB) +- Sort/filter functions to credential list (copy of host list) -- Several security vulnerabilities -- Allow navigating away from split-view to non-pane tabs -- Restore SSH keepalive internal to use 30s to prevent random disconnects -- Apply guacamole-lite protocol patch in Docker builds -- Show correct icons for network interface types -- Resolve sudo password for shared host users -- Use jump hosts for online status check and metric collection -- Broaden sudo prompt detection for newer distros -- Recalculate terminal layout after web fonts load -- Improve terminal cwd detection and initial directory command -- Decode base64 file content as UTF-8 in file manager -- Normalize lazy import default export for iOS compatibility -- Prevent RDP display from snapping back after container resize -- Removed unused code and fixed PR checks and lint warnings -- Send name instruction for protocol >= 1.3.0 (guacd 1.5.0/1.1.0 errors) -- Wire up OIDC to password link dialog with submit/visibility -- Pass through command completion -- Resolve terminal jump hosts server-side -- Auto allow SSL certs for private network hosts -- Removed deprecated host management button from command palette -- Command palette opening wrong protocol -- Export/import failing for ssh key hosts -- Docker ssh2 native crypto not compiling in Docker -- Persisted terminal tabs attempt SSH on RDP hosts afater migration -- Credentials not appearing in host manager until refresh +- SFTP jump-host fallback from host data +- Guacd password incorrectly passed for app view +- Admin page failing to load admin information +- Disabled hardware acceleration on Windows to prevent startup crash +- Dashboard total credentials stuck at 0 +- Host username ignored when credential attached +- Clone host cannot switch auth method +- File manager context menu off-screen +- File delete affecting inactive tabs +- Silent delete failure on Windows hosts +- iPad host tab does nothing +- Docker console terminal background using incorrect colors +- Reliable OIDC group syncing for admin roles +- Guacd connections using incorrect screen height +- 2FA failing to disable +- Hostname fill entire column and truncate at proper spot in dashboard +- Make credentials start collapsed +- Incorrect JSDoc comments diff --git a/crowdin.yml b/crowdin.yml index 5ef3a0ce..765747aa 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,6 @@ +project_id: "858252" +api_token: "env:CROWDIN_API_TOKEN" + files: - source: /src/ui/locales/en.json translation: /src/ui/locales/translated/%locale_with_underscore%.json diff --git a/docker/nginx-https.conf b/docker/nginx-https.conf index ad7575ec..a3036727 100644 --- a/docker/nginx-https.conf +++ b/docker/nginx-https.conf @@ -58,6 +58,8 @@ http { listen ${SSL_PORT} ssl; server_name _; + absolute_redirect off; + ssl_certificate ${SSL_CERT_PATH}; ssl_certificate_key ${SSL_KEY_PATH}; @@ -187,6 +189,15 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location ~ ^/audit-logs(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location ~ ^/terminal(/.*)?$ { proxy_pass http://127.0.0.1:30001; proxy_http_version 1.1; @@ -312,6 +323,24 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location /session_logs/ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /tailscale/ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /ssh/websocket/ { proxy_pass http://127.0.0.1:30002/; proxy_http_version 1.1; @@ -522,6 +551,19 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location ~ ^/host-metrics(/.*)?$ { + proxy_pass http://127.0.0.1:30005; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } + location ~ ^/global-settings(/.*)?$ { proxy_pass http://127.0.0.1:30005; proxy_http_version 1.1; @@ -549,15 +591,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - location ~ ^/dashboard/preferences(/.*)?$ { - proxy_pass http://127.0.0.1:30006; - proxy_http_version 1.1; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - location ^~ /docker/console/ { proxy_pass http://127.0.0.1:30009/; proxy_http_version 1.1; @@ -583,6 +616,21 @@ http { proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; } + # --- tmux-monitor begin --- + location ~ ^/tmux_monitor(/.*)?$ { + proxy_pass http://127.0.0.1:30010; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + # --- tmux-monitor end --- + location ~ ^/docker(/.*)?$ { proxy_pass http://127.0.0.1:30007; proxy_http_version 1.1; diff --git a/docker/nginx.conf b/docker/nginx.conf index 36846d61..dd5eabda 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -51,6 +51,8 @@ http { listen ${PORT}; server_name _; + absolute_redirect off; + add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; @@ -167,6 +169,18 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location ~ ^/proxmox(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 60s; + proxy_send_timeout 120s; + proxy_read_timeout 120s; + } + location ~ ^/c2s-tunnel-presets(/.*)?$ { proxy_pass http://127.0.0.1:30001; proxy_http_version 1.1; @@ -176,6 +190,15 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location ~ ^/audit-logs(/.*)?$ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location ~ ^/terminal(/.*)?$ { proxy_pass http://127.0.0.1:30001; proxy_http_version 1.1; @@ -301,6 +324,24 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location /session_logs/ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /tailscale/ { + proxy_pass http://127.0.0.1:30001; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location /ssh/websocket/ { proxy_pass http://127.0.0.1:30002/; proxy_http_version 1.1; @@ -511,6 +552,19 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } + location ~ ^/host-metrics(/.*)?$ { + proxy_pass http://127.0.0.1:30005; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } + location ~ ^/global-settings(/.*)?$ { proxy_pass http://127.0.0.1:30005; proxy_http_version 1.1; @@ -538,15 +592,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - location ~ ^/dashboard/preferences(/.*)?$ { - proxy_pass http://127.0.0.1:30006; - proxy_http_version 1.1; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - location ^~ /docker/console/ { proxy_pass http://127.0.0.1:30009/; proxy_http_version 1.1; @@ -572,6 +617,21 @@ http { proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; } + # --- tmux-monitor begin --- + location ~ ^/tmux_monitor(/.*)?$ { + proxy_pass http://127.0.0.1:30010; + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + # --- tmux-monitor end --- + location ~ ^/docker(/.*)?$ { proxy_pass http://127.0.0.1:30007; proxy_http_version 1.1; diff --git a/electron/main.cjs b/electron/main.cjs index 59dedcb3..104ec5a2 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -478,6 +478,10 @@ if (process.platform === "linux") { app.commandLine.appendSwitch("--enable-features=VaapiVideoDecoder"); } +if (process.platform === "win32") { + app.disableHardwareAcceleration(); +} + if (isInsecureModeEnabled()) { logToFile( "[security] ENABLE_INSECURE_MODE is enabled; TLS certificate validation is disabled.", diff --git a/package-lock.json b/package-lock.json index 1adeec35..1b918430 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "termix", - "version": "2.3.2", + "version": "2.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "termix", - "version": "2.3.2", + "version": "2.4.0", "hasInstallScript": true, "dependencies": { - "axios": "^1.15.2", + "@types/ldapjs": "^3.0.6", + "axios": "^1.17.0", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.9.0", "body-parser": "^2.2.2", @@ -21,9 +22,10 @@ "express": "^5.2.1", "guacamole-lite": "^1.2.0", "jose": "^6.2.2", - "js-yaml": "^4.1.1", + "js-yaml": "^4.2.0", "jsonwebtoken": "^9.0.3", "jszip": "^3.10.1", + "ldapjs": "^3.0.7", "motion": "^12.38.0", "multer": "^2.1.1", "nanoid": "^5.1.9", @@ -31,39 +33,39 @@ "socks": "^2.8.7", "speakeasy": "^2.0.0", "ssh2": "^1.17.0", - "undici": "^7.0.0", + "undici": "^8.4.0", "ws": "^8.20.0" }, "devDependencies": { - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.10.3", "@codemirror/search": "^6.7.0", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.41.1", - "@commitlint/cli": "^21.0.1", - "@commitlint/config-conventional": "^21.0.1", + "@commitlint/cli": "^21.0.2", + "@commitlint/config-conventional": "^21.0.2", "@deadendjs/swagger-jsdoc": "^8.1.2", "@electron/notarize": "^3.1.1", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.0.0", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@monaco-editor/react": "^4.7.0", - "@radix-ui/react-accordion": "^1.2.12", - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-checkbox": "^1.3.3", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-progress": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-accordion": "^1.2.13", + "@radix-ui/react-alert-dialog": "^1.1.16", + "@radix-ui/react-checkbox": "^1.3.4", + "@radix-ui/react-dialog": "^1.1.16", + "@radix-ui/react-dropdown-menu": "^2.1.17", + "@radix-ui/react-label": "^2.1.9", + "@radix-ui/react-popover": "^1.1.16", + "@radix-ui/react-progress": "^1.1.9", + "@radix-ui/react-scroll-area": "^1.2.11", "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-separator": "^1.1.9", "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", + "@radix-ui/react-tabs": "^1.1.14", + "@radix-ui/react-tooltip": "^1.2.9", "@tailwindcss/vite": "^4.2.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -77,9 +79,9 @@ "@types/js-yaml": "^4.0.9", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.1.0", - "@types/node": "^25.9.1", + "@types/node": "^25.9.2", "@types/qrcode": "^1.5.6", - "@types/react": "^19.2.14", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@types/speakeasy": "^2.0.10", "@types/ssh2": "^1.15.5", @@ -109,16 +111,16 @@ "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "husky": "^9.1.7", - "i18next": "^26.0.8", + "i18next": "^26.3.1", "i18next-browser-languagedetector": "^8.2.1", "jsdom": "^29.1.1", - "lint-staged": "^17.0.5", + "lint-staged": "^17.0.7", "lucide-react": "^1.11.0", "prettier": "3.8.3", "radix-ui": "^1.4.3", - "react": "^19.2.5", + "react": "^19.2.7", "react-cytoscapejs": "^2.0.0", - "react-dom": "^19.2.5", + "react-dom": "^19.2.7", "react-h5-audio-player": "^3.10.2", "react-hook-form": "^7.73.1", "react-i18next": "^17.0.4", @@ -136,7 +138,7 @@ "tw-animate-css": "^1.4.0", "typescript": "~6.0.3", "typescript-eslint": "^8.59.0", - "vite": "^8.0.13", + "vite": "^8.0.16", "vite-plugin-svgr": "^5.2.0", "vitest": "^4.1.8" }, @@ -549,9 +551,9 @@ } }, "node_modules/@codemirror/autocomplete": { - "version": "6.20.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.2.tgz", - "integrity": "sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==", + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", "dev": true, "license": "MIT", "dependencies": { @@ -946,16 +948,16 @@ } }, "node_modules/@commitlint/cli": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.0.1.tgz", - "integrity": "sha512-8vq10krmbJwBkvzXKhbs4o4JQEVscd3pqOlWuDUaDBwbeL694/P33UC29tZQFTAgPU9fVJ2+f2m3zw16yKWxHg==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.0.2.tgz", + "integrity": "sha512-YMmfLbqBg+ZRvvmPhc+cilSQFrh/AgzVgCT1U/OifmUZEwPbvCtA8rN//YNaF9d5eoZphxVMGYtmwA2QgQORgg==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/format": "^21.0.1", - "@commitlint/lint": "^21.0.1", - "@commitlint/load": "^21.0.1", - "@commitlint/read": "^21.0.1", + "@commitlint/lint": "^21.0.2", + "@commitlint/load": "^21.0.2", + "@commitlint/read": "^21.0.2", "@commitlint/types": "^21.0.1", "tinyexec": "^1.0.0", "yargs": "^18.0.0" @@ -1067,9 +1069,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.0.1.tgz", - "integrity": "sha512-gRorrkfWOh/+V5X8GYWWbQvrzPczopGMS4CCNrQdHkK4xWElv82BDvIsDhJZWTlI7TazOlYea6VATufCsFs+sw==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.0.2.tgz", + "integrity": "sha512-P/ZRhryQmkj0Z0dY9FOoRwe3xkwJyyAdtXwt01NT2kuZttcG2CNYp1q5Ci3u+nDT2jcbJRw2kt13Czl1qKNPfg==", "dev": true, "license": "MIT", "dependencies": { @@ -1133,9 +1135,9 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.0.1.tgz", - "integrity": "sha512-iNDP8SFdw8JEkM0CHZ2XFnhTN4Zg5jKUY2d8kBOSFrI2aA+3YJI7fcqVpfgbpJ9xtxFVYpi+DBATU5AvhoTq8g==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.0.2.tgz", + "integrity": "sha512-H5z4t8PC9tUsmZ/o+EptM3Nq8sTFtskAShdcqxCoyzklW5eaVT5xbrDAET2uypzir9Vsj4ZZmBtyKjYe2XqgeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1147,15 +1149,15 @@ } }, "node_modules/@commitlint/lint": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.0.1.tgz", - "integrity": "sha512-gF+iYtUw1gBG3HUH9z3VxwUjGg2R2G5j+nmvPs8aIeYkiB7TtneBu3wO85I0bUl93bYNsvsCNI9Nte2fmDUMww==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.0.2.tgz", + "integrity": "sha512-PnUmLYGeGLfW8oVatR9KpNxSHYAnJOEWlMZzfdeFOUq6WUrFx1fGQaWCWJqMoIll/xPM+GdfJV+tKHZVHhl0Fg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^21.0.1", - "@commitlint/parse": "^21.0.1", - "@commitlint/rules": "^21.0.1", + "@commitlint/is-ignored": "^21.0.2", + "@commitlint/parse": "^21.0.2", + "@commitlint/rules": "^21.0.2", "@commitlint/types": "^21.0.1" }, "engines": { @@ -1163,9 +1165,9 @@ } }, "node_modules/@commitlint/load": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.0.1.tgz", - "integrity": "sha512-Btg1q1mKmiihN4W3x0EsPDrJMOQfMa9NIqlzlJyXAfxvsOGdGXOW5p3R3RcSxDCaY7JabY9flIl+Om1af3PSrw==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.0.2.tgz", + "integrity": "sha512-lwUE70hN0/qE/ZRROhbaX65ly/FF12DrqfReLCESo37M0OQCFAf2jRS+2tSCSORq+bm4Kdju7qNDj46uc1QzTA==", "dev": true, "license": "MIT", "dependencies": { @@ -1184,9 +1186,9 @@ } }, "node_modules/@commitlint/message": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.0.1.tgz", - "integrity": "sha512-R3dVQeJQ0B6yqrZEjkUHD4r7UJYLV9Lvk2xs3PTOmtWk2G3mI6Xgc+YdRxL1PwcDfBiUjv2SkIkW4AUc976w1w==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.0.2.tgz", + "integrity": "sha512-5n4aqHGD/FNnom/D5L8i7cYtV+xjuXcBL832C3w9VglEsZzIsoHpJsvxzJ7cgiOsOdc/2jU4t5+7qMHh7GBX3g==", "dev": true, "license": "MIT", "engines": { @@ -1194,9 +1196,9 @@ } }, "node_modules/@commitlint/parse": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.0.1.tgz", - "integrity": "sha512-oh/nCSOqdoeQNA1tO8aAmxkq5EBo8/NzcFQRvv66AWc9HpED28sL2iSicCKU6hPintWuscL6BJEWi77Wq1LPMQ==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.0.2.tgz", + "integrity": "sha512-QVZJhGHTm+oiuWyEKOCTQ0ZM3mfJ0eGWFeHuj7WzSKEth+UukcCHac9GD8pgdFlg/qGkFWOtyaNd1T8REgagaw==", "dev": true, "license": "MIT", "dependencies": { @@ -1209,13 +1211,13 @@ } }, "node_modules/@commitlint/read": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.0.1.tgz", - "integrity": "sha512-pMEu4lbpC8W0ZgKJj2U6WaobXIZWdFlULpIEewYhkPXx+WZcnoO53YrVPc7QErQuNolq2Me8dP58Wu7YAVXVOA==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.0.2.tgz", + "integrity": "sha512-BtsrnLVycSSKf4Q0gMch4giCj5NNlmcbhc8ra5vONgGtP2IjRDo33bEFtr5Pm+2N+5fXGWb2MksWPrspPfdhdw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/top-level": "^21.0.1", + "@commitlint/top-level": "^21.0.2", "@commitlint/types": "^21.0.1", "git-raw-commits": "^5.0.0", "tinyexec": "^1.0.0" @@ -1242,14 +1244,14 @@ } }, "node_modules/@commitlint/rules": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.0.1.tgz", - "integrity": "sha512-VMooYpz4nJg7xlaUso6CCOWEz8D/ChkvsvZUMARcoJ1ZpfKPyFCGrHNha2tbsETNAb6ErgiRuCr2DvghrvPDYQ==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.0.2.tgz", + "integrity": "sha512-k6tQ69Td7t2qUSIbik8D3TL1q3ZJpkEbV+yLogDzCRAdOxJm4ndhtBNREsLA1/puRfWvzS9eioF2w43WT+hHgQ==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/ensure": "^21.0.1", - "@commitlint/message": "^21.0.1", + "@commitlint/message": "^21.0.2", "@commitlint/to-lines": "^21.0.1", "@commitlint/types": "^21.0.1" }, @@ -1268,9 +1270,9 @@ } }, "node_modules/@commitlint/top-level": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.0.1.tgz", - "integrity": "sha512-4esUYqzY7K0FCgcJ/1xWEZekV7Ch4yZT1+xjEb7KzqbJ05XEkxHVsTfC8ADKNNtlCE2pj98KEbPGZWw9WwEnVw==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.0.2.tgz", + "integrity": "sha512-s9KKM+e+mXgFeIh4n7KmOGAVT3mkJ3Fp1bBYHIK5pjeUwlEMzp/tZfb5u0Poa680AsQTXMEMRxZi1vQ9m2X5ug==", "dev": true, "license": "MIT", "dependencies": { @@ -1663,6 +1665,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@electron/get/node_modules/undici": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz", + "integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/@electron/notarize": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.1.tgz", @@ -2321,9 +2334,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2341,9 +2351,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2361,9 +2368,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2381,9 +2385,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2401,9 +2402,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2421,9 +2419,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2441,9 +2436,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2461,9 +2453,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2481,9 +2470,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2507,9 +2493,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2533,9 +2516,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2559,9 +2539,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2585,9 +2562,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2611,9 +2585,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2637,9 +2608,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2663,9 +2631,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2834,6 +2799,101 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@ldapjs/asn1": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz", + "integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT" + }, + "node_modules/@ldapjs/attribute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz", + "integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "2.0.0", + "@ldapjs/protocol": "^1.2.1", + "process-warning": "^2.1.0" + } + }, + "node_modules/@ldapjs/change": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz", + "integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "2.0.0", + "@ldapjs/attribute": "1.0.0" + } + }, + "node_modules/@ldapjs/controls": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz", + "integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "^1.2.0", + "@ldapjs/protocol": "^1.2.1" + } + }, + "node_modules/@ldapjs/controls/node_modules/@ldapjs/asn1": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz", + "integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT" + }, + "node_modules/@ldapjs/dn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz", + "integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "2.0.0", + "process-warning": "^2.1.0" + } + }, + "node_modules/@ldapjs/filter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz", + "integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "2.0.0", + "@ldapjs/protocol": "^1.2.1", + "process-warning": "^2.1.0" + } + }, + "node_modules/@ldapjs/messages": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz", + "integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "^2.0.0", + "@ldapjs/attribute": "^1.0.0", + "@ldapjs/change": "^1.0.0", + "@ldapjs/controls": "^2.1.0", + "@ldapjs/dn": "^1.1.0", + "@ldapjs/filter": "^2.1.1", + "@ldapjs/protocol": "^1.2.1", + "process-warning": "^2.2.0" + } + }, + "node_modules/@ldapjs/protocol": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz", + "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT" + }, "node_modules/@lezer/common": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", @@ -3238,9 +3298,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3262,9 +3319,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3286,9 +3340,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3310,9 +3361,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3334,9 +3382,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3412,9 +3457,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.132.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", - "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -3467,21 +3512,21 @@ } }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", - "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.13.tgz", + "integrity": "sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collapsible": "1.1.13", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -3498,19 +3543,28 @@ } } }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collapsible": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.13.tgz", + "integrity": "sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3527,14 +3581,89 @@ } } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.9.tgz", + "integrity": "sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-direction": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz", + "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3546,6 +3675,201 @@ } } }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.16.tgz", + "integrity": "sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dialog": "1.1.16", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", @@ -3623,20 +3947,20 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", - "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.4.tgz", + "integrity": "sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-use-previous": "1.1.2", + "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3653,6 +3977,183 @@ } } }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-previous": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.2.tgz", + "integrity": "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-size": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz", + "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", @@ -3792,26 +4293,26 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.16.tgz", + "integrity": "sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -3828,15 +4329,287 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz", + "integrity": "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-escape-keydown": "1.1.2" }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", + "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.9.tgz", + "integrity": "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.11.tgz", + "integrity": "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", + "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -3892,19 +4665,19 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.17.tgz", + "integrity": "sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-menu": "2.1.17", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -3921,6 +4694,515 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-arrow": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.9.tgz", + "integrity": "sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-collection": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.9.tgz", + "integrity": "sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-direction": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz", + "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz", + "integrity": "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-escape-keydown": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", + "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.9.tgz", + "integrity": "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-menu": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.17.tgz", + "integrity": "sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-popper": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.0.tgz", + "integrity": "sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-rect": "1.1.2", + "@radix-ui/react-use-size": "1.1.2", + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-portal": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.11.tgz", + "integrity": "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.12.tgz", + "integrity": "sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", + "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz", + "integrity": "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-size": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz", + "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.2.tgz", + "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", @@ -4068,13 +5350,13 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", - "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.9.tgz", + "integrity": "sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.4" + "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", @@ -4092,13 +5374,13 @@ } }, "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" + "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", @@ -4312,27 +5594,27 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", - "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.16.tgz", + "integrity": "sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -4349,14 +5631,147 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.9.tgz", + "integrity": "sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz", + "integrity": "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-escape-keydown": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", + "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.9.tgz", + "integrity": "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -4368,6 +5783,247 @@ } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.0.tgz", + "integrity": "sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-rect": "1.1.2", + "@radix-ui/react-use-size": "1.1.2", + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.11.tgz", + "integrity": "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", + "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz", + "integrity": "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-size": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz", + "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.2.tgz", + "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", @@ -4495,14 +6151,14 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", - "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.9.tgz", + "integrity": "sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4" + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", @@ -4520,9 +6176,9 @@ } }, "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4536,13 +6192,13 @@ } }, "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" + "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", @@ -4625,21 +6281,21 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.11.tgz", + "integrity": "sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/number": "1.1.2", + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -4656,6 +6312,148 @@ } } }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/number": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.2.tgz", + "integrity": "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-direction": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz", + "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", @@ -4720,13 +6518,13 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", - "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.9.tgz", + "integrity": "sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.4" + "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", @@ -4744,13 +6542,13 @@ } }, "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" + "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", @@ -4802,13 +6600,13 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.5.tgz", + "integrity": "sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", @@ -4820,6 +6618,22 @@ } } }, + "node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-switch": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", @@ -4851,20 +6665,20 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.14.tgz", + "integrity": "sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-roving-focus": "1.1.12", + "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -4881,6 +6695,258 @@ } } }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.9.tgz", + "integrity": "sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz", + "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.12.tgz", + "integrity": "sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-collection": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-direction": "1.1.2", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toast": { "version": "1.2.15", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", @@ -5027,24 +7093,24 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.9.tgz", + "integrity": "sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-popper": "1.3.0", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", @@ -5061,14 +7127,105 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.9.tgz", + "integrity": "sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz", + "integrity": "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-escape-keydown": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -5080,6 +7237,271 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.0.tgz", + "integrity": "sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.9", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.2", + "@radix-ui/react-use-rect": "1.1.2", + "@radix-ui/react-use-size": "1.1.2", + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.11.tgz", + "integrity": "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", + "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz", + "integrity": "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-size": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz", + "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.5.tgz", + "integrity": "sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.2.tgz", + "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", @@ -5324,9 +7746,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", - "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -5341,9 +7763,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -5358,9 +7780,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", - "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -5375,9 +7797,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", - "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -5392,9 +7814,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", - "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -5409,16 +7831,13 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", - "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5429,16 +7848,13 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", - "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5449,16 +7865,13 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", - "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5469,16 +7882,13 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", - "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5489,16 +7899,13 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", - "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5509,16 +7916,13 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", - "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5529,9 +7933,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", - "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -5546,9 +7950,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", - "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -5565,9 +7969,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", - "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -5582,9 +7986,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", - "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -6049,9 +8453,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6069,9 +8470,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6089,9 +8487,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6109,9 +8504,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6497,6 +8889,15 @@ "@types/node": "*" } }, + "node_modules/@types/ldapjs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-3.0.6.tgz", + "integrity": "sha512-E2Tn1ltJDYBsidOT9QG4engaQeQzRQ9aYNxVmjCkD33F7cIeLPgrRDXAYs0O35mK2YDU20c/+ZkNjeAPRGLM0Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -6525,10 +8926,9 @@ } }, "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "dev": true, + "version": "25.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", + "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -6578,9 +8978,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", - "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -7327,6 +9727,12 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -7672,9 +10078,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.8" } @@ -7763,9 +10167,9 @@ } }, "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", + "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", @@ -7813,6 +10217,18 @@ } } }, + "node_modules/backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "license": "MIT", + "dependencies": { + "precond": "0.2" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -8868,9 +11284,9 @@ } }, "node_modules/cosmiconfig": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", - "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", "dev": true, "license": "MIT", "dependencies": { @@ -10297,12 +12713,10 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, "engines": [ "node >=0.6.0" ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -11201,9 +13615,9 @@ } }, "node_modules/i18next": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.0.tgz", - "integrity": "sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA==", + "version": "26.3.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.1.tgz", + "integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==", "dev": true, "funding": [ { @@ -11668,9 +14082,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -11730,6 +14154,16 @@ "node": "20 || >=22" } }, + "node_modules/jsdom/node_modules/undici": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz", + "integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -11869,6 +14303,29 @@ "dev": true, "license": "MIT" }, + "node_modules/ldapjs": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz", + "integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==", + "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", + "dependencies": { + "@ldapjs/asn1": "^2.0.0", + "@ldapjs/attribute": "^1.0.0", + "@ldapjs/change": "^1.0.0", + "@ldapjs/controls": "^2.1.0", + "@ldapjs/dn": "^1.1.0", + "@ldapjs/filter": "^2.1.1", + "@ldapjs/messages": "^1.3.0", + "@ldapjs/protocol": "^1.2.1", + "abstract-logging": "^2.0.1", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "once": "^1.4.0", + "vasync": "^2.2.1", + "verror": "^1.10.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -12035,9 +14492,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12059,9 +14513,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12083,9 +14534,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12107,9 +14555,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12173,16 +14618,16 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.5.tgz", - "integrity": "sha512-d12yC+/e8RhBjZtaxZn71FyrgU/P5e+uAPifhCLwdosQZP/zamSdKRWDC30ocVIbzDKiFG1McHc/LUgB92GIPw==", + "version": "17.0.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.7.tgz", + "integrity": "sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==", "dev": true, "license": "MIT", "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", - "tinyexec": "^1.1.2" + "tinyexec": "^1.2.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -12194,7 +14639,7 @@ "url": "https://opencollective.com/lint-staged" }, "optionalDependencies": { - "yaml": "^2.8.4" + "yaml": "^2.9.0" } }, "node_modules/lint-staged/node_modules/yaml": { @@ -14493,6 +16938,14 @@ "node": ">=10" } }, + "node_modules/precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14590,6 +17043,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/process-warning": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==", + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -14956,6 +17415,165 @@ } } }, + "node_modules/radix-ui/node_modules/@radix-ui/react-accordion": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/radix-ui/node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/radix-ui/node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/radix-ui/node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/radix-ui/node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/radix-ui/node_modules/@radix-ui/react-label": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", @@ -14980,6 +17598,44 @@ } } }, + "node_modules/radix-ui/node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/radix-ui/node_modules/@radix-ui/react-progress": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", @@ -15005,6 +17661,38 @@ } } }, + "node_modules/radix-ui/node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/radix-ui/node_modules/@radix-ui/react-separator": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", @@ -15048,6 +17736,72 @@ } } }, + "node_modules/radix-ui/node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/radix-ui/node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -15103,9 +17857,9 @@ } }, "node_modules/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", - "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "dev": true, "license": "MIT", "engines": { @@ -15127,16 +17881,16 @@ } }, "node_modules/react-dom": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", - "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "dev": true, "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.6" + "react": "^19.2.7" } }, "node_modules/react-h5-audio-player": { @@ -15614,13 +18368,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", - "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.132.0", + "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -15630,21 +18384,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.2", - "@rolldown/binding-darwin-arm64": "1.0.2", - "@rolldown/binding-darwin-x64": "1.0.2", - "@rolldown/binding-freebsd-x64": "1.0.2", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", - "@rolldown/binding-linux-arm64-gnu": "1.0.2", - "@rolldown/binding-linux-arm64-musl": "1.0.2", - "@rolldown/binding-linux-ppc64-gnu": "1.0.2", - "@rolldown/binding-linux-s390x-gnu": "1.0.2", - "@rolldown/binding-linux-x64-gnu": "1.0.2", - "@rolldown/binding-linux-x64-musl": "1.0.2", - "@rolldown/binding-openharmony-arm64": "1.0.2", - "@rolldown/binding-wasm32-wasi": "1.0.2", - "@rolldown/binding-win32-arm64-msvc": "1.0.2", - "@rolldown/binding-win32-x64-msvc": "1.0.2" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/router": { @@ -16588,9 +19342,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.2.tgz", - "integrity": "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -16598,9 +19352,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -16887,19 +19641,18 @@ } }, "node_modules/undici": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.26.0.tgz", - "integrity": "sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.4.0.tgz", + "integrity": "sha512-tDR4LgFBeV7YBWOFMlmbhtrnFVWuZ6HKbkG+88/csQdhK2tPlW78PnLI2VRjIQtTlslvgSZ4R43WBE+4g6rhng==", "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, "license": "MIT" }, "node_modules/unified": { @@ -17132,13 +19885,43 @@ "node": ">= 0.8" } }, + "node_modules/vasync": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz", + "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "verror": "1.10.0" + } + }, + "node_modules/vasync/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/vasync/node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, "license": "MIT", - "optional": true, "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -17152,9 +19935,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/vfile": { "version": "6.0.3", @@ -17187,17 +19968,17 @@ } }, "node_modules/vite": { - "version": "8.0.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", - "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", - "rolldown": "1.0.2", - "tinyglobby": "^0.2.16" + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index 88a8cc0b..83d6cbed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "termix", "private": true, - "version": "2.3.2", + "version": "2.4.0", "description": "Self-hosted SSH and remote desktop management.", "author": "Karmaa", "main": "electron/main.cjs", @@ -41,7 +41,8 @@ "build:mac-dev": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --mac dir --publish=never" }, "dependencies": { - "axios": "^1.15.2", + "@types/ldapjs": "^3.0.6", + "axios": "^1.17.0", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.9.0", "body-parser": "^2.2.2", @@ -53,9 +54,10 @@ "express": "^5.2.1", "guacamole-lite": "^1.2.0", "jose": "^6.2.2", - "js-yaml": "^4.1.1", + "js-yaml": "^4.2.0", "jsonwebtoken": "^9.0.3", "jszip": "^3.10.1", + "ldapjs": "^3.0.7", "motion": "^12.38.0", "multer": "^2.1.1", "nanoid": "^5.1.9", @@ -63,39 +65,39 @@ "socks": "^2.8.7", "speakeasy": "^2.0.0", "ssh2": "^1.17.0", - "undici": "^7.0.0", + "undici": "^8.4.0", "ws": "^8.20.0" }, "devDependencies": { - "@codemirror/autocomplete": "^6.20.1", + "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.10.3", "@codemirror/search": "^6.7.0", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.41.1", - "@commitlint/cli": "^21.0.1", - "@commitlint/config-conventional": "^21.0.1", + "@commitlint/cli": "^21.0.2", + "@commitlint/config-conventional": "^21.0.2", "@deadendjs/swagger-jsdoc": "^8.1.2", "@electron/notarize": "^3.1.1", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.0.0", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@monaco-editor/react": "^4.7.0", - "@radix-ui/react-accordion": "^1.2.12", - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-checkbox": "^1.3.3", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-progress": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-accordion": "^1.2.13", + "@radix-ui/react-alert-dialog": "^1.1.16", + "@radix-ui/react-checkbox": "^1.3.4", + "@radix-ui/react-dialog": "^1.1.16", + "@radix-ui/react-dropdown-menu": "^2.1.17", + "@radix-ui/react-label": "^2.1.9", + "@radix-ui/react-popover": "^1.1.16", + "@radix-ui/react-progress": "^1.1.9", + "@radix-ui/react-scroll-area": "^1.2.11", "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-separator": "^1.1.9", "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", + "@radix-ui/react-tabs": "^1.1.14", + "@radix-ui/react-tooltip": "^1.2.9", "@tailwindcss/vite": "^4.2.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", @@ -109,9 +111,9 @@ "@types/js-yaml": "^4.0.9", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.1.0", - "@types/node": "^25.9.1", + "@types/node": "^25.9.2", "@types/qrcode": "^1.5.6", - "@types/react": "^19.2.14", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@types/speakeasy": "^2.0.10", "@types/ssh2": "^1.15.5", @@ -141,16 +143,16 @@ "globals": "^17.5.0", "guacamole-common-js": "^1.5.0", "husky": "^9.1.7", - "i18next": "^26.0.8", + "i18next": "^26.3.1", "i18next-browser-languagedetector": "^8.2.1", "jsdom": "^29.1.1", - "lint-staged": "^17.0.5", + "lint-staged": "^17.0.7", "lucide-react": "^1.11.0", "prettier": "3.8.3", "radix-ui": "^1.4.3", - "react": "^19.2.5", + "react": "^19.2.7", "react-cytoscapejs": "^2.0.0", - "react-dom": "^19.2.5", + "react-dom": "^19.2.7", "react-h5-audio-player": "^3.10.2", "react-hook-form": "^7.73.1", "react-i18next": "^17.0.4", @@ -168,7 +170,7 @@ "tw-animate-css": "^1.4.0", "typescript": "~6.0.3", "typescript-eslint": "^8.59.0", - "vite": "^8.0.13", + "vite": "^8.0.16", "vite-plugin-svgr": "^5.2.0", "vitest": "^4.1.8" }, diff --git a/readme/README-AR.md b/readme/README-AR.md index c6524a3f..f7e02e4b 100644 --- a/readme/README-AR.md +++ b/readme/README-AR.md @@ -94,21 +94,21 @@ Termix هي منصة مفتوحة المصدر ومجانية للأبد وذا **مدير مضيفات SSH:** -حفظ وتنظيم وإدارة اتصالات SSH الخاصة بك باستخدام العلامات والمجلدات، وحفظ بيانات تسجيل الدخول القابلة لإعادة الاستخدام بسهولة مع إمكانية أتمتة نشر مفاتيح SSH. +حفظ وتنظيم وإدارة اتصالات SSH الخاصة بك باستخدام العلامات والمجلدات (مع دعم تخصيص المجلدات والمجلدات المتداخلة)، وحفظ بيانات تسجيل الدخول القابلة لإعادة الاستخدام بسهولة مع إمكانية أتمتة نشر مفاتيح SSH. -**إحصائيات الخادم:** -عرض استخدام المعالج والذاكرة والقرص إلى جانب الشبكة ووقت التشغيل ومعلومات النظام وجدار الحماية ومراقب المنافذ على معظم الخوادم المبنية على Linux. +**مقاييس المضيف:** +عرض استخدام المعالج والذاكرة والقرص والشبكة ووقت التشغيل ومعلومات النظام وجدار الحماية ومراقب المنافذ وعارض السجلات والمستخدمين/الصلاحيات والشهادات وغيرها الكثير، تعمل على معظم الخوادم المبنية على Linux. **مصادقة المستخدمين:** -إدارة آمنة للمستخدمين مع ضوابط إدارية ودعم OIDC (مع التحكم في الوصول) و 2FA (TOTP). عرض جلسات المستخدمين النشطة عبر جميع المنصات وإلغاء الصلاحيات. ربط حسابات OIDC/المحلية معاً. +إدارة آمنة للمستخدمين مع ضوابط إدارية ودعم OIDC/LDAP/SSO (مع التحكم في الوصول) و 2FA (TOTP). عرض جلسات المستخدمين النشطة عبر جميع المنصات وإلغاء الصلاحيات. ربط حسابات OIDC/المحلية معاً. عرض سجل تدقيق لجميع إجراءات المستخدمين. @@ -170,7 +170,8 @@ Termix هي منصة مفتوحة المصدر ومجانية للأبد وذا - **سجل الأوامر** - الإكمال التلقائي وعرض أوامر SSH التي تم تنفيذها سابقاً - **الاتصال السريع** - الاتصال بخادم دون الحاجة إلى حفظ بيانات الاتصال - **لوحة الأوامر** - اضغط مرتين على Shift الأيسر للوصول السريع إلى اتصالات SSH باستخدام لوحة المفاتيح -- **ميزات SSH الغنية** - دعم مضيفات القفز، Warpgate، الاتصالات المبنية على TOTP، SOCKS5، التحقق من مفتاح المضيف، الملء التلقائي لكلمة المرور، [OPKSSH](https://github.com/openpubkey/opkssh)، tmux، port knocking، إلخ. +- **تكامل Proxmox** - إضافة المضيفات تلقائياً إلى Termix من نسخة Proxmox الخاصة بك +- **ميزات SSH الغنية** - دعم مضيفات القفز، Warpgate، الاتصالات المبنية على TOTP، SOCKS5، التحقق من مفتاح المضيف، الملء التلقائي لكلمة المرور، [OPKSSH](https://github.com/openpubkey/opkssh)، tmux، port knocking، تسجيل الطرفية، إلخ. diff --git a/readme/README-CN.md b/readme/README-CN.md index e420ead9..7cf79e01 100644 --- a/readme/README-CN.md +++ b/readme/README-CN.md @@ -94,21 +94,21 @@ Termix 是一个开源、永久免费、自托管的一体化服务器管理平 **SSH 主机管理器:** -通过标签和文件夹保存、组织和管理您的 SSH 连接,轻松保存可重用的登录信息,并能自动化部署 SSH 密钥。 +通过标签和文件夹(支持文件夹自定义和嵌套文件夹)保存、组织和管理您的 SSH 连接,轻松保存可重用的登录信息,并能自动化部署 SSH 密钥。 -**服务器统计:** -在大多数基于 Linux 的服务器上查看 CPU、内存、磁盘使用情况以及网络、运行时间、系统信息、防火墙和端口监控。 +**主机指标:** +在大多数基于 Linux 的服务器上查看 CPU、内存、磁盘使用情况、网络、运行时间、系统信息、防火墙、端口监控、日志查看器、用户/权限、证书等更多信息。 **用户认证:** -安全的用户管理,具有管理员控制、OIDC(带访问控制)和 2FA (TOTP) 支持。查看所有平台上的活动用户会话并撤销权限。将您的 OIDC/本地账户链接在一起。 +安全的用户管理,具有管理员控制、OIDC/LDAP/SSO(带访问控制)和 2FA (TOTP) 支持。查看所有平台上的活动用户会话并撤销权限。将您的 OIDC/本地账户链接在一起。查看所有用户操作的审计日志。 @@ -170,7 +170,8 @@ Termix 是一个开源、永久免费、自托管的一体化服务器管理平 - **命令历史** - 自动完成并查看之前运行过的 SSH 命令 - **快速连接** - 无需保存连接数据即可连接到服务器 - **命令面板** - 双击左 Shift 键即可通过键盘快速访问 SSH 连接 -- **丰富的 SSH 功能** - 支持跳转主机、Warpgate、基于 TOTP 的连接、SOCKS5、主机密钥验证、密码自动填充、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、端口敲击等 +- **Proxmox 集成** - 从您的 Proxmox 实例自动将主机添加到 Termix +- **丰富的 SSH 功能** - 支持跳转主机、Warpgate、基于 TOTP 的连接、SOCKS5、主机密钥验证、密码自动填充、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、端口敲击、终端日志记录等 diff --git a/readme/README-DE.md b/readme/README-DE.md index 72d5215f..5f1aebc4 100644 --- a/readme/README-DE.md +++ b/readme/README-DE.md @@ -94,21 +94,21 @@ Container starten, stoppen, pausieren, entfernen. Container-Statistiken anzeigen **SSH-Host-Manager:** -Speichern, organisieren und verwalten Sie Ihre SSH-Verbindungen mit Tags und Ordnern und speichern Sie einfach wiederverwendbare Anmeldeinformationen mit der Moglichkeit, die Bereitstellung von SSH-Schlusseln zu automatisieren. +Speichern, organisieren und verwalten Sie Ihre SSH-Verbindungen mit Tags und Ordnern (Ordneranpassung und verschachtelte Ordner werden unterstutzt) und speichern Sie einfach wiederverwendbare Anmeldeinformationen mit der Moglichkeit, die Bereitstellung von SSH-Schlusseln zu automatisieren. -**Serverstatistiken:** -CPU-, Arbeitsspeicher- und Festplattenauslastung sowie Netzwerk, Betriebszeit, Systeminformationen, Firewall, Port-Monitor auf den meisten Linux-basierten Servern anzeigen. +**Host-Metriken:** +CPU-, Arbeitsspeicher- und Festplattenauslastung, Netzwerk, Betriebszeit, Systeminformationen, Firewall, Port-Monitor, Log-Viewer, Benutzer/Berechtigungen, Zertifikate und vieles mehr auf den meisten Linux-basierten Servern anzeigen. **Benutzerauthentifizierung:** -Sichere Benutzerverwaltung mit Admin-Kontrollen und OIDC- (mit Zugriffskontrolle) sowie 2FA (TOTP)-Unterstutzung. Aktive Benutzersitzungen uber alle Plattformen anzeigen und Berechtigungen widerrufen. OIDC-/Lokale Konten miteinander verknupfen. +Sichere Benutzerverwaltung mit Admin-Kontrollen und OIDC-/LDAP-/SSO-Unterstutzung (mit Zugriffskontrolle) sowie 2FA (TOTP)-Unterstutzung. Aktive Benutzersitzungen uber alle Plattformen anzeigen und Berechtigungen widerrufen. OIDC-/Lokale Konten miteinander verknupfen. Audit-Protokoll aller Benutzeraktionen anzeigen. @@ -170,7 +170,8 @@ Integrierte Unterstutzung fur ca. 30 Sprachen (verwaltet uber [Crowdin](https:// - **Befehlsverlauf** - Autovervollstandigung und Anzeige zuvor ausgefuhrter SSH-Befehle - **Schnellverbindung** - Verbinden Sie sich mit einem Server, ohne die Verbindungsdaten speichern zu mussen - **Befehlspalette** - Doppeltippen Sie die linke Umschalttaste, um schnell auf SSH-Verbindungen mit Ihrer Tastatur zuzugreifen -- **SSH-Funktionsreich** - Unterstutzt Jump-Hosts, Warpgate, TOTP-basierte Verbindungen, SOCKS5, Host-Key-Verifizierung, automatisches Ausfullen von Passwortern, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, Port Knocking usw. +- **Proxmox-Integration** - Automatisches Hinzufugen von Hosts zu Termix aus Ihrer Proxmox-Instanz +- **SSH-Funktionsreich** - Unterstutzt Jump-Hosts, Warpgate, TOTP-basierte Verbindungen, SOCKS5, Host-Key-Verifizierung, automatisches Ausfullen von Passwortern, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, Port Knocking, Terminal-Protokollierung usw. diff --git a/readme/README-ES.md b/readme/README-ES.md index 052d081f..8095686a 100644 --- a/readme/README-ES.md +++ b/readme/README-ES.md @@ -94,21 +94,21 @@ Inicie, detenga, pause, elimine contenedores. Vea estadisticas de contenedores. **Gestor de Hosts SSH:** -Guarde, organice y gestione sus conexiones SSH con etiquetas y carpetas, y guarde facilmente informacion de inicio de sesion reutilizable con la capacidad de automatizar el despliegue de claves SSH. +Guarde, organice y gestione sus conexiones SSH con etiquetas y carpetas (con personalizacion de carpetas y soporte de carpetas anidadas), y guarde facilmente informacion de inicio de sesion reutilizable con la capacidad de automatizar el despliegue de claves SSH. -**Estadisticas del Servidor:** -Vea el uso de CPU, memoria y disco junto con red, tiempo de actividad, informacion del sistema, firewall, monitor de puertos en la mayoria de los servidores basados en Linux. +**Metricas del Host:** +Vea el uso de CPU, memoria y disco, red, tiempo de actividad, informacion del sistema, firewall, monitor de puertos, visor de registros, usuarios/permisos, certificados y muchos mas en la mayoria de los servidores basados en Linux. **Autenticacion de Usuarios:** -Gestion segura de usuarios con controles de administrador y soporte para OIDC (con control de acceso) y 2FA (TOTP). Vea sesiones activas de usuarios en todas las plataformas y revoque permisos. Vincule sus cuentas OIDC/Locales entre si. +Gestion segura de usuarios con controles de administrador y soporte para OIDC/LDAP/SSO (con control de acceso) y 2FA (TOTP). Vea sesiones activas de usuarios en todas las plataformas y revoque permisos. Vincule sus cuentas OIDC/Locales entre si. Vea el registro de auditoria de las acciones de todos los usuarios. @@ -170,7 +170,8 @@ Soporte integrado para aproximadamente 30 idiomas (gestionado por [Crowdin](http - **Historial de Comandos** - Autocompletado y visualizacion de comandos SSH ejecutados anteriormente - **Conexion Rapida** - Conectese a un servidor sin necesidad de guardar los datos de conexion - **Paleta de Comandos** - Pulse dos veces la tecla Shift izquierda para acceder rapidamente a las conexiones SSH con su teclado -- **SSH Rico en Funciones** - Soporta jump hosts, Warpgate, conexiones basadas en TOTP, SOCKS5, verificacion de clave de host, autocompletado de contrasenas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, etc. +- **Integracion con Proxmox** - Agregue automaticamente hosts a Termix desde su instancia de Proxmox +- **SSH Rico en Funciones** - Soporta jump hosts, Warpgate, conexiones basadas en TOTP, SOCKS5, verificacion de clave de host, autocompletado de contrasenas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registro de terminal, etc. diff --git a/readme/README-FR.md b/readme/README-FR.md index 5c180a17..103df606 100644 --- a/readme/README-FR.md +++ b/readme/README-FR.md @@ -94,21 +94,21 @@ Demarrez, arretez, mettez en pause, supprimez des conteneurs. Consultez les stat **Gestionnaire d'hotes SSH:** -Enregistrez, organisez et gerez vos connexions SSH avec des tags et des dossiers, et sauvegardez facilement les informations de connexion reutilisables tout en automatisant le deploiement des cles SSH. +Enregistrez, organisez et gerez vos connexions SSH avec des tags et des dossiers (personnalisation des dossiers et prise en charge des dossiers imbriques), et sauvegardez facilement les informations de connexion reutilisables tout en automatisant le deploiement des cles SSH. -**Statistiques serveur:** -Visualisez l'utilisation du CPU, de la memoire et du disque ainsi que le reseau, le temps de fonctionnement, les informations systeme, le pare-feu et le moniteur de ports sur la plupart des serveurs Linux. +**Metriques d'hote:** +Visualisez l'utilisation du CPU, de la memoire, du disque, le reseau, le temps de fonctionnement, les informations systeme, le pare-feu, le moniteur de ports, le visualiseur de journaux, les utilisateurs/permissions, les certificats et bien plus encore sur la plupart des serveurs Linux. **Authentification des utilisateurs:** -Gestion securisee des utilisateurs avec controles administrateur et support OIDC (avec controle d'acces) et 2FA (TOTP). Visualisez les sessions utilisateur actives sur toutes les plateformes et revoquez les permissions. Liez vos comptes OIDC/locaux ensemble. +Gestion securisee des utilisateurs avec controles administrateur et support OIDC/LDAP/SSO (avec controle d'acces) et 2FA (TOTP). Visualisez les sessions utilisateur actives sur toutes les plateformes et revoquez les permissions. Liez vos comptes OIDC/locaux ensemble. Consultez le journal d'audit des actions de tous les utilisateurs. @@ -170,7 +170,8 @@ Support integre d'environ 30 langues (gere par [Crowdin](https://docs.termix.sit - **Historique des commandes** - Auto-completion et consultation des commandes SSH precedemment executees - **Connexion rapide** - Connectez-vous a un serveur sans avoir a sauvegarder les donnees de connexion - **Palette de commandes** - Appuyez deux fois sur Shift gauche pour acceder rapidement aux connexions SSH avec votre clavier -- **SSH riche en fonctionnalites** - Support des hotes de rebond, Warpgate, connexions basees sur TOTP, SOCKS5, verification des cles d'hote, remplissage automatique des mots de passe, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, etc. +- **Integration Proxmox** - Ajoutez automatiquement des hotes dans Termix depuis votre instance Proxmox +- **SSH riche en fonctionnalites** - Support des hotes de rebond, Warpgate, connexions basees sur TOTP, SOCKS5, verification des cles d'hote, remplissage automatique des mots de passe, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, journalisation du terminal, etc. diff --git a/readme/README-HI.md b/readme/README-HI.md index a2116bc1..a2b37a7b 100644 --- a/readme/README-HI.md +++ b/readme/README-HI.md @@ -94,21 +94,21 @@ Termix एक ओपन-सोर्स, हमेशा के लिए मु **SSH होस्ट मैनेजर:** -टैग और फ़ोल्डर के साथ अपने SSH कनेक्शन सहेजें, व्यवस्थित करें और प्रबंधित करें, और SSH कुंजियों की तैनाती को स्वचालित करने की क्षमता के साथ पुन: उपयोग योग्य लॉगिन जानकारी आसानी से सहेजें। +टैग और फ़ोल्डर (फ़ोल्डर कस्टमाइज़ेशन और नेस्टेड फ़ोल्डर सपोर्ट के साथ) के साथ अपने SSH कनेक्शन सहेजें, व्यवस्थित करें और प्रबंधित करें, और SSH कुंजियों की तैनाती को स्वचालित करने की क्षमता के साथ पुन: उपयोग योग्य लॉगिन जानकारी आसानी से सहेजें। -**सर्वर आँकड़े:** -अधिकांश Linux आधारित सर्वर पर नेटवर्क, अपटाइम, सिस्टम जानकारी, फ़ायरवॉल, पोर्ट मॉनिटर के साथ CPU, मेमोरी और डिस्क उपयोग देखें। +**होस्ट मेट्रिक्स:** +अधिकांश Linux आधारित सर्वर पर CPU, मेमोरी, डिस्क उपयोग, नेटवर्क, अपटाइम, सिस्टम जानकारी, फ़ायरवॉल, पोर्ट मॉनिटर, लॉग व्यूअर, उपयोगकर्ता/अनुमतियाँ, सर्टिफ़िकेट और भी बहुत कुछ देखें। **उपयोगकर्ता प्रमाणीकरण:** -व्यवस्थापक नियंत्रण और OIDC (एक्सेस कंट्रोल के साथ) और 2FA (TOTP) सपोर्ट के साथ सुरक्षित उपयोगकर्ता प्रबंधन। सभी प्लेटफ़ॉर्म पर सक्रिय उपयोगकर्ता सत्र देखें और अनुमतियाँ रद्द करें। अपने OIDC/स्थानीय खातों को एक साथ जोड़ें। +व्यवस्थापक नियंत्रण और OIDC/LDAP/SSO (एक्सेस कंट्रोल के साथ) और 2FA (TOTP) सपोर्ट के साथ सुरक्षित उपयोगकर्ता प्रबंधन। सभी प्लेटफ़ॉर्म पर सक्रिय उपयोगकर्ता सत्र देखें और अनुमतियाँ रद्द करें। अपने OIDC/स्थानीय खातों को एक साथ जोड़ें। सभी उपयोगकर्ताओं की कार्रवाइयों का ऑडिट लॉग देखें। @@ -170,7 +170,8 @@ Termix एक ओपन-सोर्स, हमेशा के लिए मु - **कमांड इतिहास** - पहले चलाए गए SSH कमांड का ऑटो-कम्प्लीट और दृश्य - **क्विक कनेक्ट** - कनेक्शन डेटा सहेजे बिना सर्वर से कनेक्ट करें - **कमांड पैलेट** - अपने कीबोर्ड से SSH कनेक्शन तक त्वरित पहुँच के लिए बाएँ Shift को दो बार टैप करें -- **SSH सुविधाओं से भरपूर** - जम्प होस्ट, Warpgate, TOTP आधारित कनेक्शन, SOCKS5, होस्ट की वेरिफ़िकेशन, पासवर्ड ऑटोफ़िल, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, पोर्ट नॉकिंग आदि का सपोर्ट +- **Proxmox एकीकरण** - अपने Proxmox इंस्टेंस से Termix में होस्ट स्वचालित रूप से जोड़ें +- **SSH सुविधाओं से भरपूर** - जम्प होस्ट, Warpgate, TOTP आधारित कनेक्शन, SOCKS5, होस्ट की वेरिफ़िकेशन, पासवर्ड ऑटोफ़िल, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, पोर्ट नॉकिंग, टर्मिनल लॉगिंग आदि का सपोर्ट diff --git a/readme/README-IT.md b/readme/README-IT.md index b680563c..1f06fb02 100644 --- a/readme/README-IT.md +++ b/readme/README-IT.md @@ -94,21 +94,21 @@ Avvia, ferma, metti in pausa, rimuovi container. Visualizza le statistiche dei c **Gestore Host SSH:** -Salva, organizza e gestisci le tue connessioni SSH con tag e cartelle, salva facilmente le informazioni di accesso riutilizzabili e automatizza il deployment delle chiavi SSH. +Salva, organizza e gestisci le tue connessioni SSH con tag e cartelle (con personalizzazione delle cartelle e supporto per cartelle annidate), salva facilmente le informazioni di accesso riutilizzabili e automatizza il deployment delle chiavi SSH. -**Statistiche Server:** -Visualizza l'utilizzo di CPU, memoria e disco insieme a rete, uptime, informazioni di sistema, firewall, monitoraggio porte sulla maggior parte dei server basati su Linux. +**Metriche Host:** +Visualizza l'utilizzo di CPU, memoria, disco, rete, uptime, informazioni di sistema, firewall, monitoraggio porte, visualizzatore di log, utenti/permessi, certificati e molto altro sulla maggior parte dei server basati su Linux. **Autenticazione Utente:** -Gestione utenti sicura con controlli amministrativi e supporto OIDC (con controllo degli accessi) e 2FA (TOTP). Visualizza le sessioni utente attive su tutte le piattaforme e revoca i permessi. Collega i tuoi account OIDC/Locali tra loro. +Gestione utenti sicura con controlli amministrativi e supporto OIDC/LDAP/SSO (con controllo degli accessi) e 2FA (TOTP). Visualizza le sessioni utente attive su tutte le piattaforme e revoca i permessi. Collega i tuoi account OIDC/Locali tra loro. Visualizza il log di controllo delle azioni di tutti gli utenti. @@ -170,7 +170,8 @@ Supporto integrato per circa 30 lingue (gestito da [Crowdin](https://docs.termix - **Cronologia Comandi** - Autocompletamento e visualizzazione dei comandi SSH eseguiti in precedenza - **Connessione Rapida** - Connettiti a un server senza dover salvare i dati di connessione - **Palette Comandi** - Premi due volte shift sinistro per accedere rapidamente alle connessioni SSH con la tastiera -- **SSH Ricco di Funzionalita** - Supporta jump host, Warpgate, connessioni basate su TOTP, SOCKS5, verifica chiave host, compilazione automatica password, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, ecc. +- **Integrazione Proxmox** - Aggiungi automaticamente host a Termix dalla tua istanza Proxmox +- **SSH Ricco di Funzionalita** - Supporta jump host, Warpgate, connessioni basate su TOTP, SOCKS5, verifica chiave host, compilazione automatica password, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registrazione terminale, ecc. diff --git a/readme/README-JA.md b/readme/README-JA.md index 1c229c95..e906e88e 100644 --- a/readme/README-JA.md +++ b/readme/README-JA.md @@ -94,21 +94,21 @@ Termixは、オープンソースで永久無料のセルフホスト型オー **SSHホストマネージャー:** -タグやフォルダでSSH接続を保存、整理、管理し、再利用可能なログイン情報を簡単に保存しながらSSHキーのデプロイを自動化できます。 +タグやフォルダ(フォルダのカスタマイズとネストフォルダ対応)でSSH接続を保存、整理、管理し、再利用可能なログイン情報を簡単に保存しながらSSHキーのデプロイを自動化できます。 -**サーバー統計:** -ほとんどのLinuxベースのサーバーで、CPU、メモリ、ディスク使用量、ネットワーク、アップタイム、システム情報、ファイアウォール、ポートモニターを表示できます。 +**ホストメトリクス:** +ほとんどのLinuxベースのサーバーで、CPU、メモリ、ディスク使用量、ネットワーク、アップタイム、システム情報、ファイアウォール、ポートモニター、ログビューア、ユーザー/権限、証明書など、さらに多くの情報を表示できます。 **ユーザー認証:** -管理者コントロールとOIDC(アクセス制御付き)および2FA(TOTP)対応による安全なユーザー管理。すべてのプラットフォームでアクティブなユーザーセッションを表示し、権限を取り消し可能。OIDC/ローカルアカウントの連携が可能です。 +管理者コントロールとOIDC/LDAP/SSO(アクセス制御付き)および2FA(TOTP)対応による安全なユーザー管理。すべてのプラットフォームでアクティブなユーザーセッションを表示し、権限を取り消し可能。OIDC/ローカルアカウントの連携が可能です。すべてのユーザー操作の監査ログを表示できます。 @@ -170,7 +170,8 @@ Termixは、オープンソースで永久無料のセルフホスト型オー - **コマンド履歴** - 過去に実行したSSHコマンドの自動補完と表示が可能です - **クイック接続** - 接続データを保存せずにサーバーに接続できます - **コマンドパレット** - 左Shiftキーを2回押すことで、キーボードからSSH接続に素早くアクセスできます -- **SSH機能充実** - ジャンプホスト、Warpgate、TOTPベースの接続、SOCKS5、ホストキー検証、パスワード自動入力、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、ポート敲き(port knocking)などに対応しています +- **Proxmox統合** - Proxmoxインスタンスからホストを自動的にTermixに追加できます +- **SSH機能充実** - ジャンプホスト、Warpgate、TOTPベースの接続、SOCKS5、ホストキー検証、パスワード自動入力、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、ポート敲き(port knocking)、ターミナルログ記録などに対応しています diff --git a/readme/README-KO.md b/readme/README-KO.md index 4e000bbd..08398e0a 100644 --- a/readme/README-KO.md +++ b/readme/README-KO.md @@ -94,21 +94,21 @@ Termix는 오픈 소스이며 영구 무료인 셀프 호스팅 올인원 서버 **SSH 호스트 관리자:** -태그와 폴더로 SSH 연결을 저장, 정리, 관리하고, 재사용 가능한 로그인 정보를 쉽게 저장하면서 SSH 키 배포를 자동화. +태그와 폴더(폴더 사용자 지정 및 중첩 폴더 지원)로 SSH 연결을 저장, 정리, 관리하고, 재사용 가능한 로그인 정보를 쉽게 저장하면서 SSH 키 배포를 자동화. -**서버 통계:** -대부분의 Linux 기반 서버에서 CPU, 메모리, 디스크 사용량과 함께 네트워크, 업타임, 시스템 정보, 방화벽, 포트 모니터를 표시. +**호스트 메트릭:** +대부분의 Linux 기반 서버에서 CPU, 메모리, 디스크 사용량, 네트워크, 업타임, 시스템 정보, 방화벽, 포트 모니터, 로그 뷰어, 사용자/권한, 인증서 등 다양한 정보를 표시. **사용자 인증:** -관리자 제어와 OIDC(액세스 제어 포함) 및 2FA(TOTP) 지원을 통한 안전한 사용자 관리. 모든 플랫폼에서 활성 사용자 세션을 보고 권한을 취소 가능. OIDC/로컬 계정 연동. +관리자 제어와 OIDC/LDAP/SSO(액세스 제어 포함) 및 2FA(TOTP) 지원을 통한 안전한 사용자 관리. 모든 플랫폼에서 활성 사용자 세션을 보고 권한을 취소 가능. OIDC/로컬 계정 연동. 모든 사용자 작업의 감사 로그 조회. @@ -170,7 +170,8 @@ Termix는 오픈 소스이며 영구 무료인 셀프 호스팅 올인원 서버 - **명령어 기록** - 이전에 실행한 SSH 명령어의 자동 완성 및 조회 - **빠른 연결** - 연결 데이터를 저장하지 않고 서버에 접속 - **명령어 팔레트** - 왼쪽 Shift 키를 두 번 눌러 키보드로 SSH 연결에 빠르게 접근 -- **풍부한 SSH 기능** - 점프 호스트, Warpgate, TOTP 기반 연결, SOCKS5, 호스트 키 검증, 비밀번호 자동 입력, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, 포트 노킹 등 지원 +- **Proxmox 통합** - Proxmox 인스턴스에서 Termix로 호스트를 자동 추가 +- **풍부한 SSH 기능** - 점프 호스트, Warpgate, TOTP 기반 연결, SOCKS5, 호스트 키 검증, 비밀번호 자동 입력, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, 포트 노킹, 터미널 로깅 등 지원 diff --git a/readme/README-PT.md b/readme/README-PT.md index fd100435..99dea580 100644 --- a/readme/README-PT.md +++ b/readme/README-PT.md @@ -94,21 +94,21 @@ Inicie, pare, pause, remova conteineres. Visualize estatisticas de conteineres. **Gerenciador de Hosts SSH:** -Salve, organize e gerencie suas conexoes SSH com tags e pastas, e salve facilmente informacoes de login reutilizaveis com a capacidade de automatizar a implantacao de chaves SSH. +Salve, organize e gerencie suas conexoes SSH com tags e pastas (com personalizacao de pastas e suporte a pastas aninhadas), e salve facilmente informacoes de login reutilizaveis com a capacidade de automatizar a implantacao de chaves SSH. -**Estatisticas do Servidor:** -Visualize o uso de CPU, memoria e disco junto com rede, tempo de atividade, informacoes do sistema, firewall, monitor de portas na maioria dos servidores baseados em Linux. +**Metricas do Host:** +Visualize o uso de CPU, memoria e disco, rede, tempo de atividade, informacoes do sistema, firewall, monitor de portas, visualizador de logs, usuarios/permissoes, certificados e muito mais na maioria dos servidores baseados em Linux. **Autenticacao de Usuarios:** -Gerenciamento seguro de usuarios com controles de administrador e suporte para OIDC (com controle de acesso) e 2FA (TOTP). Visualize sessoes ativas de usuarios em todas as plataformas e revogue permissoes. Vincule suas contas OIDC/Locais entre si. +Gerenciamento seguro de usuarios com controles de administrador e suporte para OIDC/LDAP/SSO (com controle de acesso) e 2FA (TOTP). Visualize sessoes ativas de usuarios em todas as plataformas e revogue permissoes. Vincule suas contas OIDC/Locais entre si. Visualize o log de auditoria de todas as acoes dos usuarios. @@ -170,7 +170,8 @@ Suporte integrado para aproximadamente 30 idiomas (gerenciado pelo [Crowdin](htt - **Historico de Comandos** - Autocompletar e visualizar comandos SSH executados anteriormente - **Conexao Rapida** - Conecte-se a um servidor sem precisar salvar os dados de conexao - **Paleta de Comandos** - Pressione duas vezes a tecla Shift esquerda para acessar rapidamente as conexoes SSH com seu teclado -- **SSH Rico em Funcionalidades** - Suporta jump hosts, Warpgate, conexoes baseadas em TOTP, SOCKS5, verificacao de chave do host, preenchimento automatico de senhas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, etc. +- **Integracao com Proxmox** - Adicione automaticamente hosts ao Termix a partir da sua instancia Proxmox +- **SSH Rico em Funcionalidades** - Suporta jump hosts, Warpgate, conexoes baseadas em TOTP, SOCKS5, verificacao de chave do host, preenchimento automatico de senhas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registro de terminal, etc. diff --git a/readme/README-RU.md b/readme/README-RU.md index e452b73d..61918474 100644 --- a/readme/README-RU.md +++ b/readme/README-RU.md @@ -94,21 +94,21 @@ Termix - это платформа для управления серверам **Менеджер SSH-хостов:** -Сохранение, организация и управление SSH-подключениями с помощью тегов и папок, с возможностью сохранения данных для повторного входа и автоматизации развёртывания SSH-ключей. +Сохранение, организация и управление SSH-подключениями с помощью тегов и папок (с настройкой папок и поддержкой вложенных папок), с возможностью сохранения данных для повторного входа и автоматизации развёртывания SSH-ключей. -**Статистика сервера:** -Просмотр использования CPU, памяти и диска, а также сети, времени работы, информации о системе, файрвола и монитора портов на большинстве серверов на базе Linux. +**Метрики хоста:** +Просмотр использования CPU, памяти и диска, сети, времени работы, информации о системе, файрвола, монитора портов, просмотрщика логов, пользователей/прав доступа, сертификатов и многого другого на большинстве серверов на базе Linux. **Аутентификация пользователей:** -Безопасное управление пользователями с административным контролем и поддержкой OIDC (с контролем доступа) и 2FA (TOTP). Просмотр активных сессий пользователей на всех платформах и отзыв прав доступа. Связывание аккаунтов OIDC/локальных аккаунтов. +Безопасное управление пользователями с административным контролем и поддержкой OIDC/LDAP/SSO (с контролем доступа) и 2FA (TOTP). Просмотр активных сессий пользователей на всех платформах и отзыв прав доступа. Связывание аккаунтов OIDC/локальных аккаунтов. Просмотр журнала аудита действий всех пользователей. @@ -170,7 +170,8 @@ SSH-сессии и вкладки остаются открытыми на вс - **История команд** - Автодополнение и просмотр ранее выполненных SSH-команд - **Быстрое подключение** - Подключение к серверу без необходимости сохранения данных подключения - **Командная палитра** - Двойное нажатие левого Shift для быстрого доступа к SSH-подключениям с клавиатуры -- **Богатый функционал SSH** - Поддержка jump-хостов, Warpgate, подключений на основе TOTP, SOCKS5, верификации ключей хоста, автозаполнения паролей, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking и др. +- **Интеграция с Proxmox** - Автоматическое добавление хостов в Termix из вашего экземпляра Proxmox +- **Богатый функционал SSH** - Поддержка jump-хостов, Warpgate, подключений на основе TOTP, SOCKS5, верификации ключей хоста, автозаполнения паролей, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, логирования терминала и др. diff --git a/readme/README-TR.md b/readme/README-TR.md index 06738a5b..1d838537 100644 --- a/readme/README-TR.md +++ b/readme/README-TR.md @@ -94,21 +94,21 @@ Konteynerleri baslatın, durdurun, duraklatın, kaldirin. Konteyner istatistikle **SSH Ana Bilgisayar Yoneticisi:** -SSH baglantilarinizi etiketler ve klasorlerle kaydedin, duzenleyin ve yonetin; yeniden kullanilabilir giris bilgilerini kolayca kaydedin ve SSH anahtarlarinin dagitimini otomatiklestirin. +SSH baglantilarinizi etiketler ve klasorlerle (klasor ozellestirme ve ic ice klasor destegi ile) kaydedin, duzenleyin ve yonetin; yeniden kullanilabilir giris bilgilerini kolayca kaydedin ve SSH anahtarlarinin dagitimini otomatiklestirin. -**Sunucu Istatistikleri:** -Cogu Linux tabanli sunucularda CPU, bellek ve disk kullanimini ag, calisma suresi, sistem bilgisi, guvenlik duvari, port izleme ile birlikte goruntuleyin. +**Ana Bilgisayar Metrikleri:** +Cogu Linux tabanli sunucularda CPU, bellek, disk kullanimi, ag, calisma suresi, sistem bilgisi, guvenlik duvari, port izleme, gunluk goruntuleyici, kullanicilar/izinler, sertifikalar ve daha fazlasini goruntuleyin. **Kullanici Kimlik Dogrulama:** -Yonetici kontrolleri, OIDC (erisim kontrollu) ve 2FA (TOTP) destegi ile guvenli kullanici yonetimi. Tum platformlardaki aktif kullanici oturumlarini goruntuleyin ve izinleri iptal edin. OIDC/Yerel hesaplarinizi birbirine baglayin. +Yonetici kontrolleri, OIDC/LDAP/SSO (erisim kontrollu) ve 2FA (TOTP) destegi ile guvenli kullanici yonetimi. Tum platformlardaki aktif kullanici oturumlarini goruntuleyin ve izinleri iptal edin. OIDC/Yerel hesaplarinizi birbirine baglayin. Tum kullanicilarin islemlerinin denetim gunlugunu goruntuleyin. @@ -170,7 +170,8 @@ Yaklasik 30 dil icin yerlesik destek ([Crowdin](https://docs.termix.site/transla - **Komut Gecmisi** - Daha once calistirilan SSH komutlarini otomatik tamamlayin ve goruntuleyin - **Hizli Baglanti** - Baglanti verilerini kaydetmeden bir sunucuya baglanin - **Komut Paleti** - Sol shift tusuna iki kez basarak SSH baglantilariniza klavyenizle hizlica erisin -- **SSH Zengin Ozellikler** - Atlama ana bilgisayarlari, Warpgate, TOTP tabanli baglantilar, SOCKS5, ana bilgisayar anahtar dogrulama, otomatik sifre doldurma, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking vb. destekler. +- **Proxmox Entegrasyonu** - Proxmox ornekinizden Termix'e otomatik olarak ana bilgisayar ekleyin +- **SSH Zengin Ozellikler** - Atlama ana bilgisayarlari, Warpgate, TOTP tabanli baglantilar, SOCKS5, ana bilgisayar anahtar dogrulama, otomatik sifre doldurma, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal gunlukleme vb. destekler. diff --git a/readme/README-VI.md b/readme/README-VI.md index e82713ad..09a39b94 100644 --- a/readme/README-VI.md +++ b/readme/README-VI.md @@ -94,21 +94,21 @@ Khoi dong, dung, tam dung, xoa container. Xem thong ke container. Dieu khien con **Trinh Quan Ly May Chu SSH:** -Luu, sap xep va quan ly cac ket noi SSH cua ban voi the va thu muc, de dang luu thong tin dang nhap co the tai su dung dong thoi co the tu dong hoa viec trien khai khoa SSH. +Luu, sap xep va quan ly cac ket noi SSH cua ban voi the va thu muc (ho tro tuy chinh thu muc va thu muc long nhau), de dang luu thong tin dang nhap co the tai su dung dong thoi co the tu dong hoa viec trien khai khoa SSH. -**Thong Ke May Chu:** -Xem muc su dung CPU, bo nho va o dia cung voi mang, thoi gian hoat dong, thong tin he thong, tuong lua, giam sat cong tren hau het cac may chu chay Linux. +**Chi So May Chu:** +Xem muc su dung CPU, bo nho, o dia, mang, thoi gian hoat dong, thong tin he thong, tuong lua, giam sat cong, trinh xem nhat ky, nguoi dung/quyen, chung chi va nhieu hon nua tren hau het cac may chu chay Linux. **Xac Thuc Nguoi Dung:** -Quan ly nguoi dung an toan voi quyen quan tri va ho tro OIDC (co kiem soat truy cap) va 2FA (TOTP). Xem phien hoat dong cua nguoi dung tren tat ca cac nen tang va thu hoi quyen. Lien ket tai khoan OIDC/Noi bo cua ban voi nhau. +Quan ly nguoi dung an toan voi quyen quan tri va ho tro OIDC/LDAP/SSO (co kiem soat truy cap) va 2FA (TOTP). Xem phien hoat dong cua nguoi dung tren tat ca cac nen tang va thu hoi quyen. Lien ket tai khoan OIDC/Noi bo cua ban voi nhau. Xem nhat ky kiem toan cac hanh dong cua tat ca nguoi dung. @@ -170,7 +170,8 @@ Ho tro tich hop khoang 30 ngon ngu (duoc quan ly boi [Crowdin](https://docs.term - **Lich Su Lenh** - Tu dong hoan thanh va xem cac lenh SSH da chay truoc do - **Ket Noi Nhanh** - Ket noi den may chu ma khong can luu du lieu ket noi - **Bang Lenh** - Nhan dup phim shift trai de truy cap nhanh cac ket noi SSH bang ban phim -- **SSH Giau Tinh Nang** - Ho tro jump host, Warpgate, ket noi dua tren TOTP, SOCKS5, xac minh khoa may chu, tu dong dien mat khau, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, v.v. +- **Tich Hop Proxmox** - Tu dong them may chu vao Termix tu instance Proxmox cua ban +- **SSH Giau Tinh Nang** - Ho tro jump host, Warpgate, ket noi dua tren TOTP, SOCKS5, xac minh khoa may chu, tu dong dien mat khau, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, ghi nhat ky terminal, v.v. diff --git a/repo-images/Image 8.png b/repo-images/Image 8.png index d4d1ec387f05375886d35f5bd4dfab2242890923..46d72226a5badf1b98a52c7cf2bbd0db02ade8a0 100644 GIT binary patch literal 546706 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≦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

d*rMTBO7p<;)4iz?(r+3gUEN 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 z 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 AhX4Qo literal 325838 zcmeEuXINBQw(TaU1OMUF)*lq6Y6 zMb0@?kz+xEJz*wy{l(+-CmxYb&b@RX8-*ewU8A$kk!_ z(fHD$a%E3OHXN>^=`$}aO`ko6kr?0gR8e)y*&L!&$5fBoSP_h5P zH+~lMZ)N1ipJ*)RP-oZ>)-4u1I?Mjno51jR(W8MoQSC#J=8Kn*oqboxk`)OFWR^;) zapUJNLG0UeJ?nyON-)XZh6?Jk@?rv+dNt!EV&7m-n@481WFavo?*g(Pxqtk_KC1G? z${$b^E0}RW37zt^1cdsx>!M+mckLDCA7xJUv7d+ZMjTVcnFtB%1{Yqa|6$|&)siVg zCuUT-U1QY6c?l+*l(R{w7A_FhVta&x)WkDx(&nEeC775>V(J^DqJlc_vgJJgu_YiN z>=R&zh1b^SbZe7B8IG!Vd}Zm+uFWRrJxu7nPMhr}e2)a8-yO;9eB}$-i5xtp{ekuU z?GZ9v2=*hkSWF{36(F(12p&S>%GbVvIH!=Q_nlb}8sRr?LI}kCU1t$uV&CHLr{I6o zd4Bga*;4Ya$)7O4!ksjL2PFUx`gVudYd^#Cm*2gWu&z-`G`sjbJCqP2t##t}5B^a@ zW$VixU-AynPAjH{l;=MML4E=kCfa25Q^nF8c^<7>yrqH!&=n68&kztySZ#gg`Q!fo zmpgf|O1QzbDACT77zx>lbEy-PIGx3Z0Au67L*O@O`+Eq$dkH4SN=HAcSS$59VWMp2 z$Ta%$$EkSx`$40p8Sl>8NzTzR&SFbaZ)Sv&;W3SJYsP0zT(%Df5 zTn1d9$> zD&B|g|LH#T?;`b2>(}3lR5l;Mg!Msh!yrvS^YQR*@Mmw!p3#cPY=7I_5IIx6U^rU> zlWk6>pnKka8>&pK|HZ>AS$@QoFYy^iDIMZ~^>(jJePD>i+dT<>c;kIO_g?C-YU z*RC;-h3s0KPEs#)pY<* z)K|u%t*AfW@)V?yA#7ybY`u z{9x`*VVCsbZEI_5`;J!ieC=Y+B(IH@y#rB!H)cuh=?m*8A4LNY<-FZv^P?EytvVh= zB|iBtLDWZ8f{BcDNtXHm%CL-NAMr7d<=m;bNH}`9-1dIVujl95V_7^g#jf9{BM3Y( zrLN3&6Mpe5RZm;OW3DaIb9C4~m<9NY*KAe!pT@_(0A&N=zX8-_b#XW_Sw@xfvbz2v z@kDB+nK4pxHHGsAG)Cx= ziaTA7bFpTP{`7i?L3_0f<&YPgwZ#h+XEJlZ@|e_&hWt@z`1>;IX3KJHa_Nu8s8>=% zaX?<|S=@HGhADc}-x_H+{H_yD| z7CQ~$?D3muM>RIw9+y#a&91q9E8EdtliQ>}eXDy_M`xr-M$q|R>{Auo`ett;lW`;T zM#I*kM+OGPrb;7r_1i*6eT#){0#yz2$>P_;4?^;%_0CQ&Q-8(Ln zV+%7pLgh~-I7%}~tQ6d0UK2dku!^j#$!s)mpH*Lf2gBW~Hb#0In+nM4uQ*QyvIY03 zNYpewxhNOlGKtKK0ExiRYb#UCVV+^A5rsqobhW7WVif{u#TJUris2Pdz;~L6}nP)nzXK^1?f4fe2Pun>6z6) z!TG$Jow%5|!?S)0`r0lErw_K5=`(x|dbJWI`gY_h(Lv|lu3qML9TCNw7FaAZTqDmahe^~%?+sJAn%;U1ulUNP ziB1xTOQz^uLX(8)%7WJ5Q>3*N&FUGbTm(ja`gn|Xybb9+tFGCU7H!boubO>uWL-AM zIp#JU(t#FIQj)ts3b}sDt04X1ts?t_K&*V}vtk8Q3#c3Ai)u9U%+=N_NN?o1-VC`) ziE`D_wcOJ6ugh&y*#UD+A8EJoxqC!B5mb_}NI!kq6ke}=uF3HT74hp$VdGMBUCC^> zUY$M**=$&CV^A`k(uc+F(F|A38g`N6RAnpeaO$o;MB(&QPH|_bC#HrAeKcJYGR$)A z*#obmFSZS-VbYAqYHL-mt(P>$$fKQLTm7vLt&(RCjYl71uJeS7FD)SE;@}c|_s19r zCWeNF?kSIWua=^MJcz}4AndmnxCnBcu;GG6xH((}-#5ktxrX;AlMu^Eczxxa0*L%Iz2ab&GvsMyrk5f8!8ip{ww54cMR4P--~ z{P{<-BKuoayxYE4Z1G}%JMr@)rJBrdB|7XeBjzc#HQO54=~-BV zGfID$bAZ7tf#IUun>SDDU(H%Z^xR7JSjzHp%UO&h{?hb@p=VFkAV5f0>Y(^S} zI~+G^eb3!tAedu6U%txheYn$sj3$1glWpxdNk-z7BHNF|Myz3AUQ-c;^GTjfSGy!> zaM<|-Hx&WI;JXhUQHbKD_X*hegy#}&YZF=syC>+EeQZrdCT-$tf%2lx9M#0WOdj8n zFr3>yTmB3#>1a4WTB|n;>2Jdc%kc3=;Q~o%I2#a&O-&oKZ9T%3A=#IlJi9ZLTQ$9x zKB<(QTR0lEg+-G;!ysGkw=6c&>$%;yAGZ~3is?9-uf`$`w^oBUWQZRi!l~9lG9H-n z%pJYRopjaACBdPUo0gn_&2z=x{_E#}hGd)<47=lsv@R>Bti}=9?@eMtk=SiPdz8Lw zRNASu`xhX+pg5r)UZn>)vK$*+jd|Sg?VN7syMUljgR9Gvlv8CUMv+e_OS%~q4<%#w z(#7Utj5qQ}y?00LTQgL~W{&D9B=|gb(W5Kdmo7UUu9i3N(CdYMsJRH&QzwJ8hW##f;H~Y>FpB!(66=kWFVX(idPC(3Q@X zOvCXBBJl}yUiN4Sug_mju&l4QO0>rarQVve5nmj29go00e6wQ^Cbjdd8gqoP#9Z57 zZ}C7^E~EC5{xHNLrTs?BnHv-7n&;UQu-4V9JHrkzcvr~GrE!qNrP#=n4P6jqt^@gy<-MnXFv=Z9Q5h@}HJMqLBDo0yc@%o0^>^d=@(x~h-z99X(FbUTk#Vro! zIQ}3L_z#?nyPv|0icYHZa!zq-UYUpwHiE6({hVR=r2G{UxMqH+WgN4qMvI6`y1L>a zUkpB{M?Oi*X{Cn7(p8iar0m^(%XcH?W%`zed;LRhPn(Jm2cIj}n}QC)=aqG2>e{gp02hb7hR;I$kD4+7>TJsuH@H-*s1w<*c)Ozt_QXo6=U5hP^ZIQ zxhjqzN(4$s063XO*~+EJv#3cOuF+T@CdrK4@4%VQXJl?qMv_5XPx^ZtJ`8&A402g` z{(>8)g`;vhPZ`@Hjt{iTswKJZjk^r5+7EmPXq;*co^GI#8JZ%kqLkW`5waOUxINw= zvj0NbEc(K0-9LImX2kyY;<9H@NHCGkMMOF7qE+m9>B+Q^aZ^(`hoZ=Jb3{FB5KhLv zhsth4l}`DXe`S!gj%Cz)ljdJQy?^>&vKPL^R-_C1tSbsxR6Q(w{P{WJUseNNA6HanuH6e)oEr1QwBQb#o{UB~ zWYBP8I5K__m69*CyI}uBiRt3FJ1Q~6am;l*nGtoxePafJ4Qku49JI`@nQFh@P9H(zFJBlt>vhcD*m17t&9;m9R{$!lwn3tn@ z#krvbE=6T+N*rfQB9xYqlj zUe$NOiC@Ns_qHaCi0YJ7pxXVY_|1&dX0tpmeIwXJ_m*G4Iuc{N%s8Vsp@cvYE#iL& zpg)uZvkma&sh_H6#<9EiQMP=dd7o2Qpgi?!R`I9L+6{k|!D&n;N3d@fnao~~$$NwX zcmlT-t8XNW84}wLH5W432~tIcvz70Dt+k&HXl|IIwVeLSR`X~>FA=~6%R(h^+b^o+ z+1Fm+cKl51*oHE1HUhdRYX)UKp*QsA`0mq!2P%H=%k;JQO%aXofk!(%2}r-6be z6%*L8kjVpzfM?SuwH}m1~J8lyF}Mp)z^0 z<%o`7hgq@iR5z(O(^&sj|dtoYF@(|;Oq z|7v&QS9XHuoy_0}j|lx*hw7f_;4L#<4$EgPm)wsMx_ksqQnE6W(+jk-T-vQyK0^=< zw)g|{E?qLBn7zqjYneuFo>n2lexf?-6Yjd6NOLk-LVx8O67ZE2szdZVBh_iqL9 zqSJDhZHK>3@@t{g<_7bO#3XV8l(lL!Rv)plfpHnQs0Fstw9$rV*$xMqhik%gZI86Q zX5ohBC`o``4A3$ooh@D9>%4_Hl@9K}H?0LAy>ssq-VAT`jv1*y0_~1{>YB}C6>PNU z03I*i&e5y5B7j(X-jP?V&z^f)`3tf|ejc{JY`8%i!0dI;^kyAl9m{unN9rH7a$OUl z_Sa=6{|A;+{ev3%9DJka2_}}qSQ23_c(Nc|x=~*kX*1Zl-yY`wx8E`Eph9%lkX6HEh}+C{16nOjfgHw2eL)%{^n2xe1ozpT&>^d#wU(d z1$U0~6GuD4GD{-j+b&Bb=0eU(y!&Y^haaYmrWyi;_5~x1)dg0x5#j6$2z!Y}+VQH7 z^~!{Kw;_b>c&(82(v}+l;!|Jp4XN-D^*fvI)6n4B?bU%N0W3!C1K;1hG*YYx_g(!Y zk8BSpVvE7bCfI#hk55def@&9r=^IN?1`$h9=7hkL!os8v7Dlex+a=gxN%wKmZs>~n zsZlxRq^0B8%NC5sHOqMtOi4Y)VQu$Gv>*htzR0N`&1NJe91pUv^190l8pRtI!CHPf)H$yX>w3aJrS>|xcSvNxnkr-r_U~j5tcYI=BJ6$QB zk%ls9z^bYLd2QNB--s4GmaFJtQwc6Kj!!<&MA%b1RRW7nW63)$?Oj0}M39D4tu;Gx z36;6eCnv}7)Omf zJi<&Nafe;8%Nl8kLxf*j*&6%%V>$A}lI)C*12}4(zl%D`U<)$sTjXlyGw6I zCr{AN;k@_C>Q!@b;feww5z@}80({cK6mffKy6k@-I(~uo*Ez;0%-hW2dEwwlkp(9C(7=Y|1edG(Qx}ukAN$FNL-+4PrWtUbRjv4UY^_JUO8Z2fQ+|o4cIMt$=t3#`+ z1BsEA0K>&r$S&WwlQ=Ux2H81eqw!UJJfOsH3^dZEC?vKDV*a|X3Gthp!EEWf9*N^b z9j8+;N3a9OH*7LK5kmf`2xPwPxN~&V{0Axp0K5U*zA5YK{JR!Fgq(xE=@Abk={2yX z_iZ1GE#8kGKTdB9+dn0<_Sp?pMbU+O@O8x9vB!({ea&46vGR%Y(vKo=KRRNbNE!Ly zWXgghXN_8HoMm21LkrTs)HS`2UK;1W&mH~T$%_oO4W4q0Jy<93mQab||lfVvnZ?Vh9+|n(h#?U;YgsH=V1VL?}Yh9}D zwH!}>2W--GXAZ0d9`D!9oGL>=Yaarb<0QOy5n_kMXKz))_ z>TllDA)*_%b`IfgAEdcQ#C}a6j>xatIly}PAAGWj zw=Ofso_$;5H(L@F4rJl^40&r!jRoKgWP$LWs}yaM`Nv z?is5zygqwzBOkWkW*8)oOnBtbb^6i+3O@zdK0RR_3_D#@xLS6Ru^uT?wVhE2XbcCd ztXp%LC2B(PUvXEO2F;qkoxf%L*BGb2T7Aocce3x=eIHx2JCELrlV^hR4fjIu5N#U3 z>Ffh&?e)m~LJL<$1IA}}SZw=hM$||b+vr}Z8}3ks3NR^VSVy!>a1xgigh-Rc1akTC zDa)*G;p_jjR746IztM^|bkQ!R@gzUdQq$RbWIIvk7h)U^t>?-hg#~K1!j<~Rx~0ND z&Pd~q>R`<+T*;5NeBkbY2o`oljs0WV`Xhn#mIc3iEh-+~$$hCVj{L(cT;w*i)|4^N zd42+za2D%>r?#3NSW`no*4VrdirvxOjC080JuI>-_9_<}q0eTA=q66uq2a68@!m9( z%8WJt+hM2BOMLH%W8w_(Wo$3MCtcY#0aG!b96XMl6Ai&|aIvsDwhQgUROT2YZ0@%l zU3n{8K@75?A>ovW(0t3Km9?I;}>WHkD#2aGd>-E<9z(eX2EWv)Ni*jYdkJy(6Q5 z=$cw;1H!s9*6^&Xz{MVe5MfJHL$1uB_Ay4WP;=uJ1D=ZB$l&IJ#fx8jlZ*|KoTwu* zZVBfQGVc~twLX7+)U`dPP{{kB1OYJJ5VIOWPBq5metd++6F^Z$joBJ8Xj zrmQw34FFMW)SnaEX#J~+^VF}#O>f@!rA4#g-c3&zwF!h(R#t{{Y1eo-do8vZw&T1V ztOp4R>vUakt4aR~{Qoo96iJq1UcyC^U! zr$`b;9&e00elb{Y8JRCkTh7Wz3Q9g+iQ2HrX)k^>l-1srzvl$@$zSpe_ct_+*Csy4 zuh@3P30ELVQO#SL8O;MDq=m`*?dJTijLo`|SfUN>wFCA+QB1H(aTVB;^ueMZ>JFza z9OW3)a2mKx?uRQ=3LKc8d};s$UYqI9L)#Bis?Uw{OtcT)W~4%e>E zZ~qW`gn16SO?%R+$;Xxo$ZMW z@d?j#5lc%RO*^S~g^jf$J&~z^wHe6_Czw#!9XQI?l!Xu;o|a}mipIW%%cgB`X%9JO zSw4wWR{r0XXvI@PyQ^ov=j!5>B&Fv?8lKNC>*&2Zb@5h6(JzE7>=Rd$C?fy#RyI(u zNEWy%_4Yfdh^oEH?(feJYvf(l{eN-YzbI#TUCHd|tH$(71vGMpzfEOJozu^WjZ#dx zN07Yk^!QI#+F2A6Ogs(Bb0J%sQ^qOnO#d$ngi0A8G#!)C<7C$Fsh+z|S6z(Mj}EpA zpEXvF1XYz*l2P8}68V}-`bZW?cUZ3c>9RbFtB_tyx~p~hf|@Rk@Te{&D!s~NAHIaL zeNAh*Y&<)3##$Nu`B?;@ZUy*(gXMYu=?3R<2K^nU!TgQKPmyvO_fn+ra-pq1T`u%* zf#5&wiT+(6_-~EuKjpIit&#nwX8Ajo`UfTYzh&ZoH_JZ&%KwjK;)!$+dK|WV?$^i5 zT18LgtM}*P7d3QW@lf7npi;WQUXRb+arJ+?_?m|i%7ep$i736ym|Yg06mIwHissqv zZ_KW@UsY+&$93f!k5)P_Y8V(ro=UwfR*#o~19b4epp&O1hr)(z9wm5fa960oPR(eR zd2DCL$iRF~GhDgmvAq-tvz`OZy6I%>CD#jU-A-`953MgA62_e;u6&EvIhy{iwtFXr z?cxfc3&@C*aEzfpbSXq2=X5o9PTzkBz zpbriPW5e%Gef)uqg6*eWf~G^I1YVtCqEmF790x7&AU%@34kwArMc_^^#W)b^-^c5O z>;Gd3_OoO&I2S!CbCD^y@P}tTs4{i%GQ3=q$jU9 z@9v)~9nIWBR;WG4;5LsIT^`<6FM2}%wrpM%TYvYW% zcAM6Tf6WhXZ`Q#C(`8j}{!P)95=4we5@ct_mi_&AkFOFQ506iSxg9@#&6MGZ^TSpP znX9;Y(OL$glc!(HBe8cRcLt0*Y#i|ts}BLJbP-%KR1vr<*KuPz!?tXOdsuqo#voez zvHkfdZ|(3&6E5NN;8wE@*!PD)Q=>O~Jmbs{cW{1x1QtIEa%gwq`^7v2))P-xflcw| z#)q+V)SdAIm-aYe-tfnL{)Whu_1v~vPxs!wzDCjG&E-O;#qJ^nQYbpI=P7XSfeS!8 zS^ipN_|o5O_(N~}?vpx!&PQIJncmK5+sE2k>Ds+CL93!gO19m}0oQTwO+b?3o70$N z*Rg$kZv!zE)P`r#Od6>n0p5%jK$;ZX0V7hjXRXWRe{_fYS4xiJuwSldWm7v9@w%X^J)QfeZ1BDDZB5pNvC4F+HCD)QqW^%9^bl8Aot!o)oM z-<}XmBuRNTMI*N&&q=!3jT~(1P6ICPnWOC?`Y?2F0B8!M4UgsYT#uZs`{{MrA-$X! z5B&A0pqd(AatocM7?pNA@~h1y`vq6HHJ%5$49bz7Du__OPYn&t97QK|f^I zvxWi620GNZoqq9}Z5XSR5q58VeWOjVIf#~bgB^6Q!y=S{4iFmj$L6|sUOUCH+()^@&N?e{+;RUv>qfrb@ zH%eCH-C=ulxhOPJ0o?Ah z#dyU{8IgxeK+2z;VE^Bm?!Pejd!BD-Xbm(r<)-lZSo5BY#t_l5Z+zHzzNG*e#1&*& zm-lgkDoU%&e)YG&vp#99Rz}0oR>n}aIALk2JQc!>&&&7;kam5z#7!^}K5+WN ze|Dcg^qUkG!c__gHs6i8n{QAv#cOz^Ab_nBN503)7Qf4}M9A!mRshuoO|<^;s0&}Q zJ>VhBXINX! zXZb5BKD9fA%HocXrbF)7&cMEXhyhQ2j=%J$9pqNZC|+5&J#tRkYmd`XUitV4WH

$zm0d*NP6I-n!Wk?VJ2;3d`okLb$9H!LHPd{KKQSV zk#ZXE5h`FJV(f4X7V_cND-i*+W32mnKoyQkMdrwfAxf*38VSvVaNYDb@Qw_q@Kc48+8~!?MiMR>NAAPVx+yO23UzC-N6; z_Y)bRt6_AdZyZkgx(>QWe?;fT{FG4vw#(yK>y|O#X?uRcclhxykU*QGDK#+J)0^x- z(xzQ(X7c6Bg`W+V%lUOK7t6u-P~VSGV>avH5pV-(im>E7Sdy=qpi_63$yoa*Ju+Js z)Y1k{GANqF>>R$f-UdG_Fq$*{44K^T<*{1 zw>NwBTcTqG!@}1TEPl)+Fz=n&-?adLck|14z%Bn$IugSNKqzq80bAtJN89n-xK*S2 zS7+8+dGi~B1#78VWTFol#htXK-*3)$@$PQXCg$hm37kBAE{0r5FceJszh4~#4cOMM z(r4d47Im0kAS5p003*}6K3Zq+dEoCC{BO9Fzx}bF1vNm3h4G0j5G$CeSSf6Nsa0yB zj_;N6X%(<*izTI&c3|~q6 zSXK(^DGE^roAEi`V9IwBr*;V*mr8Kl|A z+#IqgvA}+({-^dUCaeR#*p|5B`55f>QY+ZeD7FyI$!hFa%9|%X-(oaWu@c=8$F8~Bx@3VVK&#>kQS^-~2w_yo$tC%j3(MA2_GI0<|{a3_%#A4vD{^L(IB8&+u47$l;J#MENG`rj!J1Oqtb9YYk0o;Gs+iyYP3njNRv1}k3Ai` z#22r3@vapU0E~k(k0ycm^g;XzY}h(rzP>JsK&VjY?25t6t;UnRAT%n(1XqYT|FOS7# zptjMQ_H_6++w%ztE8L{Fxt`3Y`^|pZZSju&NVPo4$Sy95&!n1UmGYW;}{wjc>UPrWL^ z3rV`H3;>mv)D(~+>!WWE9sv7-bTj{@e7Jek75AZIsEmy4!w@i6!k&a#XM*Wu@0Y!s zqhQdf``i_!F^RQ0BG3Vgb6fauipe&z7wlJf<6eQPWy1w#Ng^KDo`)o@he}K{7C4^_ z&wQGc!R;SotNqvI_sSQm{R?b+s2nCsXG$ic4Lq{!_5D*;n>?SjWv=-YrGqg#QF12w z=VeR*k>mm`HDno=%QZZ|-Jo&wTN3Vg-Xg#H_1(qI$=MK<(>B&HrQO2m(A10Wc_PxZ zV(wC;%A(HlRWN7bGGg$c{2$tO?){9J0Qz(UU_#LelLM^*MgLSHM)LCU0SZ}iu6EQ- z;uMS119X~A$IWsMo9i<8Ox&7$Iz9n-R!df)yllxLcd7hdX?i**n~${zEn&5P3VN;| zKglyTLp=6nFXTlwQ&&~RH_Hu!qPZ`N<^Dtcb0d4(B`$$C9IU;b+bJGeES5gCqCTyB zb4+ReasnTAt4BqcT(i6^?;5D3!sffVp>0c@;287f`LXq`?=keh^@7QO)}>z1Rd$)==|&!6B^PE#$)?U`sC=$=%s22`>D_S5k}+~ z4$xsg13MfZ7x5gwDOPIkhWM48RM7P{Bvvus8p+G}qDY$pP)iF32M4AZ?B&EdVzdZz zN;2n0pr7wd+!7{@cv|5ejh|f2r}u$z8eTb>LSotuT}e`o z1RXV-T%UoiOK{A?@=gwzMR)bEOdc?e|NKx_H=g{PKo|kh$p)YpX6}-~c?C*Xp97Ot zd*?)~+$0YcE_Nu#^1oDxf8|DAN-4dE!p4!In$Az%6cZ+;;F|3FqE12^%?Js+QBh|1 zIathKnz!aY9q$j=tgY3t8ctS^h0jW-;hlPORyXKu*4%@T!OJ8P0D6fLvnnwNUse;0acH1ALxC zngl>k0X|PKpVxK5F<|yZZA)OCVzfXAhaz2bZkdzSxLeGTAU%Hob5pKMz2^J7r_M0Oz()n*PU;+j3F5<*Vili} z<@~O%0lpD&t7S~FGVW!)fG9$Ofi6+oD*&3w!)xnQSB!MUgVo}WFSVywt_dLn0A!OIfAL9#)Q`*4dhycirE3Yq zZ)m3P1k(viuNIqi*<5?(fs4IrHfKpzqx8H+=|u|@OwVP|B-*Ph9G-T_m7kHty~Ud~ z?$*WObNkH2ZJ5crJM#?#MNk5Ee?wymR;uMa%F`e5aIE#)fOWV(|$}HuJpY6M8MPB{MrMH;)r(1_H)c4yZcJ+5n;X|tatK=6 z8OFTDz5-c3{v3Eds_s<1B;_R&FLAXmA+rLCwI?9{+< zY}%KV{@&jC5gY+-7X;|c12A`ve zH<?;s)Thf${Cn>HS^wVFQcN}H2-oR_j-8aoqRb6@vNwb5)cj& zS)@N64zlV14{ADlw$)xIm?(;%lX(ItWPHm`1aK6zyc!LlTONcgJd3~dH*-SywQ^U? zSm9cTKS%BjaI{TX5yjtni9fiDlI_D^**mzc@&>rFLzIX%xblpRye}UqK|VNp0dLhI z!Z&yp-!F))x4-``fGC@j6fTY2m+ks7h=%2xj=f{-`6)=3r96{i$*trgFqBdg6EIT zvm!d-+jbXEfBk5j{oa8U80N%(h4#U1aOMX+I)T_1sD2a4WBO+6>qe`b1-&2M#W$>F z2Z@(ZsCgB0!zvdgFsoR^C({iIQQ6LW$#;J_+UbX#-;ZBa7IF)=@@$Ex!#|4`Q5!8J zFdo$Y8 z^jltq1P|mjWS$XSodp_)(~=h5a7I+m zaKnx0#{(Ji3&q=0Y}g_}8(2c07V4Cx;qwCvrhh+0hp29zS?pKkG1tenOpejH!Lf>{ zsM4#cWqBc!M;lYXU~AfN#nyNpxvHY zi#O~lE!K_ItvrA1)lDJv6wbOnZ*JQ-kNY)$X8(k*V*1s$KkjN)?+C%X!_#5Ku@cBG z6kSk1MjL=!UPH`}c}~HO=RbrCDY9Jty;V8ja3Wh6^6CjF3vsnVNwCJ3UQs^YmXY}M zdA3qc?u0Mf9;PLJMWR9zbZv*X5HsD1Q?HovY>Sl%emDd`+uI!kL2JD0tc1-v!33qx z+W6GBTNEKCZBes_Zj&g%OgEVmzP!rlS0K0+HVAc9p*`PK&t^Fn485y#IUlQsw@H;} z^fqJ{cLvPC8mz;IVSRkRR0GmrZ{@mKGZ>#~zNVIW{s%KK-oiY54+0@OkXemK5cWcV zW&&h;AE(3$ppI4$)1mn8IvHkcHHdcLBRE6^Hhws*Ul9Q#N5S)J88r>I>y6F~>w`0z z{?e#^985D1PFROR!nCR#n`_tGWi%U`(J?(}1I=5|-hBD-1&Zr4Z%x4&Jf13Eb5}Fy zRyUJ&tGVumvC>e=VC^Kgd*6R-*7T?YQ{Jrkwc`5UD!SLuazC7OR_PWpp*28lO{*ni zAFTk^zi_itLJoA{@CSc@)2AZc4q`KmyFbaVPX!Ckp>W@6q#4Kennb5!c3C4RNvn!@ z3mQ-QhIw>7wC{Z=C%W8j>vI?%Bf0MvVZ2B&q+(MuAUaFJ} z8xgccfKS>S9cUGrg7ZBq1nBq-nG`j1ZFh~zKS2dFzVR{w@{f0$We((v>FEUxm}z!K zF7`P_Ca_`yRlF`xfT<_^aRR<8>FTmhWc)_*Z0&NGYLiGN8a#%APET`|Vtb+8!59V!r z^14YlO97$!`t*FN{4OI;nzfSK%0>Xi zYrpD@Y62@rXO`^j;)SJQ3zK8>E}2P82B-6MG;!`$1EO3pDZkVY4ChsCa?x?208zbr!N7F>EmX;!lfINvTLpU)r=-u}4{>3`%{1E&nJEY3OSC9*JNX^) zGaheJNKoAk%~Xj_Dtj4;atdCECby4G&Sb6m1~i)M#iO!RarRa)6{GY+vm2V=!W7^}RnQ4_v0s_j%*i97%VT<=x`^JhQuL1sP)wQkV2w@Zs zUmY%Q106E~!1C{0hpLSaL#=kjJ$G_azoNt!d&c5MC>kv=-({jnVAH}9 zI~`l+h<%kJz7zPp{pIpmS^*^<`Uw4iuCJ47vFFXH_=58u@TnTL$+gXSXUuH5TlV?B z5QeZC^FiI`%VJVh9nziLM86l#hHjPQqB!;^-^Ec+XQmw8`oir#75^eiPP5O^b#=_V z0vQ>adN(xubEwLokO5ET@M75rZX^2(Qcx22@fU8@hGkv&1M6iGl$#YMA6knKg=sle zRmGz=dzt1$410*W2?&U(@CN+h7p4;{ry&_|y5N-*9Hd?JH1LhLPfmgiY;5|zIziuL zeL&kqN|_7h=H{wEUOY@##{1 zXP7XYnUVEKUy+*J7fhb(yPX{!^j?AELP?$@iY204ldbs5n|`i*qB<~i7wwMeXj84( z1sl*SP6JfhWqk9cuqYc?Zdx>fF*V-Z5e&01rc;um}&LK5~Lf0+;@r~>w6uUd$>Q{=g8 zQ?Z=vI(zPT6_uZ?2OHfU3U7@T6ttjNVq9B*v@0CaX495veV0WuG0dPXgTCT3;B#*1e&i_j%%EH5+)nJRLn-{&KzQp(I4*wD^$6x zme_}~$q10k7+l65J`!bb7`qizVmof25G{~sEFp}_NcPatDS0-zlFvhxuUoD>mV_+> z8o@h1p8djhx{k;h@ zVV%ztlLy4l@21==B*{_}(D6|eXSil*bRI@t0@v3oTan;5XPA4Q`I!;YxYBr`ex}xx zBkiI8Wo}mWul?n1fyMMs4<}FW+o{DRh+~Egg*UE1InVQo&Ge5I^Le3&>EB;XxLJfo3p+3U;=Wy~S-g73;Dj#*u}KlOywV4ce$V5)FF3xW3bWNM zc&93Z>~;Q=E&xh_r0jju3=qE6mq;9Rj?+<=JTn>w7NVlIIah5#AnA2(S7KA@WHrV zOAFSfLmAPz>*bTCR|jseLEyZzK*^1@i_~;&{Me#FssTE%pkktZss%H`ZqZuNXINd5u`x zg#27g`TAlAU5I_vZ}d7_qFT(7y*X86HcJ-e{R09Nb9G9+^7!DVeAT~cB5g2URoAQr z9*`dx^c*m*4g-cRdVRXda(o$uOfIg2vz8pqMjIx4Fz~*w60EBJLc(P!vnD6NBV;;z zsixShqyJ`I_La4_A8wVDd0ZYm*UEC$^vwut{7Aa7Oa&+qb{UkxTc6Pnzb7Sm#B;#^cAos)1i2us{=KE<+ih_*8TUKh14|1a zuXVzqwbU?*_usqTbQZlz;MuFx&52FPTf#7kb+$nT85YfkKKuUIq9uCmGOJj_oroL1 z0i7SN)5KJ3<-RNgk0ax;Q=5V_A)@1NwTn$-lN^Ac(PS^8K5cU~(FB|il76_%BI)m} zK0C5b(MN>z_=?320g-#^rI+!pSJqiVuqW4Vjx+rvR@$FSDNQxN;s zJp6cu@B{C+4+h-Yh06#%YPndgycC+(B=qLGRWGsc-`ZGMe`baR=omP6g?YK{X+M6t z{ph@!K!vb!7lM5~;L1$BlDR``^8;^?3H0IX*}^-^!O6~>iZBTQB@YPa#890i$7k5@t+`m2~0b2)1OQ} z%+uo;t&LjPr#J(YM9ckjMhJoY#xB?%`)eEke&r>{ zxz2Zcd%4n`O^&W0d8e8@CVqZX_Jl8N!1==E{x26s$h8KiAD@Au-;hP9c&JD4a9lh( za2bqtF+gj-1Wd8Z8yv>qn0W>W=Hnf zI=iUf3u`otmRf3-d9sRoT~_q$tBWS9%M<<9ta@V%DEScjn;`0oybQO^dn!X7? z96K|T2o)H*GcfxR3O?pw)|sGZF(NN0Y6hi^)y0}&v8U+G4u#m;ByLZYg!1jt2Q`mA zJ>oh0A>_L2sF1to_INV0@y^|23TPY%X|L?Ba%=OfXCsTZd@{?ehDyHoI+5!@TKvlX zzNaXdFX!f)=N_31#w)6y^l_RaBfu zKuQ$Bprk|uq*0^=6hs7+9=ep0F6kH$6@zX8L8XW8E>Vz1x}(?eu{qs6?`i@AzM67sPq?IeZP3nWXd%V0 zKb!D8r0mX;4?87=CiLk5-STtl{BZy*Kr&uN+b zpYq@qut1C6v$*`{0{Uh<%Pvvq-9MAu5p`yRO>FY?L!o+R-d6I^{;0SgXSe?>cUtz{ zAP3dRn%cWS+j52F?=n3Vf7-!u-Y~E$l?%I!?@5bwf`09_7;`8F$iZYe-4!t zQ@JZ8Al6QE-YDs+fcgDy7e>Hhh^TnssMfgUR?f9-@ZtGK2`F)y8nfuo$)4X#7$A6b z1gj(vK-n-f?}~eCD`e!0NDHDD?VaIH`S{Uv$xX3zVQZsy!%(MwvV1kw8lp{Uj-E!7 zUl9}2{om;!wF#l23BR@vwk=(&jZx10dv5t;+jV>K6Af0zK6wE|t!rWH-zlpM!mdPe__tvpNR_~#raJk(JBXZF*j_sZ0Uv0}> zVh|x=8^laCd|LXB&v!S3Z;?`RUSaax{g9(qJ~*Y`(@YL}o2IELmmhsmaN6v-E$X(e zU*?j1>&gB5grc+V5@C5nWEb1^*2eHH6*>s<#t3ts$=D_noIpYw+8BwgA?|iVZM|^a zvpNahcahv90Pd?^|K1HLs4lRPmVP?!VB)37X8yFw+|Jls-v~@g&EHs0b9CyKkSNlS zmvo*Fn&Ioyiqj#o{oPRa^*qjNXc_sGQk6VVREz|q<2E1*I0vF$&(oYF-e{&oe&46# zK=N9bUyJaGPpv*!;F?P3d0fIh3s@i>=iVC}&M0JoPQ)JCpxRZw(rGRguna~o1U-D{ zqvT5Lhw#XHRD?_XaA`W|R+koR1AY*n%!@OQPDhxr4K?6nR4D_n2Q%brs5X6{?h0N` z2nuV2Yl)Ri|k-GcN$q6G@Ewha==zzHc3Bs%b$BQUS8~-a0gy zTD7ZfmTnsSSzus$nqq8;u=yjkk2%OWYI!C3LAKE{Tk)mI-0GT}mLKVF($FwZW6;Ep z2XJ=CD_}lvD8b(?6h+5uT#rnjc?gqA zLk`M~VBMPE_p&nNTAvc0;?AAAuQJp@))9o7Pq3FSudX(u#e|4&ggr_>4s+vg}ROQTaFGl8|TM2De~1_A)^y|GO6|4ky@-MF>e3{ zJprZ)tnO3d56aXpThHz8bW2&B#KJdLpnlP@TWa5@DmS{KW#8~>;e%tcqxW?O0!Xjn z3oUh*-oAYsvblJAX{bCo^W0!EY^yRy|1gDVBhhEE21IDm76!Z3OXcHE*|=N~a9CKs zoZXf1J}6)jH8SMX%HqB@XHvpnfF!K98BtRmh!A&Oy#jc$;S(pTclui##Eg?$ zc^+en`haIKhSeo>X5$2Kj#D+3Z!+uEmxk(mP^m5n*5e^7V#EVq4OFzCh+LJzkIq3d zA>@p-0VI=xOkt$_L0_bo)CuT#!Y zMAK<_v_41>ZUy&aq?rfD>rR(h4ZufjfELiz^K42K(MIk0iiG}9!HIedQZa_lD2cC6 zrO_aIr!SyJ^nr9KD5T>PnY8BGqD=wjUpB65Mqj=$Y}c;+aL%OdP!K(gVOpEZtHy9} zvKpq|D`APE<(cwD1$$$YH*9tM-rmNQoxW$KFbC(=YIR*ipDe!bz&g}jVP!Q-umeNs zbB)Aaiowf*R{*GYDizc8kctG)xYXVX8$vJoF&a6Dh0Q5*%>nZgkV$5@9k7pha8dYM zA`FEV&eGZ-wVce+3E4N?LtkGLKFdoG1rFk4JEs}DT(N>J3d1|UzqP_t;tXwm*RS=l~ z`FGa$+O|%=Eov)oTh!bWiNbIApn+GRQ#`-D^(mAACd?5rmz#+>dZ4J>jac*e@!7E) z`t&EzLeDh&u;wQ8f+pVhWG&M+d@bWm?7eKw+@cb+-I7}fOk}rvTf)r*^v$7S6NA^r z;`IYQfe(f=N_e^FTsWF2S4;!yn>x)O@E!8(DKGv8hK0*dW&?1^bNYjLtM5>g=Mxnb zB9$9;s1o`FI0-!!ReG)xv~r@-I^%wVP^YShi+0Y8)5zBHPdW+ ze%NZ_T-YfUc3H#yXbBR(JE; zmb^?~vCotNp{ZaJk|G$?H)3Piu&^6$1nI%i!2@rSPIIU9p8Z{yoq)@8vM+z&(JcuB zvGJAX z^hH6duaU~9hkU=+?IE*8U%>2Z3Ijfie#&0vRQ;F=iWpVY1}IMzj>-Pk=a-lw@c-yVM<6{p3=#;FnbA|f@@GuK|a2HScxL0n%z3Om-GuuT1&K9 zDThzZVW#&=X1~|L>>)R$U#(^2SUh0*Z3J<#*jmMg8Da0o^8F~r%F118`}Y03?T#<}L`P zpx@#|5LMo4B_r5WGdT|O(l(cnO1QEbh=QFf9uD=>XA91q>9j=vC^mUb1&S=sx?0{j z$u_lTeYMzE;_7+ddybQagwHG{4dk*Za(F3YBwW@%?oGX$w;94rB?wjK%GItv0OgPy zwUoB9RZOt!nXLK#HMD`#*Ru+)!b}JW=!AQ3C~6k%5-9sT5V|=uK?$3F5@TE}yrXHG zv+7ttDJ9vRuK8|kk1B&a1!X(V<4as(aJJ@1uhJ$AV;$eL%}^x+?GBMtU1)@ovx1erD19ej+Q>sb5VNvs9*f;<_kL>fKQ&1 z^*%faA#P}%mxYLcI*zUO=yj$-CGbu?yYQw@pRTZ_KTSby-0t&14={(FXsFDs)UR|p z#|Tn8o#x8Z@`{S>wj_q+%y>p88fL^bBR#Ll7QGbAaX4JD4eT;K#N;(u%vPp}YLN)L zzGU)9%eg1=fzTwwHJ4SmIVPfFNJu;(Qzfrz*Q$QG{(N#Zr{dM21ffZraz==&t@UY0 zb#?5@e!p1N=jdpZ)F>-;%OgVcyl@u6(1dS7r<%&m*Ub&<-!WD>xn~y{n(?E5MLIP&b%Qy@Ng;a(^e{IKDt(TWsE$=CU%V(~Mp# zpSu72l|k%B>2?59wU$YX&s+Nk^ZqVep?&TW#O4)`srXsQ8Nb}_H-J6}hnmg4RNWpJ zGHNB4;ufx5KEq%a!CUSYE}vh+4Pp-e#1s<)DInFCDdm(r{&ra|BCS0}RJwuvsIfVY zPz5;t{)+oBLcjcMb?NyjJ>^}iD)O?Ra7Tu0i<4cR=hqSG!NOzd#D3NxQ`0u<4~B;k z7%KyTjXahw^o@P$wA3Y5UlR6{V}xO&8<`H?lI=hy++vE>hzsIXJmlZYMuXqr|ETrm z??PN8;1=G74+ZUexuDzW6B8rX2Hx4-y`sd@ITw_GI=y|)d5@Run0-*Y(apgLU%#rd z%pM));EA-K+AlP@O$L#Zm3@T_7VD59oOWL5YF(eG_U`}S#eeePBE;3Aa1&ECTJF^~ zW&3t{~#o8<4dc??v+-oz2p}tTsBt!)hI= zq)@)^X8x8GZ+cZ+1~Q!B#`W7c-mOa)2uUD;Kl1d91n`VkT+0w001e8I^zmIi<@Eg% zA+6NI#C#c#iF%M)EsYdsXcmgF&N;>PxX)2RG@+r;xgP-hYpZmoHJln+fAR3)!`>Pl zXL*STPI-BGSMqLm=1UZrpz&W3%)!VpQ>W~@{wYom-uh53dR)o5%z8@Q_BG|1*p;bQ zZe@;I(Zc26loXeP7Ka!5NINl6%AIMrkr&r1hN%XM*BSkxQX=K0E zoyBFg&ptx%rd7sWR;<||HWOWCNgih*yn5+5K{VIYLKvjjER>R6yCi(agmemj1kw-n zg7JFV9<1s}iDhJs@WxbGu6Visa(^C+OyHSFFvxtj4%@vy65j%#HK57S9`v@)47Jz( zP-e#EPgtilawg!?f95z;F%LV1s3V$c=)|TFxaOA{9L}urIiwu&zxzaYl zp1S{uV1cM(g*F@7o(n39z1l1TYx&rUq9S7x_z}{g>WQtc=0zxHJnNO!2XywX!wHbh z)GonPt)1=BnphiAPGRAzbBR-ZQ~GBX`FuA?bGJ8k2Z*mM4U2L>Z%8NAeR!RkB$mwj zHD@%>tLD{(;M1{uGoh9~>LC_iqoBmKZ|!1)H21X8BZXe&2-2?RlQ%M_ZbhhL&Nw9) z`h7ToXKb{)_?xX-u=tJKfH@iGHiRiX3n`oG-+c+zKE#@a9gQOYDpU?USKAT!f>)xm zscCAE@4OBN=*Qhp`a#mW-dcUhc4D~dy89-DPSvisPL)lTY1ZGRoK9M?DKVITdua3bnvzwR#+Q#h>8uN+;d|}(h%ooz5{7b%a^^D zd$S3=`q;`5nM1WFA=$2;)7nfs*#{}O0NSNj(Ir>=9)3}3YtBdqj-S1eZPjQ$p{khu zEGO^aM8><7ZSyWqIxesKkl~;L*r;Zfnyl;Ed#{sd2zK(5han`K0O2Qr*r zGf9?2jb|)z-wTMOaj2!fJUZIx-tH_nQUnc;7I`X8jeDMZiyXPS2D#9anC)!7TD`Cb zxulOE+HWV8Nf@d5H1GHJ_NAG=I#&d%XF00ov_2^RBSLC&&gj2LO-ma|{mS2X2>{0N zV^u9~Iq8BPc3BcoBj;)(7CBi17h4JUW)pS$(OFj}f&6TW;`c6Bp?`7#NIAT!G$<+_ z1pnB!?v)pv1yO9LsHg>GvXv~8!4DwA;_SN2xP$sTm1~0;aN9NKJ4h%XLMd|@WgfH; z%u+_QTK*)+t`s>n16qyOW1%d^w z5#2HO%+1Y{6tgzB7A@|!1Axi2J*G2#W2$5+TDQmJ)3Yrn5k$cfrtZnhkdPQ0E350B z_#EKN>Ac{C!lTR+`$|n(_HL5vqlyfXNG)9at`~Upq?#~i^%|aWwOiwnJAq$k!nYH@ zX~q63RF@$1ZPoU^Yq-BNGRXp3TJVn>pdyXst4FG7<{aGClM$37DV=Z`l1Eq#l)kqC zjf_1XDA_@S{|ViJi38iMvizYAztA%@SII8k;O}Eq9J3uOYX#Raa_?ub;QQkY_9DrS z`I$)ow}L*?Bnp|$if0UKz0Ui4<_0_(2k>Yj`vXSVmm#ZsI_GCV(y0wa=BihRtV$Ia zDZLYmwsi$jGDabqvvq5R{R+?8vsWXidP|#k+7==s9TU8UHCMeb<0k2+Y|oP1!#O8E zeUixStH!^~hVJ#hHk(WcKQ+EN!QN^iJ4+U6o8465L`SYf85tLo`g#Np3#H%fZE!gu zgeLAXK8ZU8DC7u>jZgkupTl}^EO%h!ad^MGJm3)DTE{2wa~5_22cSS!q>7p1GQQ@g z56sf%Z?u=-j{Ob{g*zXN{3m|5(gHc2|2Rlw7VKur2=R>HI6RTLbcbNHB_;~HE+2V{ zS)E$qj?f-9R_E&d*(xzsQ#0wRtmo$;LSN=$n}OT;)b{?je9a#&O6U{?q>KdctuE6~ zP>`JU7ZQBa9^;cS*MInix#&lauKCud@4Xdjs)syFss9(h_66m~xe}J61ou__mpLLO zF*?=vqb`JyL^1?+$LosV8LR92T7r(0{_Nl6Gi(_}bO%+-V!e7+DG7R-_HfKNyTd^NQ+@xQPt z;K??#Z#97S>QzpuyxCb(#d20q1_djTJd3*ZT{VrGHTmF|m>|;F zyV0*(k+YFb>z`Qr+OI%t;)Blo8}lN0mWq3VQ}L2`(@)DxI;j1R&R#VX(T~^UU=t@# z4)0gj!0$>nC|Jm&WPrj}qTaX?R4KPF?F`~i5csW)oVxqF@X}9I>e4VcS9;?Q@f)bV zFTS`%LVCgZncoa2Rrpuw?@s*a8Ws|7LnU*4ic^H9GAex77wVUv;C_XA?O2s=? z(8O|vn=BRrom!Xyt=kY0>|Uh!zp;QzDq#e;=i&MDc}_*~s@;1;|FE}L_Ht?_%LV$g z>-XZE2y<@De|e+3xIesfA0!xd8RD3+AzWqibXV>-!M0zkapUK=Tm1TVa2}o&fLelL z(mN*PUz{mFKOke)z*Z3S;3P!vSqdFEW)z=*&y;cB(GmL>ju(Q@1|Bz|;kpno+Or{c zIA2TxoYk4{ofb>~$|U0`02+YbJ`C7WIFAmJHD+95|4cYJ?O*M#u{4nKImvCJ{{I*F z|Ir18zFe7YjimDGBg18Z0A6@a&BqJ0p32OY$d-k}W}{5?njca8JC{tt4UP-degNrD zpY-b#QvUzb@^c~lfOsKAy6L|=-UPJ`dkkZ?IwsnOaZa%|v{{hTKyL|Rb58vZaD({w z8@eD4emucTzf++7$4}n!BL%*wC-|Z6PhaHE3_J+`p1;n&`l}Q`kHb&KvRsD?=Q#Z= zfu*FkF8*#c`NzXbk{*E31os}}EWPq1XJFBWQBgh|MX)K~kpjnBo2j~qmZgN1JZb&b z*4BBkZx?@>eku-G+ubX6|5wJoE!&WfHx1b2cR>v{>-Z4Ycw|lX;RJnx&d4w4AzkOq z?Vf_iB!~uq=>4b?Rlt81_<#P>Eq{tlFhOeiD&o57SCv<uqnUgW>-$y6{o>7u0QXL%ib?7f`CIj5mR*OU%fQGA$V!yQe@or zwd>voB(Fd@`m62eP9{s}kJ_hu4<%8Omv?;THx&~Ubh`d2m@whu3&4e*+qiPKH=a`uIfY`y4r;p z1ZlWU7}unF{y$&(|9s-sty8n|K;ki)^STw_DKBm^zZG$mrlO$4Pukt)azgG}(xO)d ze7}HtQ_GMDm(M&^1L%*69oHM_f0f+4C)~aQrn_x)XvOC}DxWMx+}xU-8M&na(I)gy z_MPVdSA^zFH2}CKIL04v!2I7+f_l9~ul>{SV(dSm5E5;?dSX9t$t*7pP$j)adE;42 zGEEFwihXijZ-wJ|pg$Pe5iika&uom}fQOPaeT2(n8gRa2IQJp$KmS>}|LIQzxqhI> z!+9ibx~Y<-UHH+|++5+#ooDo7-uKd#-d?s`vQ*N5qTM#%Gk=uU1hZt3pFiJuyVs>L z_U$BOOsUNe_ar^Ij0(wP?*H$eB++jQj13)$FBUf9Xvm`Oo9}UG^Y&&_s0bIHQH{H* zX1*af`Ixu*6%Mzpg&!T<g^lX&4r7P&_#vAcJ1&Y_`wxa7F)X84GJ`g|3 z4)DR$vs_=@si!x3arh++N9gxgIS(w{O1#VScPWp**wQdOy%P<9Uw-|`SAL%U>`(QP z88tkBveOF0)d8z1<rf{b1^w49t59P2^fod3VY@Mg)g$8NnKCNzO|*Jo7_BP9hfWKYH}>m0_JQ zD`d5rndM*X3)oXXQM@r=&{2qClYrE_h>H=45!`+04+cKf!!K_ZdcI_-xNyaDB<|irVIe!6)t7F15<8DLLjZPJH|w5`VCpOG zN$)!^V9~Xt=8^f&-986_K0r0jH!JslT3mkpIwT@)d#)7A z21y*MzgTH>_0;`keHl-|`XlRZcb?%mPGwl+Y|LGBRdDWkt^^_b2tZ7>w(TE6=3AI# zysLIqu(E>Neg@;&g zpaQO8JXPoO5fCjNx3^c0OmbhqZy?QmY+7+6lY&R}`KMx<7lDED*g?uvp9!^IuSQ;- zw_$c874M1sf=}(ql^Vi=EIlF@UxK`G5pVk7L~K9}60ufha@{N%rLYNDrWX(koq($N zZVd{l0IaLgWQpb#K-_c!@>FA*_7;$e+i0S)&Nu$Y!ocigog$4M(%b#DOPfC_)_lW0#dBf zTQMCl*b=$n_u(9*>tvN)U(t3mL#gROEUfhhj~vg&D{ylfn7y4#THn9d8DE;JTMBm? zR{gLu-55|b+ox=X$j^S~th(_oJKl(Otjs=AtkhaudUjwIh4tq1+Pm@<%=3OMMVWrP ztOJgE%;5^Uf7qhi{GNhB(NTqyS(&RRyTHQ<>oI*UgN-`9Y#}(DD7%uKm8})U)Da2B zenb`?(mM~&G^HBVW7LGp@)E^kzmssRM}z2VW@+zn0xUZoRnLhA!4~^XD@wq_>xG+f z$tx&Cn6~6wqt)=Hf3Uu`I7#Rw_`dZn;9&<_^NUi({}(LaHkV4L;~ASFa!CzHoIu=r zpX>tuV~1?>wx0kE)8^uT0V>@&j2CEwy78t@&af*0d0Erl;_kbyRXQ9>aOPRN5#BTyG5R`?n-_C%;4a6%TQHW^92FW4=!`nWuEEL=7p>ARn+=4q z6}j)6rNux$?idOisVWea=U(ZAQ|-L=Y_E&FzS$?mdiboA^Q}29S3r$Bo0M$ktfYOa z0vd0x1qgCN-^IkRSz0dXYHJ&uJb6;BS9MJtjAd14v`BP)FodDWEXOm$$4dE*4oo~Y zYGD>$onsJS5+N56Q7prZ(YB`rL&4Coh)iwsHEg+yb(ttDd&a#fU!R&$7BH}ugRfpa z7{TjkCi{yjp*IiPch9?*hv~^)KJYOHqQyF#C@}a4KUYwtfiS8DO%vBQ*KtAiA8|Fz zprMP&n*uV=@Zxx$R6#v``h^?O@lQ>|@Wi`~z0W}E4?ew7gKJoN%mfe3ef`GLTR#QK zf@a60sF{4c@3r70JAJG2h!Uc?Vm$zlj^ff&0#{Qc^AO~Gst1h39&e!RyK+9T8nVd~ zvbnM9^@x>a=cCPi(dd-}S(c_$%jh3P=pNci7tNX!AMHFJeL5PMD%jCh;Le;{nxeQ& zJ{a?V;tM<2JnOXcK-e)q*kbR>J&abYH;gT;C%H=0E*Xnu4rQll@ z1f&Ii*B~{f?W&gQ)z5aV3=K~a>>I{~nVG?h>{BZ47C8?o$-6w;42~s*Xys^sJJj`P z$>*aSlmc_NSJ*HH%oAFiV0MkPm+5$)^6MW*q-!F9>e%_Ah{sQCd^TTKY<0|GebxKp zkYuNo*QJ*}S#k>0^xs;*Vz{TgcS=E3F`xUI+HJ=IqCGJwNRU%!VR4`pyl@e+K3I}d z<0!{-XzC0PI^t|S^}PK-4Ef`$1B%o!L?Mt(Rwf|K@Vc&9#V{)6avo_VyxjmYEO@lc z`^*}wB)5yK06rVLkh&Gc-=YV^u-Gpwo>naDtM!#Mt5KDghJY)}PtI$Mo zPJ7?|ylp8D7U3rAh;XU()DnSr#74M;kgIIYS8ca(m6|6A+wY}UYS{vc&6hhY$6oVR z13R8LZ1Xl`I3xGHz;UxJgjn$3uBqhyiT(Yp6ve;W&?P{lhu@&UcHQUPZHqs-0N2%C zU*O=hv}DI8YIC^^=J2vnSriWKR6J>bV>P-4?)>3g8O-0Q+|I9~8iH&VxJJ)zt#By@ z(ry(kJbt0IckK&{>BP)~Id-|yPGSSNt*4-0N_YmmItdOW3hGB`Azha1bp(D)o{JAF zSGua{@%FElmtTUcu2sKqC2^=xW>Pbq&nK>3x3HmkiD&Fa>4y--ZnaR2 zfFwSq)RUBpj?Q3gUYAyInS|KaZ4nL?%l5h$SQ2>gsl3tFJC6b4X{k`)|+?6s{t~; z{Jnc&{rwso;0Bq`&CLo5P6{z91^KSaO)RcJd}@VX7se=A`aJyhv0c1G@^kxvjUNy_ zls~rp#&3Z6m+@aGIlc&CDSq!P_(2nII3d>xB>EDmue_^Rp-D&E8_RFaW42bpnnBj! zQ{1|eF!zI1Kmu!LEd~#J)8crPZW#YYApr zIP&ZouPk!kx~%gv&|wfJp=1%_RZqf;Ed*x4$e1V>8W;DfCMFE5QKKvQ=Gl-OI12El zG`-MZKqJC!@@))R6U{lsy#xLQ8msjg!;9>!iZQ!15X{tI?)68X9c=Fl#?H=*u_;PNepBe%0zTKRdViQ*X(ed>h92sFq?L_lc@?h+~0qdCzNv$gc{7RAYN zTCKZvFQsB^Wj$S{K*93$a3@x#QPg00s2XD}RbFOp#16614B9(pT7G^We>mP6DT(8D zc>^Q_Cxd;=qmO+stp3;1bWPQkJlT)6&*t4Xq%_IOUycg*P_>Ic%O~H`tpg_hAq9nx z@~XlO!?nnp;2T-DR?DY+V$&|?MT0e3-dhxnXYw3;!9T(6&sPE+L5;-3cyWj8)} z^(bfBg)5-et`j&5>DuQpK7M8>@bO(S(*Y0%lA4FwfSK}-bXNP^^y%hvbnlL>1_V$2 z*EZCfm42Ac=5lGR#=Cx}f?m|8QA0|cF1yDTOf{N;%me3H-pN>- z00yFgQqui$iO1P+8m%l{C>5WfsZ|%}K@RWJ`t;$)LAM$Jv+PVS=K7P%)f}PVZAO%{pH+~|xBv8<{_)xWzLwD>HY^(iB z;=9`4%@`hg1kfE93E;R_{YC#+*7*KC>97ZI?G^9qPQ?l1p6*r>c$Gi<2Y+8(W?th3 za!Js?I7oOkcdUx@lLK+i-TChy`S~mu>R&H{-G3wg^Vb)Q!J81e@5CMNV_%^8WzrQ5 zHsa@H|G6msH>p6y9oksQaBLx;rT%iJGTS_#KmL5r8URGE=3ZF)FSfAqYflOgKJVF& z{Uw5O;ISKP>vPT=TW5S3T*3k<__*I*x&`_<#EZ@RJjWam3Fogj5y_mq$W3(Y zPuW}mmU{R8i~j=7-z53DCvoL}Bpae&N9Nc3kC$f$M!&WlxH!k-gqEvuATzzt;bU?< zCs8RNSiKp*4AC#xOB(q(Rxw+63q+!giWwzb^>!cV5~XKgP>sZ%?SV`_S5Ka-F5Qq2 zatNcp`9wt${lt;+l=FX?fVuVgk5cl#bUG@iufDY4UmkhKYp@;`FKC5B)R%HQJ3691 zr`|bS&gKIuY$H!#V*kZ{%n5%!tc_;!FCWOvssbd_P8LLno?ql0MTaSw_U-N!o~y}jE^?`V%b)vd25FMJk=yVe~Xt(KL2zpTrXq4*#e0kUG@5U2nPzT_2F~m&q(=Ugv5&X%1d<<%NOCcT1Vs{uYt4>QXN+Df@}Xf&Z{4KU$oh&7M z(bDR!6;^D;wj8)+I=|L#BkRLT{l2rfvg!;?_c{F*FhL!bnwsjK7+bzT;VC|}LnPj9 zyIZKgkTWHoQA#u6q*omBP`_Gu;b!wr%J?2@hJ@<={=Bbb{ac!U9O_F~2uMe1gOK}( z;cMOPOjwl6e5Y6UHX~vk9d^TV{Ep$p{f}DRk7l2+*e3;i8>{f5YUj_Lu}P1QrLfHlDo_9P&B_^sTJspI^S5>Xg=21j9<= z2*i7}U`S5kZnF-L3f=khETAhA18~9Qy~R=MF@=ROF~z8vNDTy+goFVV_t96|f_=o$ zq=Q$z?O1v1N7-ak6kd=Re?7Ji#Sr~wzcs-gJyAY3F@YQH8L6{(xrVH{8$`cOFI``b ze6fY{3`h_gzak6-9AZi>L?oZ@CD_-81In*QqC&EyPujzP7o#gU(HA2aI=ZE7*#QHP z%2dGT?h;yN#Es6zU<+>QNlkp|^V^Y1>~2ccoVv36_r;*t)WP|mSr>?iP+8t-?esIx_Nt(=nA$NS^&q}~>Ov?viu#}dN_@!kV>vr*JtilGUQ*Hy8|TY`v(4$DWk zvBuZXH_hL$G_0-%eA#s-K_{}8JCnGP>n>Y}F54_dYq1~d+qNh`v|$aJQp$j45b=U+ z=Ef;y^2?k8=o-0BgVN5WfUzKQ!(|H@pibKRsVQmi&c0fHwpWE=GkN{a99r7wpU;f* za6m=Ihr$Q7C;ZsI8_a%r)PATV^6Qj5^)WesMy9w~fCw?Q4nLb98lY-H@qmHpMut(d z05jVAHsvV?GQ)U@K$Y&DOqa}JGKu%_*Z5D+7FuhlWteNCL8eER;)EPpIrsLv=uDio zHLGNAiPD0yx!EEz$o}$>%k17!WVHaVTesAbS4!9GdC;<+^U0pi&i7dA-6$y*l`zh& z_9BI>SQsFE=>mG16BZZzm%Y}z2nO~Ix(T5)=)Eq3l83tsW1oT5SyFAERa=%z3mMg4d~Bg%O67ZGwE}H*xyk9={CN?{GWpPuLzbJQ_ z3k{Lk%rAx4tucI`1k#okGC&}CfKgXt;OWMqQ-(eajnGX1I-<0wBN}xn{%^!XObt)S z3g>rEW~_6r!B4DZ+?B{>kX#8IM@j&5%gLWW^hjouW(zJ!-B6J-?P01zJmI%Lx*d-E zj%)8W|7e{!h-aX5-NtIM{d3}iMCfPui^ZGPmizM7{{&MckeHB}2a-}lV>o%KG{>3d z>t@TQ^7rrWRwY}@xG`%Z91MXlPj8p{xX-<~m66v-u?*7UBu4GdCHhYj0Wq$c*v_RK zmJHP5f^xGJXR~opMlp>8bscjpY{ZVrX|Z9q-Rt2GC@QpqWHvw5C{D`LCG@GV*fU?HOKp3`o8Upi7Gx2hKR za^63R6w6%@t94_i#K1G9%?QzL)RF8sN10JJig`Fq_K$_4VgUfTih+`ZB#GGaeF zXBXUYrZL#4S8BtGla37q0-87!T8`28&W|4#J;p$aTrlk`DGWxCMt z4H5IK7-GIrPnrCchLw3e)UyQ}(7l(cC|L@jb~=SaCPAq#TFy1{-20fnJu z8w(k8rkYZu?+2!TMloOX#UyQMsK@Sfy-AGJtg%zd6|&>a*+NT}gR45dW^9fAIw=*kR$74+Ov zQ;R6En;50->1rJx6S(bW;Uh}Pz^|z&lqZpaTFPI-GxzbvZ!m`q^f3O%RRHdw>K|oQ zMN`K~+%GN}696X0(dwdwVBg8}?54$nb>>>Yu#|nD><5qxo!CtL^x2;VQU^3bbW6<; zClYZHpA1#y6|l0Rk$grs%_*9me7+X_FmOwf!0*I-IVBs84)>S9I1Qj@kh(yDPxc$? zVQw;A83ZGd`;&sT`AFijLp8$TzWV)IQR2+{AYgKU$IOZhoBp@ z?rvIS3QNbre1X{JbF(?PmTs82~&HkPj+k!&D~hJlQ-y*Zff9U;Lu zba~*!{PKx4%z{X=Mz@N`4&(mb{ho;tsFH+-D}vx~U_i4b93n&>48sA|M`!otYP&r6CfM zya$Ib%mUAC7VOD{kfB9L&6Jw8&)ZN2@Yvow2JIAF#i0`0oRFj8hT9>e6)vXVekfl- z6dVdf#l?~K=IRNLyerVlHa3|9v)TpmvA%;T7!*+EFB(#HvFIEX9O1++Tlb6s$SUm| zB7Q`nRrIa)Nyv&1Sa9zvqO2pTx#kHuhP7=oxx34(Ry^=e!II5w$(n5-?TF4zE+-$m zC|>1BYlK23mX5(KCx`0xEARNlR}Lz(og;HzX%Bl3F&=WzdR%d zF2XBWNC)#3hZCSARijXQB+u7)dT|!d53vB|bo4uv^MsD+Fd4&C>fTo~UtD zF~^c{gwM<-Mua`N9ezRjB&8CAyc_zSlVT;+ca5u*t!d9VIx41`U2Y86*R|_Z`{;Oe z>v|1N77xY`dEV@rNv<85L;3i|_+llkYxhCbMb9x^_KQ~X_H4I;KfVQ%`_cXmwoQ(y zld>3Zx@;tS2p-6W6k2R$p9KowG1id)$^7;u$QRw8g=Z}2i^$Hg)h!>mxQ$7`Sf?~P zpY{#ckl0lvXa6KPA8?`p!}lHTOeffYHgX`Vs~dpYb5Bvw|D$W}ck2wktK`8;aP<+m zaPPYJFv-|5+_2-#JYl~(p&vB=qe+W6>s)V?pJSL$@_O?4>ArU`V(~BP?sd=K|ZrNQQ5-d{}?9uck z&w>N##=5suCJ#8-&b_^6MkwNmY-@SNL*6Tky zD#45rcQ=Te|L$=KP7RL$#Hj=Q3mdvcfL9I_S2UJ~>UFJ#;5;tN{o!wm&D(GSw8VaN zLN-_3N;WQmf5UeAY zCjcO+?Q3{<+lQt=?qoJYJYyf@Ww{Owl7k3E-T_0nhF)qc%K&F35FPB*~$g~ya=a(f3Y3msu?YO>qC8~J_bu(mVma0 z6-&@y54C}YE5jQY3AEE7x2p<(3!rWF6ilun$;+}Zoo2CUo)(D1O-+bqE!%)|?-aEf z*VT-)wCCebF<{{kv#sZKRYde5+D4BXV6qPpEKimVI0r%#gg0gi%B$2*0L9qO9_bFN ztv8h;Zww0X3mC9vKj{DZ=wY$=u6xE{w9<^VQV17U{bmTqX&{=Jh5cRc!~#|;CvH$)KcWU%cKL}m-s?+ zrvF_*{NY6(@fa@zgt`;?)wix!*CbECUb&*YT(R@;Rml+0ImYbR^rh$Cue5v)}Eo%}v z#4`2cdj7-YlYD^$fhnbZFz9u6LYmUvTx8mV6sS*=U~W@zBk+)jOVYMLjsEn(T-QDF ztKd{YXLP4_jqn7mI#%lSFODy=LFm^Q+8zI~Xdb_IYS(xdbfiItDBRHTOZ0Xlai3t@ z8Mh~4OhsBmlQkadcSf}d{SZ*=V9kXAV$(e{sex_|bvD&9phcDPsm45n+}Cj;?sm_g zTmaSFc+`QPzi!xI9@H-vWEtLkZH7FHU&EV)+8skFgI^&3?TMt$s7n8 zzy|qNJjm7fsOfsrP&N?aV2IUwvGR|8#Z|m=gmJ&sdzNG$CDg_?`GW6Niy|!D#ImMH zHIt)W^;jyP8f?$12jC)zrt*j9VFTuK1{4Ak7qI?!`U1A3Bmq4g;Twm1h>22Em-ZBB zYg?|C|2_$Oec>fp-Q85CZ|WwmK};s@9+)~cP~Iy{>8%OV-1n&Hv{CNCx&vL60W2NB zFI?kb4Wj^SFdEU$X9n8PSojafUyp!8nBO}I&~Z4!X;blm0^)i*oCY_Kt=V7;_y&Zh z+Wat~)_k<^wLWF3=r76K5m8aPqfMvL&e*36Kt6chTS2Pzy* zXeB(O5}mt5N6>Hawx_zKV)^P0ZeBLy1_3dxDuhAI>fzWl1srSKQMXE+NB(gi*8k>3 zcko48e&rNuY6Lagnx|uTCU%zT#+jsQNuvSltqs=0e8^4j&-_NA@(=FS-*w2BtqmJ1 zGUK|o+_)%l3AYLgv&u4QkqJztSRfVD+AR*X2Cx3@(b5sk+*4!!KpS0lcA9x^y(M!GJr zv|&bzNZ!32zjhL-+_qY+eRo4!cA1$dACi-o<*T0$Ze!3TTU%zii`pOy`8$H@#A0kg z{sQ7)$9XoxFy(kP@6eX>uKFN46eGi#P(_3J?Y!3Ce>^eX<(0ps}$Ff zzNxdyL+)^$XD7&Mu=Y5I6~6@&g`!oBK4_sIrH=+(JVc%#MATvw6EM!?j9L}c@&jAp z7k7|W*BhO+T4-y>v+HxRhggP9CXYhF#bG?(t~=8It*QdgUJ1IEjdC~f8(xiY{Q&om zs{@6RsjsLF{|=D$F(%s379F=-IgpSFRz_WVd*-x+A{;fy{M~J z#I*~BzUY1C?3s%QkE2k*9; zj*Y_%z=#V}ffq3LS&r?mxl`q~@qthbi#jBgznXZdC6sBuHA zXW7~k%c1$H_nHeDAI5;RWowNG6P=;7#DQf}ZG4gE)erV?4%-|14tx>tnahIn1LNYZ zHR@`-54H?sIt>Wx0$>IjYZOb*jS;mBe3PSdogV)uizR9bTwM1N9wZToi~1;7E-;&kY88JNGl5NeRvb^x~{F4 z?esEDboaU5B(#_)CxV=tdjR@rS?gYA`Q#|}>+CZRmczoxH4=4`ARTcjQwoLv=znD2 z;^9c|Tx)U{jXv^dLRrVZeV(=uP6!i$m{>ncEK#@TqJUU&QD{H_ z$J)|NrhZ$$m9FB8QQ+}}XtB0S+vG;eh40pE@wD1WdQ|F9#W8hXP@f9KVrnNlGrvqn z#;sLiNpx-fnih1DaE?j7x296Hx;QaOdC8(m)b#=acpY%fGIQY&=(5%g#CaQSnI{uQ zG+8*EySM!0PQIJ{nKvR2+wKWw!%t+?>JnwNUGh^FaV1pNI%R4^hddg+)r2=S#NuGs1UI@&KXGS>Cm`^tK20q4p$U`}hi zU(gMK3hk1aZ0PYnmwJ2A1?0CA2T~M!r4LtuBv-tf=TQ!ouNO=(c3ZTnHgg>xrR|JH7!974`fU(|FcJJ|z2l!tu#zoZA;gvt0xZ3>P zm!gaEyF?+(#k0I%Gjd=BS7;e6OkB0QND4nRmUwa!a95Y=Bkcu&)gb7c@*K{t1E3A^ ztnRR4l0xV&TYpmi3Z2kuP#bb_2Mw+)0K+0ezpVIx5axsS1cC@a-Pd^~j~Q=c@@|wR z3m;#cmJVb?f6(!K5EPT?*60TlXC$(n(uRHR{dH@8ujwn!ZKsdpco9vy%mN_z>LdMg_|I^T!0Eg%8;RH7#YN;hUy^S7xyc7%4^e4k&psVIBvzIQ zoIqi9H5Pn&0tFIkkPzsQPzAl)xi0^`-5$|IKg$IY=?~x;9wH&Oe>djv|DBBqXGBfI z02IYonXxp#?%cTAAC*2D%=PegKqdqzR1X#2B6)~S5^t}e0B-RIDc;8ea*O}gKT1`kiu;U(;rPch$)^F|32+i zC0ZKz$dw*eG0M*owz{qO55$GosT>jy%cAc$^I=X2UlR;;sm>U#JI&5scj)72lzYi! zS9aYY-Si9l;z&H=WJ1|$FFCYaFNg6o0bmDILN9Em5Wa^1wH2ax9$yV&Hf`r3F5kRk zg7*Ng*kMKTe+=s2-4L;|0OX1Q*)mx@_w`8q8xolxQ2(84s$#0K?{_sdu}<%S0kj;p zdm4px$_D4YUZ<={xE=T5cATI-;5vR43MeBWEwiH_zHy2r?mqywKJM#S0Ziige*s&6 zKAaAR7^S!1Zcn4M{2p~0FcLC*UOrMtHCEP{d))CJ*xw;}IIt_3)BgeQ{R3c4UX+AozmgeX*zQi2 z^A%pfiB+!?{#`MI|)-c7ijuOi9d*5RtyRlH%-y68BS-5!I+V$^=(-k7FPTz*+{ zC2jTER5WJ4oSfV@oO=3Qaj~z;qU|~6-PazJ2-;6K`zBzfT>ANxmhHd3JxgAdlEQoc z=ReoV6qJMZ9IB22(=%__8_I|G$e-Ym5o!u=~|#C0^nUT=0FE^9{6e)I7Gx=S*G(*P;+ zhZQuAQYS~jumuT(`HN^p;HE5>G@YTa5A#Z*e*E+Ng7ewO z{E7q#{FqVUB*a60c%KfC!qrPTcf{*Y0Ko6<4@RoKPl1H8l%ka2*VCEqj~qm%slH-$ zkq{nD2zv(=!3ewaG?uKL%r7$el{P)mEGM~Eqt4lWChl!A>8dXc?@bCO5sqL@4FACT zYWilm4whhGxK7y!yMHaJV(zp6a?)mxFr&UqZ9nVZzkJgAy-~W_+$n#5f_RMSqp(p8 z1w8oN9jjnuqw@B8*pAd~+2!{*nF_M1=Op2px@=8{D=0^iNg}#x`{B`lrA#QROHr{N z{U$hgKS)EwE;`0(VZ2RxFS(N<=={Nm%wAU0_7w8VTK#8*g@pw_tl5<<)raMN#ms2t zS-o5M*`&GFV1jb!$@|s(?mCJ20RUuWPxhB;=a{Y3Se>~m?XPP{bg()XIg93Fp#u14 z5xS?_XHi9f3m>(^FuWW_vu---{OPmOXM#v-JI4OUb z=9YtquMh0yWPQN}cKk06`fbPsu{qE2y>s{}@G+OObxz}-Gfl+DI090j?lw&9F>ar} zl>feus!PJ>7`LBz=hXofGa`>I#VK2J$FcrADV33?sF=DBZELc@SG5zCX1`Zhck0_# zFj7N(cH`|-_ig=qIM=tAuSlo2-K~L_qFb*BM~|l6KLEmIVw;DSH(vI~K=`?0`0>k80wRw6vorgo zH6~#hD2^@vieoaTi6BK~B7u_KzS*tDjO(uvp!62kcSeYM1c@Ac^&X!bh2Ls=FdkUK zr~WY?bS5ZtI`0TGFcM5+TK!=9bnWKG+HmLV#!ok1p{$rEdQzyRduuDNCGk&+u;!L* zWRB6sr%f%4F&PnbzdjWe2s7%ug};fSTL-Wj-gR_>3HPXDz8om0_L(2{UA+a!>!&*d}9ZVKe#KUF14g(J*s$n=HZbJ0rK>J z;Ab%7kdTu82nsY?UPlT~2LoWT1}1XeeJ=^R_NfE*f;~Fj`;~!WejB| zOAN0JJD=vAkZ<7&5q9=%P1V-EWYKeuQ@t;I@Sc;EeVGTr1@kWFl8rV;fb)ehu(3kF|~n=@@~Eb3UzKCAFK#u)?*V50Er4ALlps*E(CC_RM0<4f{?Kw1BvnGhjL zR4|vO8sKSVQ{wF(VIIkgY}`1;5yIsE404+N*q@$#R)*S;8R5nodR?_kaa|M5iD>rA zpjzRwvDj`sH*{;IyML1fV9u`>7VhuYqH$Q6qw)(6Pnt?CQ4U+p>@sKwX9ZZBp;y9h)kra*w8)oQ59loX)q$iO0pyP35Fci?^rqx6M7FXo)^<1 z`|4FQL2BiScbBUvkzJhQ*v2U3C)CI`7devQ8xe9SZhC(+L8-jB_Q->d-+}|s+l%BT zFsiweeUTy481F-Hb*RJ0 z3Ii7Z4P+mw=ztac$p6Ui$hqSwm4OW2fgB(LXC)U;sJorq{q;*K;;d@?QE6&Z*}ciR zh)8a>8pMopBQ65!zKp`OEnT2X&1$K1cIJ09ZwwMV?-K@{7st;?_|gX&xAfKBbKmGO zSZe(Ba1!qN3ZlSM!XfC77o~d&kZp&IR^dKX!~Yc`{kytle1r=+8n@Z@;#1wZj-_Ri z9BD^cf!NAWgKxxXR@PgC{e||aUqXI)n)T-JaSa1QnA(5~*yd3|2?SF-A)i$t3j#`9!@jd2>7{qEa8Xy7l?C9%nxmmAa@}L~)(Q`;(7C=lk zS_iV)tnQmDhCNa@Q34jN8v79M{2a*?1N@%i@0HiiUX?#GFv8obS8=coa{*C4c%=(5 zkweHj%%f&)+cmj!vkJr!MLC2To}`woIKmy0`p9Fj7Qi}xJ^ zE;|{qTQSWEJl{I>0wo+$bZ;~f@Bh6jC4w?X2Mm;iE=~ZaItD2He3$3DEv+Ti1U~>L zK+QJCqMHq}n^zFO52rO(gD zFHa5K-p;=EdRXSiWM>)moqWpy2lDkxkxfYm0lS5`sR;&zxC!RIo|y9^G$0+h#B*3Cc|#ZKErG! zW756B`bNz@l;HaV2at@@3Pj0tlmmUrw}~fz9tjWWozoD*Pf&q>XF|XVJ-y6Bz5B`F zK|etPF-4$rw*4_G%^zFQfjpxjh(XyIXdb7+TSx~$@gosav90BKy1!l$dFM2FsDii# zg>NI#{$7NAXLl+G)0vRM*Tt3uKYV`CcxZ|1xG+*hBI@CUwe4NhUBF*kik-EUsHZuD z_duVtVQ1It^YIC7L!!Uyf0|ZXe^fZ(S{q8&eZt#`va37S`t8RHNAX_FDga!uM4(ss zpKHEdG4YW2U9+<*m%WDSAQr#-WFPZ@DkOT5oSaASQ#q3T+r}%+i?r?^{AW;rb3%`% z*o&_@$4CX27678e{QC)XsEcDup3_hn{^ztegl~}u7p$}S0)BZ9X|N*YJ;C_21H^xyc-T<-GZ2g_ z4C|c7-%y2rH^%?}-i)|sTK1wmcwOXQ3bfCJMXNuu#BprpaO;ab!?X2hQC zf542uLg)gm{#x-%%Hu$E-2eV#k%@HvMez%~y!s0v3Iyd^R7vP}s#HIE({&#!rD|!C z(O?JrT;|EO>s#HZ*pyl~lOPVia?^YbH?>IY+7^XiUl=IqHnVc%KcLb?_GU-98>y7$ zxyLJ0{<6!9jz;p#ZlozV;xs!@?%`~)u{xt)(|qRt+Pn578S@%{9{nr<_RNp4$$rndaZU7%-)yPW&ob}DZFE-IP_x7+m zp#=E&&`No#uBpBX>_^+<6)Mi7%(B=rqAkSmZ2#|gZQJyd|Ih7NsxLM*2s`jXTNGdI%sk4~gcQogvYH0`o~IG`E(!Kv`zlTC z=!d6|u@2FdeQ!~n6CeLoM0ZdbKY*d%Dd|gC*x|)6o-S+ypBbMszDJj9xte}`>q+!j zGnvR^G0+p4o(3sXKyr7E2|I~I*O$E>FAeCahK0pDAXIg z`9dET-kD)N99(kZC!k$ty0T5{AQv1enr~?Nj2*E&iuyJh9W?|1Dgt(<(U$H=~4Nud*4y^(i}8O?9kR!`a1Np8$bOQwG$ zb{WNtu7b=~=v?pBmfMQuL!K|#*KqyzKlo|#bb==ZBlTD5kpj$L4%kB(S;^Pb!Xe?~ zitlcmaK!48SM=WG>^EPns{4r^I$gi?*S|;p`@vgfFNBW^qv(ZFdx$*RNQ<^6=eQFT z227`uo^7 z)~AOWu@A~9>o@qS4yHQQ*sp5V5DXSMuj1&dbet6N7po3gtnZ&tv+DL*=U!yE7jZa* zm9C@-3E9Ubj5lG=SALSiU#+XZS8I~V7fsLw3ugNs=Eg{YJdk9)kEzL3O)5e+i&n|& zS7Eu*2eks;U5^aXG%oybKcU&1VBqXt3P0=N+7-f56Or>BWwk{HWG-`Gn3_!+xbN)A+Y_b~G~FJ+Z(Bgsi6!k{E9XA6MDBLA{b^(@OC8>e ze%fxk%8{Y4Xy>Y2EaOT0fg3?tuFlfFq8zalACwP8)4gojov6HOMEc@Q02RmnOgx{$ zK%lcVhy7{+{BeDNFZjKFeaPAnI0RTRODQQ%&Q`i2!ZbBkhw6cM-u97O*&Evc+u5o9 z@4mipX3AMsCUFJ@yLUhU2r7&+DIk`4G?2PGMYAbcEdDdyRJtRN{Kgw71uI6GLgmqo zni=JSqp(ub$;MQk>|`I;I*MLaE{#L)%WG*(&M=-C^XS${{#KY(6+_Gd@#TF&j#}wbel|npskRt210qi~*TTH4wt!8t@SHQc=Zti)k(3jP z-=R{|6|E&mSO$&40LETtDFcJp)2B~Aad%khGEMcz<>m(0i&_N1Ff|NaOQ5nAD{F9U z_2lJD{oz{f>_!-2P?XS#rzu5(bdYP%s1DCdhaSV!D|0H{B3N3nKG5S8QQs3s>@jLv ztfI(vjIZcw>-c3lm;x(IqpFyobRt1BJ>Q9 z&i&JyjA3*mqG8|eln2rIf;D1A31tkgG>^??oX7)n_j;p3uUqC_%YVLKI@gf<1(yeI zRV@PA^Hc}GN*HNV;=yUa=Sc!xQC?CsPL0RD%9la0*vZyBrcaXnr8@V7>8!on`HA!6Uuc=_YL1Q<(hMamyL9yQ zw>TZ*0TnVEH^>y3i#)pblwnM;9~0R+)P#9X44kJ@o8lX#UOBm#7uZs1)<0ny@Tw-l z?^;9Z=gUqj78QXu1sV!+jFuXmR+R$-ZO&fWUm|{66~nT{))nvU`0+KfO5B0`B`@u3 zpO4ZXWh}g#E2EMvYH%t1`0hk2mlQI}3RZ#vH7cL#Dos;b-1#>pxM;-uCu%IP$?&8f z`P&14MGran`;@83YA2_3!-zp7SdG00dUY%JYvM}Kwzem;NoQSq3ryc>yWalZ&)?^w z31RAiGs$~B7T=~`33W$x7{%O)Mn8?-kD?=YSsZV>VDs(35qpwC`~+F^H~yg;9e6wv zAjJpnT}GOMue`+E_M-~VtYd2z6$?$8ubkoeVC_0G{puw@(|+})fi~*}2CqFO<=kzw z$|+>pF<+p?!Aj-h9Wb|;imQv) zFO&@q(RyUeW{1~h^Q0##>hR)ulc7egV!nyp^o~}z+C9~s`-ptg`ZiuM7gz_sll`2; zD3UeMir;ge3werld1g@}kBUd`l;PEW5~EXAUusKNWsMi+k`z$wKUpr>Br$M<-o!Jo zx$i71@91~prZ{7jao@R_ckj%KEK**P+lR8xe;24Kgm7g-5rX+k!{+Ey<8c7*qtUA$WyR6ft=SHWfKGg18);4(gv+!@4qRi zf9=`EwA%KI*mUt1ZE~~eeL<4a^H|(v#}Uc|K|3!?dqJ&4&2PXLi3}MKG?c?0nOCOV zbqYP#LEy&gWfADjF{!I}4K}~pe3Y2J>X|0%EU(VSUrhSDqqECWA0jGh^?LVp*Wbb) zJ#Oz5RZIdmK7$`XQf)@ia8^2do}Ve`@#yq{ zJcAkU;TY}c+LA_$S>r2b(Pi)JPTh+fjM7fWYm9H!u%}>bk7b4miAQ&A!0TUAiEGnid(LKL zwK?J#8s}UuZKUXdXx6XN&7$d72h_5pAG}1fv+{>7_#1Ay;p3|&xADSSdp*#N`qptD z4jyaM&Knt)Rk+uLee&#u*KS)YRwh=)E`0piE*cc_CIX;F0UQaPv&yUPx_8yn0rA}z0qQ8>9KTD9{_Gp2>4*LR{QD3Lyyk2SmJ1FhPY5MV z+I&-dcZ#}{J~;Q=r2QmY8IYQmAmU!-OEeoViQEbAh*C+{SC!ZFTQhoN8_My5pP_AN z9gtuhbo^_ZH{Q&hh-q~u8TTPQLt4YvCS|myvY)60TRr1`Jp>OAi`(b$jzsq*ks*F* zj_B_1GO5+%xL$WCXyV_jd5Y{~9pMTqivD@DmYl1>m_-$_XFWJ9>2$R8S;?*@8Au#T zEls|EY^;g?_PB@6Kr5+DSr&t3qcl}$v-h>Mi+m+_J6E-&f4krRXZqy1&bEDRVa~nI zOfzG!6l4{IQt@t9$q)iH$MiWiFZ@un*7|jgpns-%lP9NA{esNKak}DX>#Mn5j=L?6 zPLxNt^LgAYD;B)^sTkNPXm;&GRi((~M0Cm$43IGXOs-%1`TEp_#o~k;a#47vH}u0y_(O(XZ0H>C9(pYT18rB3aXbL zq&lVNj+u&Hsr|xJsmr|kU_LyAz&o|TsxQ*VrV8pEUne*CXTuwe`zvn6ml8!1Khd+w zNQ36j7NX2Xro#+pO;~YL({P`aHK0FCw&mT00=X zM^#<)qOiGr^;PID2hv#-ofxvAI_5TB6L(ESJ59&4r@c6A(Z_}ep|LOn-vWIn;uxXt zQ51#NQ{_^g$8LTsw@U<#E~CswK1a4M-o2He(wd8PF=v^^+Kafrr;dt36oIAI2(UVytUy^q126^~M?{ z^uJ`>ulcgtlJT5$faR?AX}U`mudZZIR6nQT9eOQ7sV?%8f#m0%AuI60rayH-qVTcE zs92dgq1FM^W9&c4&&2hNG83)Q*+0AsFRnMTblMBCHwI zmG+FDbzCW?(7sEm#!>4WUdz#g7zwUF38I8HQijqt0>IM@^;+WUGN!8ZTKZbvuX4-! zNl87ftCfNqbp2gAyItKw^cv^Sh~7Z0NBS2i{yy5qGmQILk#bW1*GW=TX=}=xEV)|?5%#ME*Dn|~ zanW>q$hcn`>!R|^A0R_bb@n9BS+y)QcCq$R1ZR;B{i#Ucle6mUxfG6{$TGNIq%Eu@ z4KcyFq2(M)%O=c$ONCHBVXt)Cbzk=lSrX&|EW*AmM0odI-j@gAF%qXxD~TQ>7@(ZV zzcC)cRIr-bkZ#w$KYxm09*dgr>_%{!uDyj%)rOTHIj=3bbLa(X38U5*+qLV=b8}m` z5-@E5PQ~IHH>(f=NuSd^=ah}`q>5hfcgI84(I0TA@bfcakHsgA*)|{PcT`E z_>Be~__N2eeHP*n)GM+;RvR8W*Un~W3XC0?sLOh#@1j10ZW8c7iQqyeeu-dR9>MLC z?k?;>7?*eJ-{ld3!VsU|_WE&^8yw5gBW&}Oy&{G4V^ zGV1Z3hG-HG_1t`ML{olEPW15>&lcmB-+d6dRp(sxrn5XR)L7g*9SF#iy)M)~D61@8 zo)^`=(CCzeMyGq=BUGB?qr;E%j9*H}sC*}+eXiD&3jsuzsC!SgL78w8X0BCXc z+_$ZXDy{a+&Vz>)Ag#&{>qa;-c`}C@5Ur>1?+B-N*f;^|pe6AqHlbWH>FF zgkCni`HAdXlIra%TN_M@8G5(OOM!e?leN_E hsoN?(>*;U09jmya7A|ZYgy=sxw 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 z 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�PT3YA2ocIdM7A9h#a}2OuED=Hax>S!T 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 z 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

Cv-S5 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 Q1b&SFFf%0VbNuT+0HPSg=l}o! diff --git a/scripts/crowdin-pretranslate.cjs b/scripts/crowdin-pretranslate.cjs new file mode 100644 index 00000000..822f5adf --- /dev/null +++ b/scripts/crowdin-pretranslate.cjs @@ -0,0 +1,121 @@ +const API = "https://api.crowdin.com/api/v2"; +const ENGINE_ID = 649248; // Google Translate MT engine +const PROJECT_NAME = "termix-ssh"; +const SOURCE_FILE = "en.json"; + +function token() { + const t = process.env.CROWDIN_API_KEY; + if (!t) throw new Error("CROWDIN_API_KEY is not set"); + return t; +} + +async function request(method, pathname, body) { + const res = await fetch(`${API}${pathname}`, { + method, + headers: { + Authorization: `Bearer ${token()}`, + "Content-Type": "application/json", + }, + body: body ? JSON.stringify(body) : undefined, + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`${method} ${pathname} -> ${res.status}: ${text}`); + } + return res.json(); +} + +async function paged(pathname) { + const items = []; + let offset = 0; + const limit = 500; + for (;;) { + const sep = pathname.includes("?") ? "&" : "?"; + const page = await request( + "GET", + `${pathname}${sep}limit=${limit}&offset=${offset}`, + ); + const batch = (page.data || []).map((row) => row.data); + items.push(...batch); + if (batch.length < limit) break; + offset += limit; + } + return items; +} + +async function resolveProjectId() { + const projects = await paged("/projects"); + const match = projects.find( + (p) => p.identifier === PROJECT_NAME || p.name === PROJECT_NAME, + ); + if (!match) throw new Error(`project "${PROJECT_NAME}" not found`); + return { id: match.id, targetLanguageIds: match.targetLanguageIds || [] }; +} + +async function resolveFileId(projectId) { + const files = await paged(`/projects/${projectId}/files`); + const match = files.find( + (f) => f.name === SOURCE_FILE || f.path === `/${SOURCE_FILE}`, + ); + if (!match) + throw new Error(`source file "${SOURCE_FILE}" not found in project`); + return match.id; +} + +async function pollPreTranslation(projectId, preTranslationId) { + for (let i = 0; i < 120; i++) { + const { data } = await request( + "GET", + `/projects/${projectId}/pre-translations/${preTranslationId}`, + ); + if (data.status === "finished") return data; + if (data.status === "failed" || data.status === "canceled") { + throw new Error( + `pre-translation ${data.status} (progress ${data.progress}%)`, + ); + } + await new Promise((r) => setTimeout(r, 5000)); + } + throw new Error("pre-translation timed out after 10 minutes"); +} + +async function main() { + const { id: projectId, targetLanguageIds } = await resolveProjectId(); + if (targetLanguageIds.length === 0) { + throw new Error("project has no target languages configured"); + } + + const fileId = await resolveFileId(projectId); + + console.log( + `Pre-translating project ${projectId}, file ${fileId}, ${targetLanguageIds.length} languages via MT engine ${ENGINE_ID}`, + ); + + const { data } = await request( + "POST", + `/projects/${projectId}/pre-translations`, + { + languageIds: targetLanguageIds, + fileIds: [fileId], + method: "mt", + engineId: ENGINE_ID, + scope: "untranslated", + }, + ); + + const result = await pollPreTranslation(projectId, data.identifier); + console.log(`Pre-translation finished (${result.progress}%)`); +} + +module.exports = { + resolveProjectId, + resolveFileId, + pollPreTranslation, +}; + +if (require.main === module) { + main().catch((err) => { + console.error(`crowdin-pretranslate: ${err.message}`); + process.exit(1); + }); +} diff --git a/scripts/latest-dev-branch.cjs b/scripts/latest-dev-branch.cjs new file mode 100644 index 00000000..b29305ce --- /dev/null +++ b/scripts/latest-dev-branch.cjs @@ -0,0 +1,43 @@ +const { parseDevBranch } = require("./parse-dev-branch.cjs"); + +function compareSemver(a, b) { + const pa = a.split(".").map(Number); + const pb = b.split(".").map(Number); + for (let i = 0; i < 3; i++) { + if (pa[i] !== pb[i]) return pa[i] - pb[i]; + } + return 0; +} + +function latestDevBranch(refs) { + const versions = []; + for (const ref of refs) { + const name = ref.replace(/^refs\/heads\//, "").trim(); + if (!name) continue; + try { + versions.push({ name, version: parseDevBranch(name) }); + } catch { + // not a dev-X.Y.Z branch, skip it + } + } + + if (versions.length === 0) { + throw new Error("no dev-X.Y.Z branches found"); + } + + versions.sort((a, b) => compareSemver(a.version, b.version)); + return versions[versions.length - 1].name; +} + +module.exports = { latestDevBranch }; + +if (require.main === module) { + const input = require("fs").readFileSync(0, "utf8"); + const refs = input.split("\n").filter(Boolean); + try { + process.stdout.write(latestDevBranch(refs) + "\n"); + } catch (err) { + console.error(`latest-dev-branch: ${err.message}`); + process.exit(1); + } +} diff --git a/scripts/latest-dev-branch.test.ts b/scripts/latest-dev-branch.test.ts new file mode 100644 index 00000000..926b86e1 --- /dev/null +++ b/scripts/latest-dev-branch.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from "vitest"; +import { createRequire } from "module"; + +const require = createRequire(import.meta.url); +const { latestDevBranch } = require("./latest-dev-branch.cjs"); + +describe("latestDevBranch", () => { + it("picks the highest semver dev branch", () => { + expect(latestDevBranch(["dev-2.3.9", "dev-2.4.0", "dev-1.11.2"])).toBe( + "dev-2.4.0", + ); + }); + + it("compares numerically, not lexically", () => { + expect(latestDevBranch(["dev-2.10.0", "dev-2.9.0"])).toBe("dev-2.10.0"); + }); + + it("ignores main and non-dev branches", () => { + expect(latestDevBranch(["main", "feature/x", "dev-1.0.0"])).toBe( + "dev-1.0.0", + ); + }); + + it("strips refs/heads/ prefixes", () => { + expect(latestDevBranch(["refs/heads/dev-3.0.1"])).toBe("dev-3.0.1"); + }); + + it("throws when there are no dev branches", () => { + expect(() => latestDevBranch(["main", "feature/x"])).toThrow( + /no dev-X\.Y\.Z branches/, + ); + }); +}); diff --git a/scripts/parse-dev-branch.cjs b/scripts/parse-dev-branch.cjs new file mode 100644 index 00000000..7bdda80c --- /dev/null +++ b/scripts/parse-dev-branch.cjs @@ -0,0 +1,36 @@ +const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; + +function parseDevBranch(ref) { + if (!ref || typeof ref !== "string") { + throw new Error("a branch ref is required"); + } + + const name = ref.replace(/^refs\/heads\//, "").trim(); + + if (!name.startsWith("dev-")) { + throw new Error( + `release must run from a dev branch, got "${name}" (expected dev-X.Y.Z)`, + ); + } + + const version = name.slice("dev-".length); + if (!SEMVER.test(version)) { + throw new Error( + `branch "${name}" does not contain a valid semver version (got "${version}")`, + ); + } + + return version; +} + +module.exports = { parseDevBranch }; + +if (require.main === module) { + const ref = process.argv[2] || process.env.GITHUB_REF; + try { + process.stdout.write(parseDevBranch(ref) + "\n"); + } catch (err) { + console.error(`parse-dev-branch: ${err.message}`); + process.exit(1); + } +} diff --git a/scripts/parse-dev-branch.test.ts b/scripts/parse-dev-branch.test.ts new file mode 100644 index 00000000..6b6781d5 --- /dev/null +++ b/scripts/parse-dev-branch.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest"; +import { createRequire } from "module"; + +const require = createRequire(import.meta.url); +const { parseDevBranch } = require("./parse-dev-branch.cjs"); + +describe("parseDevBranch", () => { + it("extracts the version from a dev branch name", () => { + expect(parseDevBranch("dev-2.4.0")).toBe("2.4.0"); + }); + + it("strips a refs/heads/ prefix", () => { + expect(parseDevBranch("refs/heads/dev-1.11.2")).toBe("1.11.2"); + }); + + it("rejects the main branch", () => { + expect(() => parseDevBranch("main")).toThrow(/dev branch/); + }); + + it("rejects a dev branch without a semver version", () => { + expect(() => parseDevBranch("dev-foo")).toThrow(/valid semver/); + }); + + it("rejects a partial version", () => { + expect(() => parseDevBranch("dev-2.4")).toThrow(/valid semver/); + }); + + it("rejects an empty ref", () => { + expect(() => parseDevBranch("")).toThrow(); + }); +}); diff --git a/scripts/publish-youtube.cjs b/scripts/publish-youtube.cjs new file mode 100644 index 00000000..5f6cfe32 --- /dev/null +++ b/scripts/publish-youtube.cjs @@ -0,0 +1,105 @@ +const fs = require("fs"); +const path = require("path"); + +function extractYoutubeId(notes) { + const match = notes.match( + /([\s\S]*?)/, + ); + if (!match || !match[1].trim()) { + throw new Error( + "missing or empty section in release notes", + ); + } + return parseYoutubeId(match[1].trim()); +} + +function parseYoutubeId(raw) { + const value = raw.trim(); + let m = value.match(/[?&]v=([A-Za-z0-9_-]+)/); + if (m) return m[1]; + m = value.match(/youtu\.be\/([A-Za-z0-9_-]+)/); + if (m) return m[1]; + m = value.match(/embed\/([A-Za-z0-9_-]+)/); + if (m) return m[1]; + if (/^[A-Za-z0-9_-]+$/.test(value)) return value; + throw new Error(`could not parse a YouTube video id from "${value}"`); +} + +async function getAccessToken({ clientId, clientSecret, refreshToken }) { + const res = await fetch("https://oauth2.googleapis.com/token", { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + client_id: clientId, + client_secret: clientSecret, + refresh_token: refreshToken, + grant_type: "refresh_token", + }), + }); + if (!res.ok) { + throw new Error( + `token exchange failed (${res.status}): ${await res.text()}`, + ); + } + const json = await res.json(); + return json.access_token; +} + +async function setVideoPublic(accessToken, videoId) { + const res = await fetch( + "https://www.googleapis.com/youtube/v3/videos?part=status", + { + method: "PUT", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: videoId, + status: { privacyStatus: "public" }, + }), + }, + ); + if (!res.ok) { + throw new Error( + `videos.update failed (${res.status}): ${await res.text()}`, + ); + } + return res.json(); +} + +async function main() { + const clientId = process.env.YOUTUBE_CLIENT_ID; + const clientSecret = process.env.YOUTUBE_CLIENT_SECRET; + const refreshToken = process.env.YOUTUBE_REFRESH_TOKEN; + + if (!clientId || !clientSecret || !refreshToken) { + throw new Error( + "YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET, and YOUTUBE_REFRESH_TOKEN are required", + ); + } + + const notesPath = path.resolve( + process.env.RELEASE_NOTES || "RELEASE_NOTES.md", + ); + const notes = fs.readFileSync(notesPath, "utf8"); + const videoId = extractYoutubeId(notes); + + const accessToken = await getAccessToken({ + clientId, + clientSecret, + refreshToken, + }); + await setVideoPublic(accessToken, videoId); + + console.log(`Set YouTube video ${videoId} to public.`); +} + +module.exports = { extractYoutubeId, parseYoutubeId }; + +if (require.main === module) { + main().catch((err) => { + console.error(`publish-youtube: ${err.message}`); + process.exit(1); + }); +} diff --git a/scripts/publish-youtube.test.ts b/scripts/publish-youtube.test.ts new file mode 100644 index 00000000..79c1fedd --- /dev/null +++ b/scripts/publish-youtube.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect } from "vitest"; +import { createRequire } from "module"; + +const require = createRequire(import.meta.url); +const { extractYoutubeId, parseYoutubeId } = require("./publish-youtube.cjs"); + +describe("parseYoutubeId", () => { + it("parses a youtu.be short link", () => { + expect(parseYoutubeId("https://youtu.be/At8iDk6-Q_s")).toBe("At8iDk6-Q_s"); + }); + + it("parses a watch?v= link", () => { + expect(parseYoutubeId("https://www.youtube.com/watch?v=At8iDk6-Q_s")).toBe( + "At8iDk6-Q_s", + ); + }); + + it("parses an embed link", () => { + expect(parseYoutubeId("https://www.youtube.com/embed/At8iDk6-Q_s")).toBe( + "At8iDk6-Q_s", + ); + }); + + it("accepts a bare id", () => { + expect(parseYoutubeId("At8iDk6-Q_s")).toBe("At8iDk6-Q_s"); + }); + + it("throws on garbage", () => { + expect(() => parseYoutubeId("https://example.com/")).toThrow(); + }); +}); + +describe("extractYoutubeId", () => { + it("pulls the id from a release notes YOUTUBE section", () => { + const notes = [ + "", + "stuff", + "", + "", + "https://youtu.be/At8iDk6-Q_s", + "", + ].join("\n"); + expect(extractYoutubeId(notes)).toBe("At8iDk6-Q_s"); + }); + + it("throws when the YOUTUBE section is missing", () => { + expect(() => extractYoutubeId("no section here")).toThrow(/YOUTUBE/); + }); + + it("throws when the YOUTUBE section is empty", () => { + expect(() => + extractYoutubeId("\n\n"), + ).toThrow(/empty/); + }); +}); diff --git a/scripts/sync-version.cjs b/scripts/sync-version.cjs new file mode 100644 index 00000000..e177c1a9 --- /dev/null +++ b/scripts/sync-version.cjs @@ -0,0 +1,79 @@ +const fs = require("fs"); +const path = require("path"); + +const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; + +function readJsonWithTrailingNewline(filePath) { + const raw = fs.readFileSync(filePath, "utf8"); + return { data: JSON.parse(raw), hadTrailingNewline: raw.endsWith("\n") }; +} + +function writeJson(filePath, data, hadTrailingNewline) { + const serialized = JSON.stringify(data, null, 2); + fs.writeFileSync( + filePath, + hadTrailingNewline ? serialized + "\n" : serialized, + ); +} + +function syncVersion(version, options = {}) { + if (!SEMVER.test(version || "")) { + throw new Error(`invalid version "${version}" (expected X.Y.Z)`); + } + + const root = options.root || path.join(__dirname, ".."); + const pkgPath = path.join(root, "package.json"); + const lockPath = path.join(root, "package-lock.json"); + + const changed = []; + + const pkg = readJsonWithTrailingNewline(pkgPath); + if (pkg.data.version !== version) { + pkg.data.version = version; + writeJson(pkgPath, pkg.data, pkg.hadTrailingNewline); + changed.push("package.json"); + } + + if (fs.existsSync(lockPath)) { + const lock = readJsonWithTrailingNewline(lockPath); + let lockChanged = false; + + if (lock.data.version !== version) { + lock.data.version = version; + lockChanged = true; + } + if (lock.data.packages && lock.data.packages[""]) { + if (lock.data.packages[""].version !== version) { + lock.data.packages[""].version = version; + lockChanged = true; + } + } + + if (lockChanged) { + writeJson(lockPath, lock.data, lock.hadTrailingNewline); + changed.push("package-lock.json"); + } + } + + return changed; +} + +module.exports = { syncVersion }; + +if (require.main === module) { + const args = process.argv.slice(2); + const idx = args.indexOf("--version"); + const version = idx !== -1 ? args[idx + 1] : undefined; + + try { + const changed = syncVersion(version); + if (changed.length === 0) { + console.log(`sync-version: already at ${version}, no change`); + } else { + console.log(`sync-version: set ${version} in ${changed.join(", ")}`); + } + } catch (err) { + console.error(`sync-version: ${err.message}`); + process.exit(1); + } +} diff --git a/scripts/sync-version.test.ts b/scripts/sync-version.test.ts new file mode 100644 index 00000000..dbfb5bbb --- /dev/null +++ b/scripts/sync-version.test.ts @@ -0,0 +1,80 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { createRequire } from "module"; +import fs from "fs"; +import os from "os"; +import path from "path"; + +const require = createRequire(import.meta.url); +const { syncVersion } = require("./sync-version.cjs"); + +let root: string; + +function pkg() { + return JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")); +} +function lock() { + return JSON.parse( + fs.readFileSync(path.join(root, "package-lock.json"), "utf8"), + ); +} + +beforeEach(() => { + root = fs.mkdtempSync(path.join(os.tmpdir(), "sync-version-")); + fs.writeFileSync( + path.join(root, "package.json"), + JSON.stringify({ name: "termix", version: "2.3.2" }, null, 2) + "\n", + ); + fs.writeFileSync( + path.join(root, "package-lock.json"), + JSON.stringify( + { + name: "termix", + version: "2.3.2", + lockfileVersion: 3, + packages: { "": { name: "termix", version: "2.3.2" } }, + }, + null, + 2, + ) + "\n", + ); +}); + +afterEach(() => { + fs.rmSync(root, { recursive: true, force: true }); +}); + +describe("syncVersion", () => { + it("updates package.json and both package-lock version fields", () => { + const changed = syncVersion("2.4.0", { root }); + expect(changed).toEqual(["package.json", "package-lock.json"]); + expect(pkg().version).toBe("2.4.0"); + expect(lock().version).toBe("2.4.0"); + expect(lock().packages[""].version).toBe("2.4.0"); + }); + + it("is idempotent when already at the target version", () => { + syncVersion("2.4.0", { root }); + const changed = syncVersion("2.4.0", { root }); + expect(changed).toEqual([]); + }); + + it("preserves the trailing newline", () => { + syncVersion("2.4.0", { root }); + expect(fs.readFileSync(path.join(root, "package.json"), "utf8")).toMatch( + /\}\n$/, + ); + }); + + it("rejects an invalid version", () => { + expect(() => syncVersion("2.4", { root })).toThrow(/invalid version/); + }); + + it("works when only the lock root version is stale", () => { + fs.writeFileSync( + path.join(root, "package.json"), + JSON.stringify({ name: "termix", version: "2.4.0" }, null, 2) + "\n", + ); + const changed = syncVersion("2.4.0", { root }); + expect(changed).toEqual(["package-lock.json"]); + }); +}); diff --git a/src/backend/dashboard.ts b/src/backend/dashboard.ts index 77314262..98814fa2 100644 --- a/src/backend/dashboard.ts +++ b/src/backend/dashboard.ts @@ -1,14 +1,9 @@ import express from "express"; import cookieParser from "cookie-parser"; import { createCorsMiddleware } from "./utils/cors-config.js"; -import { getDb, DatabaseSaveTrigger } from "./database/db/index.js"; -import { - recentActivity, - hosts, - hostAccess, - dashboardPreferences, -} from "./database/db/schema.js"; -import { eq, and, desc, sql, inArray } from "drizzle-orm"; +import { getDb } from "./database/db/index.js"; +import { recentActivity, hosts, hostAccess } from "./database/db/schema.js"; +import { eq, and, desc, inArray } from "drizzle-orm"; import { dashboardLogger } from "./utils/logger.js"; import { SimpleDBOps } from "./utils/simple-db-ops.js"; import { AuthManager } from "./utils/auth-manager.js"; @@ -354,164 +349,6 @@ app.delete("/activity/reset", async (req, res) => { } }); -/** - * @openapi - * /dashboard/preferences: - * get: - * summary: Get dashboard layout preferences - * description: Returns the user's customized dashboard layout settings. If no preferences exist, returns default layout. - * tags: - * - Dashboard - * responses: - * 200: - * description: Dashboard preferences retrieved - * content: - * application/json: - * schema: - * type: object - * properties: - * cards: - * type: array - * items: - * type: object - * properties: - * id: - * type: string - * enabled: - * type: boolean - * order: - * type: integer - * 401: - * description: Session expired - * 500: - * description: Failed to get preferences - */ -app.get("/dashboard/preferences", async (req, res) => { - try { - const userId = (req as AuthenticatedRequest).userId; - - if (!SimpleDBOps.isUserDataUnlocked(userId)) { - return res.status(401).json({ - error: "Session expired - please log in again", - code: "SESSION_EXPIRED", - }); - } - - const preferences = await getDb() - .select() - .from(dashboardPreferences) - .where(eq(dashboardPreferences.userId, userId)); - - if (preferences.length === 0) { - const defaultLayout = { - cards: [ - { id: "server_overview", enabled: true, order: 1 }, - { id: "recent_activity", enabled: true, order: 2 }, - { id: "network_graph", enabled: false, order: 3 }, - { id: "quick_actions", enabled: true, order: 4 }, - { id: "server_stats", enabled: true, order: 5 }, - ], - }; - return res.json(defaultLayout); - } - - const layout = JSON.parse(preferences[0].layout as string); - res.json(layout); - } catch (err) { - dashboardLogger.error("Failed to get dashboard preferences", err); - res.status(500).json({ error: "Failed to get dashboard preferences" }); - } -}); - -/** - * @openapi - * /dashboard/preferences: - * post: - * summary: Save dashboard layout preferences - * description: Saves or updates the user's customized dashboard layout settings. - * tags: - * - Dashboard - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * cards: - * type: array - * items: - * type: object - * properties: - * id: - * type: string - * enabled: - * type: boolean - * order: - * type: integer - * responses: - * 200: - * description: Preferences saved successfully - * 400: - * description: Invalid request body - * 401: - * description: Session expired - * 500: - * description: Failed to save preferences - */ -app.post("/dashboard/preferences", async (req, res) => { - try { - const userId = (req as AuthenticatedRequest).userId; - - if (!SimpleDBOps.isUserDataUnlocked(userId)) { - return res.status(401).json({ - error: "Session expired - please log in again", - code: "SESSION_EXPIRED", - }); - } - - const { cards, mainWidthPct } = req.body; - - if (!cards || !Array.isArray(cards)) { - return res.status(400).json({ - error: "Invalid request body. Expected { cards: Array }", - }); - } - - const layoutObj: Record = { cards }; - if (typeof mainWidthPct === "number") { - layoutObj.mainWidthPct = mainWidthPct; - } - const layout = JSON.stringify(layoutObj); - - const existing = await getDb() - .select() - .from(dashboardPreferences) - .where(eq(dashboardPreferences.userId, userId)); - - if (existing.length > 0) { - await getDb() - .update(dashboardPreferences) - .set({ layout, updatedAt: sql`CURRENT_TIMESTAMP` }) - .where(eq(dashboardPreferences.userId, userId)); - } else { - await getDb().insert(dashboardPreferences).values({ userId, layout }); - } - - await DatabaseSaveTrigger.triggerSave("dashboard_preferences_updated"); - - dashboardLogger.success("Dashboard preferences saved", { - operation: "save_dashboard_preferences", - userId, - }); - - res.json({ success: true, message: "Dashboard preferences saved" }); - } catch (err) { - dashboardLogger.error("Failed to save dashboard preferences", err); - res.status(500).json({ error: "Failed to save dashboard preferences" }); - } -}); - const PORT = 30006; app.listen(PORT, async () => { try { diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index 292284ed..b8a0fd1f 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -10,11 +10,15 @@ import credentialsRoutes from "./routes/credentials.js"; import snippetsRoutes from "./routes/snippets.js"; import c2sTunnelPresetRoutes from "./routes/c2s-tunnel-presets.js"; import terminalRoutes from "./routes/terminal.js"; +import sessionLogRoutes from "./routes/session-log-routes.js"; import guacamoleRoutes from "../guacamole/routes.js"; import networkTopologyRoutes from "./routes/network-topology.js"; import rbacRoutes from "./routes/rbac.js"; import openTabsRoutes from "./routes/open-tabs.js"; import userPreferencesRoutes from "./routes/user-preferences.js"; +import proxmoxRoutes from "./routes/proxmox.js"; +import { registerAuditLogRoutes } from "./routes/audit-log-routes.js"; +import { registerTailscaleRoutes } from "./routes/tailscale-routes.js"; import { createCorsMiddleware } from "../utils/cors-config.js"; import fs from "fs"; import path from "path"; @@ -1777,11 +1781,15 @@ app.use("/credentials", credentialsRoutes); app.use("/snippets", snippetsRoutes); app.use("/c2s-tunnel-presets", c2sTunnelPresetRoutes); app.use("/terminal", terminalRoutes); +app.use("/session_logs", sessionLogRoutes); app.use("/guacamole", guacamoleRoutes); app.use("/network-topology", networkTopologyRoutes); app.use("/rbac", rbacRoutes); app.use("/open-tabs", openTabsRoutes); app.use("/user-preferences", userPreferencesRoutes); +app.use("/proxmox", proxmoxRoutes); +registerAuditLogRoutes(app, authenticateJWT); +registerTailscaleRoutes(app, authenticateJWT); const frontendDistPaths = [ path.join(__dirname, "../../../dist"), diff --git a/src/backend/database/db/index.ts b/src/backend/database/db/index.ts index 8d50f3de..aaa27e02 100644 --- a/src/backend/database/db/index.ts +++ b/src/backend/database/db/index.ts @@ -488,6 +488,62 @@ async function initializeCompleteDatabase(): Promise { FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); + CREATE TABLE IF NOT EXISTS host_metrics_preferences ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + host_id INTEGER NOT NULL, + layout TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE + ); + + CREATE UNIQUE INDEX IF NOT EXISTS idx_host_metrics_prefs_user_host + ON host_metrics_preferences (user_id, host_id); + + CREATE TABLE IF NOT EXISTS host_health_checks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + host_id INTEGER NOT NULL, + checks TEXT NOT NULL, + interval_seconds INTEGER NOT NULL DEFAULT 300, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE + ); + + CREATE UNIQUE INDEX IF NOT EXISTS idx_host_health_checks_user_host + ON host_health_checks (user_id, host_id); + + CREATE TABLE IF NOT EXISTS host_health_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + host_id INTEGER NOT NULL, + check_id TEXT NOT NULL, + ts TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + ok INTEGER NOT NULL, + latency_ms INTEGER, + detail TEXT, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE + ); + + CREATE INDEX IF NOT EXISTS idx_host_health_history_lookup + ON host_health_history (user_id, host_id, check_id, ts); + + CREATE TABLE IF NOT EXISTS sso_providers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + display_order INTEGER NOT NULL DEFAULT 0, + config TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + `); try { @@ -627,6 +683,17 @@ const migrateSchema = () => { addColumnIfNotExists("user_preferences", "font_size", "TEXT"); addColumnIfNotExists("user_preferences", "accent_color", "TEXT"); addColumnIfNotExists("user_preferences", "language", "TEXT"); + addColumnIfNotExists("user_preferences", "storage_mode", "TEXT"); + addColumnIfNotExists("user_preferences", "command_autocomplete", "INTEGER"); + addColumnIfNotExists("user_preferences", "command_palette_enabled", "INTEGER"); + addColumnIfNotExists("user_preferences", "show_host_tags", "INTEGER"); + addColumnIfNotExists("user_preferences", "host_tray_on_click", "INTEGER"); + addColumnIfNotExists("user_preferences", "pin_app_rail", "INTEGER"); + addColumnIfNotExists("user_preferences", "folders_collapsed", "INTEGER"); + addColumnIfNotExists("user_preferences", "confirm_snippet_execution", "INTEGER"); + addColumnIfNotExists("user_preferences", "disable_update_check", "INTEGER"); + addColumnIfNotExists("user_preferences", "confirm_tab_close", "INTEGER"); + addColumnIfNotExists("user_preferences", "hidden_rail_tabs", "TEXT"); addColumnIfNotExists("users", "is_admin", "INTEGER NOT NULL DEFAULT 0"); @@ -664,6 +731,16 @@ const migrateSchema = () => { "enable_terminal", "INTEGER NOT NULL DEFAULT 1", ); + addColumnIfNotExists( + "ssh_data", + "enable_session_logging", + "INTEGER NOT NULL DEFAULT 1", + ); + addColumnIfNotExists( + "ssh_data", + "enable_command_history", + "INTEGER NOT NULL DEFAULT 1", + ); addColumnIfNotExists( "ssh_data", "enable_tunnel", @@ -714,6 +791,17 @@ const migrateSchema = () => { "INTEGER NOT NULL DEFAULT 0", ); addColumnIfNotExists("ssh_data", "docker_config", "TEXT"); + addColumnIfNotExists( + "ssh_data", + "enable_proxmox", + "INTEGER NOT NULL DEFAULT 0", + ); + addColumnIfNotExists("ssh_data", "proxmox_config", "TEXT"); + addColumnIfNotExists( + "ssh_data", + "enable_tmux_monitor", + "INTEGER NOT NULL DEFAULT 0", + ); addColumnIfNotExists("ssh_data", "connection_type", 'TEXT NOT NULL DEFAULT "ssh"'); addColumnIfNotExists("ssh_data", "domain", "TEXT"); @@ -999,30 +1087,6 @@ const migrateSchema = () => { } } - try { - sqlite - .prepare("SELECT id FROM dashboard_preferences LIMIT 1") - .get(); - } catch { - try { - sqlite.exec(` - CREATE TABLE IF NOT EXISTS dashboard_preferences ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id TEXT NOT NULL UNIQUE, - layout TEXT NOT NULL, - created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE - ); - `); - } catch (createError) { - databaseLogger.warn("Failed to create dashboard_preferences table", { - operation: "schema_migration", - error: createError, - }); - } - } - try { sqlite.prepare("SELECT id FROM host_access LIMIT 1").get(); } catch { @@ -1183,6 +1247,25 @@ const migrateSchema = () => { } } + // Rename the legacy "stats" tab type to "host-metrics" so previously saved + // open tabs restore correctly after the Server Stats -> Host Metrics rename. + try { + sqlite.prepare("SELECT id FROM user_open_tabs LIMIT 1").get(); + const result = sqlite + .prepare( + "UPDATE user_open_tabs SET tab_type = 'host-metrics' WHERE tab_type = 'stats'", + ) + .run(); + if (result.changes > 0) { + databaseLogger.info( + `Migrated ${result.changes} open tab(s) from 'stats' to 'host-metrics'`, + { operation: "open_tabs_tab_type_migration" }, + ); + } + } catch { + // user_open_tabs table not present yet; nothing to migrate. + } + try { sqlite.prepare("SELECT id FROM roles LIMIT 1").get(); } catch { @@ -1382,6 +1465,79 @@ const migrateSchema = () => { } } + // --- tmux-monitor begin --- + try { + sqlite.prepare("SELECT id FROM tmux_session_tags LIMIT 1").get(); + } catch { + try { + sqlite.exec(` + CREATE TABLE IF NOT EXISTS tmux_session_tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + host_id INTEGER NOT NULL, + session_name TEXT NOT NULL, + tag TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE + ); + `); + } catch (createError) { + databaseLogger.warn("Failed to create tmux_session_tags table", { + operation: "schema_migration", + error: createError, + }); + } + } + + // Rebuild pre-release tables created without the host FK so deleting a + // host cascades to its tags (SQLite cannot add a FK via ALTER TABLE). + try { + const tagFks = sqlite + .prepare("PRAGMA foreign_key_list(tmux_session_tags)") + .all() as Array<{ table: string; from: string }>; + const hasHostFk = tagFks.some( + (fk) => fk.from === "host_id" && fk.table === "ssh_data", + ); + if (!hasHostFk) { + sqlite.exec(` + BEGIN; + CREATE TABLE tmux_session_tags_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + host_id INTEGER NOT NULL, + session_name TEXT NOT NULL, + tag TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (host_id) REFERENCES ssh_data (id) ON DELETE CASCADE + ); + INSERT INTO tmux_session_tags_new (id, user_id, host_id, session_name, tag, created_at) + SELECT id, user_id, host_id, session_name, tag, created_at + FROM tmux_session_tags + WHERE user_id IN (SELECT id FROM users) + AND host_id IN (SELECT id FROM ssh_data); + DROP TABLE tmux_session_tags; + ALTER TABLE tmux_session_tags_new RENAME TO tmux_session_tags; + COMMIT; + `); + databaseLogger.info("Rebuilt tmux_session_tags with host FK", { + operation: "schema_migration", + }); + } + } catch (rebuildError) { + try { + sqlite.exec("ROLLBACK;"); + } catch { + // no transaction open + } + databaseLogger.warn("Failed to add host FK to tmux_session_tags", { + operation: "schema_migration", + error: rebuildError, + }); + } + // --- tmux-monitor end --- + try { const existingRoles = sqlite.prepare("SELECT name, is_system FROM roles").all() as Array<{ name: string; is_system: number }>; @@ -1487,6 +1643,43 @@ const migrateSchema = () => { }); } + addColumnIfNotExists("users", "sso_provider_id", "INTEGER"); + + // Migrate legacy single oidc_config settings blob into sso_providers table + try { + const migrationDone = sqlite + .prepare("SELECT value FROM settings WHERE key = 'sso_migration_v1'") + .get(); + if (!migrationDone) { + const providerCount = ( + sqlite.prepare("SELECT COUNT(*) as c FROM sso_providers").get() as { c: number } + ).c; + if (providerCount === 0) { + const legacyRow = sqlite + .prepare("SELECT value FROM settings WHERE key = 'oidc_config'") + .get() as { value: string } | undefined; + if (legacyRow) { + sqlite + .prepare( + "INSERT INTO sso_providers (name, type, enabled, display_order, config) VALUES (?, 'oidc', 1, 0, ?)", + ) + .run("OIDC", legacyRow.value); + databaseLogger.info("Migrated legacy oidc_config into sso_providers table", { + operation: "sso_migration_v1", + }); + } + } + sqlite + .prepare("INSERT OR REPLACE INTO settings (key, value) VALUES ('sso_migration_v1', 'true')") + .run(); + } + } catch (e) { + databaseLogger.warn("Failed to run SSO migration v1", { + operation: "sso_migration_v1", + error: e, + }); + } + databaseLogger.success("Schema migration completed", { operation: "schema_migration", }); diff --git a/src/backend/database/db/schema.ts b/src/backend/database/db/schema.ts index 3907e341..a900968e 100644 --- a/src/backend/database/db/schema.ts +++ b/src/backend/database/db/schema.ts @@ -9,6 +9,7 @@ export const users = sqliteTable("users", { isOidc: integer("is_oidc", { mode: "boolean" }).notNull().default(false), oidcIdentifier: text("oidc_identifier"), + ssoProviderId: integer("sso_provider_id"), clientId: text("client_id"), clientSecret: text("client_secret"), issuerUrl: text("issuer_url"), @@ -30,6 +31,21 @@ export const settings = sqliteTable("settings", { value: text("value").notNull(), }); +export const ssoProviders = sqliteTable("sso_providers", { + id: integer("id").primaryKey({ autoIncrement: true }), + name: text("name").notNull(), + type: text("type").notNull(), + enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), + displayOrder: integer("display_order").notNull().default(0), + config: text("config").notNull(), + createdAt: text("created_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + updatedAt: text("updated_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), +}); + export const sessions = sqliteTable("sessions", { id: text("id").primaryKey(), userId: text("user_id") @@ -97,6 +113,12 @@ export const hosts = sqliteTable("ssh_data", { enableTerminal: integer("enable_terminal", { mode: "boolean" }) .notNull() .default(true), + enableSessionLogging: integer("enable_session_logging", { mode: "boolean" }) + .notNull() + .default(true), + enableCommandHistory: integer("enable_command_history", { mode: "boolean" }) + .notNull() + .default(true), enableTunnel: integer("enable_tunnel", { mode: "boolean" }) .notNull() .default(true), @@ -108,6 +130,9 @@ export const hosts = sqliteTable("ssh_data", { enableDocker: integer("enable_docker", { mode: "boolean" }) .notNull() .default(false), + enableTmuxMonitor: integer("enable_tmux_monitor", { mode: "boolean" }) + .notNull() + .default(false), showTerminalInSidebar: integer("show_terminal_in_sidebar", { mode: "boolean" }) .notNull() .default(true), @@ -126,6 +151,10 @@ export const hosts = sqliteTable("ssh_data", { defaultPath: text("default_path"), statsConfig: text("stats_config"), dockerConfig: text("docker_config"), + enableProxmox: integer("enable_proxmox", { mode: "boolean" }) + .notNull() + .default(false), + proxmoxConfig: text("proxmox_config"), terminalConfig: text("terminal_config"), quickActions: text("quick_actions"), notes: text("notes"), @@ -441,21 +470,6 @@ export const networkTopology = sqliteTable("network_topology", { .default(sql`CURRENT_TIMESTAMP`), }); -export const dashboardPreferences = sqliteTable("dashboard_preferences", { - id: integer("id").primaryKey({ autoIncrement: true }), - userId: text("user_id") - .notNull() - .unique() - .references(() => users.id, { onDelete: "cascade" }), - layout: text("layout").notNull(), - createdAt: text("created_at") - .notNull() - .default(sql`CURRENT_TIMESTAMP`), - updatedAt: text("updated_at") - .notNull() - .default(sql`CURRENT_TIMESTAMP`), -}); - export const hostAccess = sqliteTable("host_access", { id: integer("id").primaryKey({ autoIncrement: true }), hostId: integer("host_id") @@ -680,7 +694,88 @@ export const userPreferences = sqliteTable("user_preferences", { fontSize: text("font_size"), accentColor: text("accent_color"), language: text("language"), + storageMode: text("storage_mode"), + commandAutocomplete: integer("command_autocomplete", { mode: "boolean" }), + commandPaletteEnabled: integer("command_palette_enabled", { mode: "boolean" }), + showHostTags: integer("show_host_tags", { mode: "boolean" }), + hostTrayOnClick: integer("host_tray_on_click", { mode: "boolean" }), + pinAppRail: integer("pin_app_rail", { mode: "boolean" }), + foldersCollapsed: integer("folders_collapsed", { mode: "boolean" }), + confirmSnippetExecution: integer("confirm_snippet_execution", { mode: "boolean" }), + disableUpdateCheck: integer("disable_update_check", { mode: "boolean" }), + confirmTabClose: integer("confirm_tab_close", { mode: "boolean" }), + hiddenRailTabs: text("hidden_rail_tabs"), updatedAt: text("updated_at") .notNull() .default(sql`CURRENT_TIMESTAMP`), }); + +export const hostMetricsPreferences = sqliteTable("host_metrics_preferences", { + id: integer("id").primaryKey({ autoIncrement: true }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + hostId: integer("host_id") + .notNull() + .references(() => hosts.id, { onDelete: "cascade" }), + // JSON-encoded HostMetricsLayout. Layout has no secrets, so it is stored as + // plain JSON (no field-level encryption). + layout: text("layout").notNull(), + createdAt: text("created_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + updatedAt: text("updated_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), +}); + +export const hostHealthChecks = sqliteTable("host_health_checks", { + id: integer("id").primaryKey({ autoIncrement: true }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + hostId: integer("host_id") + .notNull() + .references(() => hosts.id, { onDelete: "cascade" }), + // JSON array of { id, name, type: "tcp"|"http", target, port, path } + checks: text("checks").notNull(), + intervalSeconds: integer("interval_seconds").notNull().default(300), + createdAt: text("created_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + updatedAt: text("updated_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), +}); + +export const hostHealthHistory = sqliteTable("host_health_history", { + id: integer("id").primaryKey({ autoIncrement: true }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + hostId: integer("host_id") + .notNull() + .references(() => hosts.id, { onDelete: "cascade" }), + checkId: text("check_id").notNull(), + ts: text("ts").notNull().default(sql`CURRENT_TIMESTAMP`), + ok: integer("ok", { mode: "boolean" }).notNull(), + latencyMs: integer("latency_ms"), + detail: text("detail"), +}); + +// --- tmux-monitor begin --- +export const tmuxSessionTags = sqliteTable("tmux_session_tags", { + id: integer("id").primaryKey({ autoIncrement: true }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + hostId: integer("host_id") + .notNull() + .references(() => hosts.id, { onDelete: "cascade" }), + sessionName: text("session_name").notNull(), + tag: text("tag").notNull(), + createdAt: text("created_at") + .notNull() + .default(sql`CURRENT_TIMESTAMP`), +}); +// --- tmux-monitor end --- diff --git a/src/backend/database/routes/audit-log-routes.ts b/src/backend/database/routes/audit-log-routes.ts new file mode 100644 index 00000000..0ba74eb0 --- /dev/null +++ b/src/backend/database/routes/audit-log-routes.ts @@ -0,0 +1,156 @@ +import type { AuthenticatedRequest } from "../../../types/index.js"; +import type { RequestHandler, Router } from "express"; +import { eq, and, gte, lte, sql } from "drizzle-orm"; +import { db } from "../db/index.js"; +import { auditLogs, users } from "../db/schema.js"; +import { apiLogger } from "../../utils/logger.js"; + +export function registerAuditLogRoutes( + router: Router, + authenticateJWT: RequestHandler, +): void { + /** + * @openapi + * /audit-logs: + * get: + * summary: List audit logs + * description: Returns paginated, filterable audit log entries. Admin only. + * tags: + * - Audit + * parameters: + * - in: query + * name: page + * schema: { type: integer, default: 1 } + * - in: query + * name: limit + * schema: { type: integer, default: 50, maximum: 200 } + * - in: query + * name: userId + * schema: { type: string } + * - in: query + * name: action + * schema: { type: string } + * - in: query + * name: resourceType + * schema: { type: string } + * - in: query + * name: success + * schema: { type: string, enum: [true, false] } + * - in: query + * name: startDate + * schema: { type: string, format: date-time } + * - in: query + * name: endDate + * schema: { type: string, format: date-time } + * responses: + * 200: + * description: Paginated list of audit logs. + * 403: + * description: Not authorized. + * 500: + * description: Failed to fetch audit logs. + */ + router.get("/audit-logs", authenticateJWT, async (req, res) => { + try { + const authReq = req as AuthenticatedRequest; + const adminUser = await db + .select({ isAdmin: users.isAdmin }) + .from(users) + .where(eq(users.id, authReq.userId)) + .limit(1); + + if (!adminUser[0]?.isAdmin) { + return res.status(403).json({ error: "Not authorized" }); + } + + const page = Math.max(1, parseInt(String(req.query.page || "1"), 10)); + const limit = Math.min( + 200, + Math.max(1, parseInt(String(req.query.limit || "50"), 10)), + ); + const offset = (page - 1) * limit; + + const { userId, action, resourceType, success, startDate, endDate } = + req.query as Record; + + const conditions = []; + + if (userId) conditions.push(eq(auditLogs.userId, userId)); + if (action) conditions.push(eq(auditLogs.action, action)); + if (resourceType) + conditions.push(eq(auditLogs.resourceType, resourceType)); + if (success !== undefined && success !== "") { + conditions.push(eq(auditLogs.success, success === "true")); + } + if (startDate) conditions.push(gte(auditLogs.timestamp, startDate)); + if (endDate) conditions.push(lte(auditLogs.timestamp, endDate)); + + const whereClause = + conditions.length > 0 ? and(...conditions) : undefined; + + const [logs, totalResult] = await Promise.all([ + db + .select() + .from(auditLogs) + .where(whereClause) + .orderBy(sql`${auditLogs.timestamp} DESC`) + .limit(limit) + .offset(offset), + db + .select({ count: sql`COUNT(*)` }) + .from(auditLogs) + .where(whereClause), + ]); + + const total = totalResult[0]?.count ?? 0; + const totalPages = Math.ceil(total / limit); + + return res.json({ logs, total, page, totalPages }); + } catch (err) { + apiLogger.error("Failed to fetch audit logs", err); + return res.status(500).json({ error: "Failed to fetch audit logs" }); + } + }); + + /** + * @openapi + * /audit-logs/actions: + * get: + * summary: List distinct audit log action types + * description: Returns all distinct action values in the audit log for filter dropdowns. Admin only. + * tags: + * - Audit + * responses: + * 200: + * description: List of distinct action strings. + * 403: + * description: Not authorized. + * 500: + * description: Failed to fetch audit log actions. + */ + router.get("/audit-logs/actions", authenticateJWT, async (req, res) => { + try { + const authReq = req as AuthenticatedRequest; + const adminUser = await db + .select({ isAdmin: users.isAdmin }) + .from(users) + .where(eq(users.id, authReq.userId)) + .limit(1); + + if (!adminUser[0]?.isAdmin) { + return res.status(403).json({ error: "Not authorized" }); + } + + const rows = db.$client + .prepare("SELECT DISTINCT action FROM audit_logs ORDER BY action ASC") + .all() as { action: string }[]; + + return res.json({ actions: rows.map((r) => r.action) }); + } catch (err) { + apiLogger.error("Failed to fetch audit log actions", err); + return res + .status(500) + .json({ error: "Failed to fetch audit log actions" }); + } + }); +} diff --git a/src/backend/database/routes/credentials.ts b/src/backend/database/routes/credentials.ts index c81109b2..79f6e6a8 100644 --- a/src/backend/database/routes/credentials.ts +++ b/src/backend/database/routes/credentials.ts @@ -15,6 +15,7 @@ import { AuthManager } from "../../utils/auth-manager.js"; import { parseSSHKey } from "../../utils/ssh-key-utils.js"; import { registerCredentialKeyRoutes } from "./credential-key-routes.js"; import { registerCredentialDeployRoutes } from "./credential-deploy-routes.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; const router = express.Router(); @@ -186,6 +187,25 @@ router.post( userId, )) as typeof credentialData & { id: number }; + const { ipAddress: ccIp, userAgent: ccUa } = getRequestMeta(req); + const { users: usersTableCc } = await import("../db/schema.js"); + const ccActor = await db + .select({ username: usersTableCc.username }) + .from(usersTableCc) + .where(eq(usersTableCc.id, userId)) + .limit(1); + await logAudit({ + userId, + username: ccActor[0]?.username ?? userId, + action: "create_credential", + resourceType: "credential", + resourceId: String(created.id), + resourceName: name, + ipAddress: ccIp, + userAgent: ccUa, + success: true, + }); + authLogger.success( `SSH credential created: ${name} (${authType}) by user ${userId}`, { @@ -567,6 +587,25 @@ router.put( credentialId: parseInt(id), }); + const { ipAddress: cuIp, userAgent: cuUa } = getRequestMeta(req); + const { users: usersTableCu } = await import("../db/schema.js"); + const cuActor = await db + .select({ username: usersTableCu.username }) + .from(usersTableCu) + .where(eq(usersTableCu.id, userId)) + .limit(1); + await logAudit({ + userId, + username: cuActor[0]?.username ?? userId, + action: "update_credential", + resourceType: "credential", + resourceId: id, + resourceName: existing[0].name, + ipAddress: cuIp, + userAgent: cuUa, + success: true, + }); + res.json(formatCredentialOutput(updated[0])); } catch (err) { authLogger.error("Failed to update credential", err); @@ -697,6 +736,25 @@ router.delete( credentialId: parseInt(id), }); + const { ipAddress: cdIp, userAgent: cdUa } = getRequestMeta(req); + const { users: usersTableCd } = await import("../db/schema.js"); + const cdActor = await db + .select({ username: usersTableCd.username }) + .from(usersTableCd) + .where(eq(usersTableCd.id, userId)) + .limit(1); + await logAudit({ + userId, + username: cdActor[0]?.username ?? userId, + action: "delete_credential", + resourceType: "credential", + resourceId: id, + resourceName: credentialToDelete[0].name, + ipAddress: cdIp, + userAgent: cdUa, + success: true, + }); + res.json({ message: "Credential deleted successfully" }); } catch (err) { authLogger.error("Failed to delete credential", err); diff --git a/src/backend/database/routes/delete-user-data.ts b/src/backend/database/routes/delete-user-data.ts index 8e99ef38..9367d3fd 100644 --- a/src/backend/database/routes/delete-user-data.ts +++ b/src/backend/database/routes/delete-user-data.ts @@ -4,7 +4,6 @@ import { db } from "../db/index.js"; import { auditLogs, commandHistory, - dashboardPreferences, dismissedAlerts, fileManagerPinned, fileManagerRecent, @@ -77,9 +76,6 @@ export async function deleteUserAndRelatedData(userId: string): Promise { await db.delete(sshCredentials).where(eq(sshCredentials.userId, userId)); await db.delete(networkTopology).where(eq(networkTopology.userId, userId)); - await db - .delete(dashboardPreferences) - .where(eq(dashboardPreferences.userId, userId)); await db.delete(opksshTokens).where(eq(opksshTokens.userId, userId)); await db.delete(userOpenTabs).where(eq(userOpenTabs.userId, userId)); await db.delete(userPreferences).where(eq(userPreferences.userId, userId)); diff --git a/src/backend/database/routes/host-bulk-routes.ts b/src/backend/database/routes/host-bulk-routes.ts index 9edd751f..935c0727 100644 --- a/src/backend/database/routes/host-bulk-routes.ts +++ b/src/backend/database/routes/host-bulk-routes.ts @@ -100,7 +100,12 @@ export function registerHostBulkRoutes( try { const ownedHosts = await db - .select({ id: hosts.id, statsConfig: hosts.statsConfig }) + .select({ + id: hosts.id, + statsConfig: hosts.statsConfig, + credentialId: hosts.credentialId, + proxmoxConfig: hosts.proxmoxConfig, + }) .from(hosts) .where(and(inArray(hosts.id, hostIds), eq(hosts.userId, userId))); @@ -132,6 +137,12 @@ export function registerHostBulkRoutes( simpleUpdates.enableFileManager = updates.enableFileManager; if (typeof updates.enableDocker === "boolean") simpleUpdates.enableDocker = updates.enableDocker; + if (typeof updates.enableTmuxMonitor === "boolean") + simpleUpdates.enableTmuxMonitor = updates.enableTmuxMonitor; + // Disabling Proxmox is a plain flag flip; enabling is handled per-host + // below so each host can default to its own stored credential. + if (updates.enableProxmox === false) + simpleUpdates.enableProxmox = false; if (Object.keys(simpleUpdates).length > 0) { await db @@ -157,6 +168,37 @@ export function registerHostBulkRoutes( } } + // Enabling Proxmox needs per-host handling: each host defaults its + // Proxmox credential to the credential already stored on that host, so + // discovery works right away without picking one by hand. Existing + // proxmoxConfig values are preserved. + if (updates.enableProxmox === true) { + for (const host of ownedHosts) { + try { + const existing = host.proxmoxConfig + ? JSON.parse(host.proxmoxConfig as string) + : {}; + const merged = { + defaultCredentialId: + existing.defaultCredentialId ?? host.credentialId ?? null, + windowsPatterns: existing.windowsPatterns ?? "win, windows", + dockerPatterns: existing.dockerPatterns ?? "docker", + preferredPrefixes: + existing.preferredPrefixes ?? "10., 192.168.", + }; + await db + .update(hosts) + .set({ + enableProxmox: true, + proxmoxConfig: JSON.stringify(merged), + }) + .where(and(eq(hosts.id, host.id), eq(hosts.userId, userId))); + } catch { + errors.push(`Failed to enable Proxmox for host ${host.id}`); + } + } + } + DatabaseSaveTrigger.triggerSave("bulk_update"); return res.json({ @@ -244,13 +286,18 @@ export function registerHostBulkRoutes( if ( effectiveConnectionType === "ssh" && hostData.authType && - !["password", "key", "credential", "none", "opkssh"].includes( - hostData.authType, - ) + ![ + "password", + "key", + "credential", + "none", + "opkssh", + "tailscale", + ].includes(hostData.authType) ) { results.failed++; results.errors.push( - `Host ${i + 1}: Invalid authType. Must be 'password', 'key', 'credential', 'none', or 'opkssh'`, + `Host ${i + 1}: Invalid authType. Must be 'password', 'key', 'credential', 'none', 'opkssh', or 'tailscale'`, ); continue; } @@ -340,6 +387,8 @@ export function registerHostBulkRoutes( enableTunnel: hostData.enableTunnel !== false, enableFileManager: hostData.enableFileManager !== false, enableDocker: hostData.enableDocker || false, + enableProxmox: hostData.enableProxmox || false, + enableTmuxMonitor: hostData.enableTmuxMonitor || false, showTerminalInSidebar: hostData.showTerminalInSidebar ? 1 : 0, showFileManagerInSidebar: hostData.showFileManagerInSidebar ? 1 : 0, showTunnelInSidebar: hostData.showTunnelInSidebar ? 1 : 0, @@ -362,6 +411,9 @@ export function registerHostBulkRoutes( dockerConfig: hostData.dockerConfig ? JSON.stringify(hostData.dockerConfig) : null, + proxmoxConfig: hostData.proxmoxConfig + ? JSON.stringify(hostData.proxmoxConfig) + : null, terminalConfig: hostData.terminalConfig ? JSON.stringify(hostData.terminalConfig) : null, diff --git a/src/backend/database/routes/host-folder-routes.ts b/src/backend/database/routes/host-folder-routes.ts index 2964745e..c6d2da55 100644 --- a/src/backend/database/routes/host-folder-routes.ts +++ b/src/backend/database/routes/host-folder-routes.ts @@ -1,9 +1,9 @@ import type { Request, RequestHandler, Response, Router } from "express"; import type { AuthenticatedRequest } from "../../../types/index.js"; -import { and, eq, inArray, or } from "drizzle-orm"; +import { and, eq, inArray, like, or, sql } from "drizzle-orm"; import { databaseLogger, sshLogger } from "../../utils/logger.js"; -import { SimpleDBOps } from "../../utils/simple-db-ops.js"; import { db, DatabaseSaveTrigger } from "../db/index.js"; +import type { SQLiteColumn } from "drizzle-orm/sqlite-core"; import { commandHistory, fileManagerPinned, @@ -75,27 +75,32 @@ export function registerHostFolderRoutes( } try { - const updatedHosts = await SimpleDBOps.update( - hosts, - "ssh_data", - and(eq(hosts.userId, userId), eq(hosts.folder, oldName)), - { - folder: newName, - updatedAt: new Date().toISOString(), - }, - userId, - ); + const now = new Date().toISOString(); + const oldPrefix = `${oldName} / `; + const newPrefix = `${newName} / `; + const childLike = `${oldPrefix}%`; + + // folder is a plaintext column, so a SQL expression renames the exact + // folder and re-paths every nested child in one statement. + const renameExpr = (col: SQLiteColumn) => + sql`CASE WHEN ${col} = ${oldName} THEN ${newName} ELSE ${newPrefix} || substr(${col}, ${oldPrefix.length + 1}) END`; + + const folderMatch = (col: SQLiteColumn) => + or(eq(col, oldName), like(col, childLike)); + + const updatedHosts = await db + .update(hosts) + .set({ folder: renameExpr(hosts.folder), updatedAt: now }) + .where(and(eq(hosts.userId, userId), folderMatch(hosts.folder))) + .returning(); const updatedCredentials = await db .update(sshCredentials) - .set({ - folder: newName, - updatedAt: new Date().toISOString(), - }) + .set({ folder: renameExpr(sshCredentials.folder), updatedAt: now }) .where( and( eq(sshCredentials.userId, userId), - eq(sshCredentials.folder, oldName), + folderMatch(sshCredentials.folder), ), ) .returning(); @@ -104,12 +109,9 @@ export function registerHostFolderRoutes( await db .update(sshFolders) - .set({ - name: newName, - updatedAt: new Date().toISOString(), - }) + .set({ name: renameExpr(sshFolders.name), updatedAt: now }) .where( - and(eq(sshFolders.userId, userId), eq(sshFolders.name, oldName)), + and(eq(sshFolders.userId, userId), folderMatch(sshFolders.name)), ); res.json({ @@ -306,17 +308,15 @@ export function registerHostFolderRoutes( }); try { + // Match the folder itself and any nested children (e.g. "fgh / sub"). + const childLike = `${folderName} / %`; + const folderMatch = (col: SQLiteColumn) => + or(eq(col, folderName), like(col, childLike)); + const hostsToDelete = await db .select() .from(hosts) - .where(and(eq(hosts.userId, userId), eq(hosts.folder, folderName))); - - if (hostsToDelete.length === 0) { - return res.json({ - message: "No hosts found in folder", - deletedCount: 0, - }); - } + .where(and(eq(hosts.userId, userId), folderMatch(hosts.folder))); const hostIds = hostsToDelete.map((host) => host.id); @@ -363,14 +363,18 @@ export function registerHostFolderRoutes( .where(inArray(sessionRecordings.hostId, hostIds)); } - await db - .delete(hosts) - .where(and(eq(hosts.userId, userId), eq(hosts.folder, folderName))); + if (hostIds.length > 0) { + await db + .delete(hosts) + .where(and(eq(hosts.userId, userId), folderMatch(hosts.folder))); + } + // Always remove the folder records (and nested children), even when the + // folder held no hosts, so empty folders don't reappear on reload. await db .delete(sshFolders) .where( - and(eq(sshFolders.userId, userId), eq(sshFolders.name, folderName)), + and(eq(sshFolders.userId, userId), folderMatch(sshFolders.name)), ); DatabaseSaveTrigger.triggerSave("folder_hosts_delete"); diff --git a/src/backend/database/routes/host-normalizers.test.ts b/src/backend/database/routes/host-normalizers.test.ts index 4bae8a2f..d406951e 100644 --- a/src/backend/database/routes/host-normalizers.test.ts +++ b/src/backend/database/routes/host-normalizers.test.ts @@ -3,6 +3,7 @@ import { isNonEmptyString, isValidPort, normalizeImportedHost, + renameFolderPath, stripSensitiveFields, transformHostResponse, } from "./host-normalizers.js"; @@ -22,6 +23,42 @@ describe("isNonEmptyString", () => { }); }); +describe("renameFolderPath", () => { + it("renames an exact folder match", () => { + expect(renameFolderPath("Production", "Production", "Prod")).toBe("Prod"); + }); + + it("re-paths nested children under the renamed ancestor", () => { + expect(renameFolderPath("Production / Web", "Production", "Prod")).toBe( + "Prod / Web", + ); + expect( + renameFolderPath("Production / Web / app01", "Production", "Prod"), + ).toBe("Prod / Web / app01"); + }); + + it("renames a nested folder itself and keeps its parent", () => { + expect( + renameFolderPath("Production / Web", "Production / Web", "Frontend"), + ).toBe("Frontend"); + expect( + renameFolderPath( + "Production / Web / app01", + "Production / Web", + "Production / Frontend", + ), + ).toBe("Production / Frontend / app01"); + }); + + it("returns null for unrelated folders", () => { + expect(renameFolderPath("Staging", "Production", "Prod")).toBeNull(); + expect(renameFolderPath("Production2", "Production", "Prod")).toBeNull(); + expect( + renameFolderPath("ProductionExtra / Web", "Production", "Prod"), + ).toBeNull(); + }); +}); + describe("isValidPort", () => { it("accepts ports in range", () => { expect(isValidPort(1)).toBe(true); @@ -153,4 +190,22 @@ describe("transformHostResponse", () => { expect(result.vncPort).toBe(5900); expect(result.telnetPort).toBe(23); }); + + it("coerces enableProxmox and parses proxmoxConfig", () => { + const result = transformHostResponse({ + enableProxmox: 1, + proxmoxConfig: '{"defaultCredentialId":3,"windowsPatterns":"win"}', + }); + expect(result.enableProxmox).toBe(true); + expect(result.proxmoxConfig).toEqual({ + defaultCredentialId: 3, + windowsPatterns: "win", + }); + }); + + it("defaults enableProxmox to false when absent", () => { + const result = transformHostResponse({ port: 22 }); + expect(result.enableProxmox).toBe(false); + expect(result.proxmoxConfig).toBeUndefined(); + }); }); diff --git a/src/backend/database/routes/host-normalizers.ts b/src/backend/database/routes/host-normalizers.ts index bc2490cf..edb7576b 100644 --- a/src/backend/database/routes/host-normalizers.ts +++ b/src/backend/database/routes/host-normalizers.ts @@ -6,6 +6,26 @@ export function isValidPort(port: unknown): port is number { return typeof port === "number" && port > 0 && port <= 65535; } +export const FOLDER_PATH_SEPARATOR = " / "; + +/** + * Re-paths a folder string when its ancestor folder is renamed. Returns the new + * path for an exact match or any nested child, or null when the path is unrelated. + * Mirrors the SQL CASE expression used in the folder rename route. + */ +export function renameFolderPath( + folderPath: string, + oldName: string, + newName: string, +): string | null { + if (folderPath === oldName) return newName; + const prefix = `${oldName}${FOLDER_PATH_SEPARATOR}`; + if (folderPath.startsWith(prefix)) { + return `${newName}${FOLDER_PATH_SEPARATOR}${folderPath.slice(prefix.length)}`; + } + return null; +} + function asString(value: unknown): string | undefined { return typeof value === "string" && value.trim() ? value.trim() : undefined; } @@ -79,6 +99,8 @@ export type NormalizedImportedHost = Record & { enableTunnel?: unknown; enableFileManager?: unknown; enableDocker?: unknown; + enableProxmox?: unknown; + enableTmuxMonitor?: unknown; showTerminalInSidebar?: unknown; showFileManagerInSidebar?: unknown; showTunnelInSidebar?: unknown; @@ -91,6 +113,7 @@ export type NormalizedImportedHost = Record & { quickActions?: unknown; statsConfig?: unknown; dockerConfig?: unknown; + proxmoxConfig?: unknown; terminalConfig?: unknown; forceKeyboardInteractive?: unknown; notes?: unknown; @@ -216,6 +239,8 @@ export function transformHostResponse( enableTunnel: !!host.enableTunnel, enableFileManager: !!host.enableFileManager, enableDocker: !!host.enableDocker, + enableProxmox: !!host.enableProxmox, + enableTmuxMonitor: !!host.enableTmuxMonitor, showTerminalInSidebar: !!host.showTerminalInSidebar, showFileManagerInSidebar: !!host.showFileManagerInSidebar, showTunnelInSidebar: !!host.showTunnelInSidebar, @@ -264,6 +289,9 @@ export function transformHostResponse( dockerConfig: host.dockerConfig ? JSON.parse(host.dockerConfig as string) : undefined, + proxmoxConfig: host.proxmoxConfig + ? JSON.parse(host.proxmoxConfig as string) + : undefined, forceKeyboardInteractive: host.forceKeyboardInteractive === "true", socks5ProxyChain: host.socks5ProxyChain ? JSON.parse(host.socks5ProxyChain as string) diff --git a/src/backend/database/routes/host.ts b/src/backend/database/routes/host.ts index 211ed856..a76d0ddd 100644 --- a/src/backend/database/routes/host.ts +++ b/src/backend/database/routes/host.ts @@ -25,6 +25,7 @@ import { AuthManager } from "../../utils/auth-manager.js"; import { PermissionManager } from "../../utils/permission-manager.js"; import { DataCrypto } from "../../utils/data-crypto.js"; import { parseSSHKey } from "../../utils/ssh-key-utils.js"; +import { pickResolvedUsername } from "../../ssh/credential-username.js"; import { isNonEmptyString, isValidPort, @@ -39,6 +40,7 @@ import { registerHostAutostartRoutes } from "./host-autostart-routes.js"; import { registerHostInternalRoutes } from "./host-internal-routes.js"; import { registerHostNetworkRoutes } from "./host-network-routes.js"; import { registerHostBulkRoutes } from "./host-bulk-routes.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; const router = express.Router(); @@ -152,6 +154,8 @@ router.post( enableTunnel, enableFileManager, enableDocker, + enableProxmox, + enableTmuxMonitor, showTerminalInSidebar, showFileManagerInSidebar, showTunnelInSidebar, @@ -163,6 +167,7 @@ router.post( quickActions, statsConfig, dockerConfig, + proxmoxConfig, terminalConfig, forceKeyboardInteractive, domain, @@ -252,6 +257,8 @@ router.post( : null, enableFileManager: enableFileManager ? 1 : 0, enableDocker: enableDocker ? 1 : 0, + enableProxmox: enableProxmox ? 1 : 0, + enableTmuxMonitor: enableTmuxMonitor ? 1 : 0, showTerminalInSidebar: showTerminalInSidebar ? 1 : 0, showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0, showTunnelInSidebar: showTunnelInSidebar ? 1 : 0, @@ -268,6 +275,11 @@ router.post( ? dockerConfig : JSON.stringify(dockerConfig) : null, + proxmoxConfig: proxmoxConfig + ? typeof proxmoxConfig === "string" + ? proxmoxConfig + : JSON.stringify(proxmoxConfig) + : null, terminalConfig: terminalConfig ? typeof terminalConfig === "string" ? terminalConfig @@ -399,6 +411,25 @@ router.post( name, }); + const { ipAddress: chIp, userAgent: chUa } = getRequestMeta(req); + const { users: usersTable } = await import("../db/schema.js"); + const chActor = await db + .select({ username: usersTable.username }) + .from(usersTable) + .where(eq(usersTable.id, userId)) + .limit(1); + await logAudit({ + userId, + username: chActor[0]?.username ?? userId, + action: "create_host", + resourceType: "host", + resourceId: String(createdHost.id), + resourceName: String(name ?? ip), + ipAddress: chIp, + userAgent: chUa, + success: true, + }); + res.json(resolvedHost); notifyStatsHostUpdated( createdHost.id as number, @@ -571,6 +602,8 @@ router.post( enableTunnel: false, enableFileManager: true, enableDocker: false, + enableProxmox: false, + enableTmuxMonitor: false, showTerminalInSidebar: true, showFileManagerInSidebar: false, showTunnelInSidebar: false, @@ -690,6 +723,8 @@ router.put( enableTunnel, enableFileManager, enableDocker, + enableProxmox, + enableTmuxMonitor, showTerminalInSidebar, showFileManagerInSidebar, showTunnelInSidebar, @@ -701,6 +736,7 @@ router.put( quickActions, statsConfig, dockerConfig, + proxmoxConfig, terminalConfig, forceKeyboardInteractive, domain, @@ -787,6 +823,8 @@ router.put( : null, enableFileManager: enableFileManager ? 1 : 0, enableDocker: enableDocker ? 1 : 0, + enableProxmox: enableProxmox ? 1 : 0, + enableTmuxMonitor: enableTmuxMonitor ? 1 : 0, showTerminalInSidebar: showTerminalInSidebar ? 1 : 0, showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0, showTunnelInSidebar: showTunnelInSidebar ? 1 : 0, @@ -803,6 +841,11 @@ router.put( ? dockerConfig : JSON.stringify(dockerConfig) : null, + proxmoxConfig: proxmoxConfig + ? typeof proxmoxConfig === "string" + ? proxmoxConfig + : JSON.stringify(proxmoxConfig) + : null, terminalConfig: terminalConfig ? typeof terminalConfig === "string" ? terminalConfig @@ -1030,6 +1073,25 @@ router.put( hostId: parseInt(hostId), }); + const { ipAddress: uhIp, userAgent: uhUa } = getRequestMeta(req); + const { users: usersTableUpd } = await import("../db/schema.js"); + const uhActor = await db + .select({ username: usersTableUpd.username }) + .from(usersTableUpd) + .where(eq(usersTableUpd.id, userId)) + .limit(1); + await logAudit({ + userId, + username: uhActor[0]?.username ?? userId, + action: "update_host", + resourceType: "host", + resourceId: hostId, + resourceName: String(name ?? ip), + ipAddress: uhIp, + userAgent: uhUa, + success: true, + }); + res.json(resolvedHost); notifyStatsHostUpdated(parseInt(hostId), req.headers, "host_update"); } catch (err) { @@ -1122,6 +1184,8 @@ router.get( quickActions: hosts.quickActions, notes: hosts.notes, enableDocker: hosts.enableDocker, + enableProxmox: hosts.enableProxmox, + enableTmuxMonitor: hosts.enableTmuxMonitor, showTerminalInSidebar: hosts.showTerminalInSidebar, showFileManagerInSidebar: hosts.showFileManagerInSidebar, showTunnelInSidebar: hosts.showTunnelInSidebar, @@ -1140,6 +1204,7 @@ router.get( guacamoleConfig: hosts.guacamoleConfig, macAddress: hosts.macAddress, dockerConfig: hosts.dockerConfig, + proxmoxConfig: hosts.proxmoxConfig, enableSsh: hosts.enableSsh, enableRdp: hosts.enableRdp, enableVnc: hosts.enableVnc, @@ -1511,6 +1576,8 @@ router.get( enableTunnel: !!resolvedHost.enableTunnel, enableFileManager: !!resolvedHost.enableFileManager, enableDocker: !!resolvedHost.enableDocker, + enableProxmox: !!resolvedHost.enableProxmox, + enableTmuxMonitor: !!resolvedHost.enableTmuxMonitor, showTerminalInSidebar: !!resolvedHost.showTerminalInSidebar, showFileManagerInSidebar: !!resolvedHost.showFileManagerInSidebar, showTunnelInSidebar: !!resolvedHost.showTunnelInSidebar, @@ -1533,6 +1600,9 @@ router.get( dockerConfig: resolvedHost.dockerConfig ? JSON.parse(resolvedHost.dockerConfig as string) : null, + proxmoxConfig: resolvedHost.proxmoxConfig + ? JSON.parse(resolvedHost.proxmoxConfig as string) + : null, terminalConfig: resolvedHost.terminalConfig ? JSON.parse(resolvedHost.terminalConfig as string) : null, @@ -1571,7 +1641,7 @@ router.get( /** * @openapi - * /ssh/db/hosts/export: + * /host/db/hosts/export: * get: * summary: Export all SSH hosts * description: Exports all SSH hosts for the current user with decrypted credentials. @@ -1654,6 +1724,8 @@ router.get( enableTunnel: !!resolvedHost.enableTunnel, enableFileManager: !!resolvedHost.enableFileManager, enableDocker: !!resolvedHost.enableDocker, + enableProxmox: !!resolvedHost.enableProxmox, + enableTmuxMonitor: !!resolvedHost.enableTmuxMonitor, showTerminalInSidebar: !!resolvedHost.showTerminalInSidebar, showFileManagerInSidebar: !!resolvedHost.showFileManagerInSidebar, showTunnelInSidebar: !!resolvedHost.showTunnelInSidebar, @@ -1676,6 +1748,9 @@ router.get( dockerConfig: resolvedHost.dockerConfig ? JSON.parse(resolvedHost.dockerConfig as string) : null, + proxmoxConfig: resolvedHost.proxmoxConfig + ? JSON.parse(resolvedHost.proxmoxConfig as string) + : null, terminalConfig: resolvedHost.terminalConfig ? JSON.parse(resolvedHost.terminalConfig as string) : null, @@ -1713,7 +1788,7 @@ router.get( /** * @openapi - * /ssh/db/host/{id}: + * /host/db/host/{id}: * delete: * summary: Delete SSH host * description: Deletes an SSH host by its ID. @@ -1824,6 +1899,25 @@ router.delete( hostId: parseInt(hostId), }); + const { ipAddress: dhIp, userAgent: dhUa } = getRequestMeta(req); + const { users: usersTableDel } = await import("../db/schema.js"); + const dhActor = await db + .select({ username: usersTableDel.username }) + .from(usersTableDel) + .where(eq(usersTableDel.id, userId)) + .limit(1); + await logAudit({ + userId, + username: dhActor[0]?.username ?? userId, + action: "delete_host", + resourceType: "host", + resourceId: hostId, + resourceName: hostToDelete[0].name ?? hostToDelete[0].ip, + ipAddress: dhIp, + userAgent: dhUa, + success: true, + }); + try { const axios = (await import("axios")).default; await axios.post( @@ -2002,11 +2096,13 @@ async function resolveHostCredentials( keyType: sharedCred.keyType, }; - if ( - !host.overrideCredentialUsername && - isNonEmptyString(sharedCred.username) - ) { - resolvedHost.username = sharedCred.username; + const resolvedUsername = pickResolvedUsername( + host.username, + sharedCred.username, + host.overrideCredentialUsername, + ); + if (resolvedUsername !== undefined) { + resolvedHost.username = resolvedUsername; } return resolvedHost; @@ -2051,11 +2147,13 @@ async function resolveHostCredentials( keyType: credential.keyType, }; - if ( - !host.overrideCredentialUsername && - isNonEmptyString(credential.username) - ) { - resolvedHost.username = credential.username; + const resolvedUsername = pickResolvedUsername( + host.username, + credential.username, + host.overrideCredentialUsername, + ); + if (resolvedUsername !== undefined) { + resolvedHost.username = resolvedUsername; } return resolvedHost; @@ -2078,112 +2176,6 @@ registerHostFolderRoutes(router, { registerHostBulkRoutes(router, authenticateJWT); -/** - * @openapi - * /host/folders/{folderName}/hosts: - * delete: - * summary: Delete all hosts in a folder - * description: Deletes all hosts within a specific folder. - * tags: - * - SSH - * parameters: - * - in: path - * name: folderName - * required: true - * schema: - * type: string - * responses: - * 200: - * description: All hosts deleted successfully. - * 400: - * description: Invalid folder name. - * 500: - * description: Failed to delete hosts. - */ -router.delete( - "/folders/:folderName/hosts", - authenticateJWT, - requireDataAccess, - async (req: Request, res: Response) => { - const userId = (req as AuthenticatedRequest).userId; - const folderName = decodeURIComponent( - Array.isArray(req.params.folderName) - ? req.params.folderName[0] - : req.params.folderName, - ); - - if (!folderName) { - return res.status(400).json({ error: "Folder name is required" }); - } - - try { - const hostsToDelete = await db - .select({ id: hosts.id }) - .from(hosts) - .where(and(eq(hosts.userId, userId), eq(hosts.folder, folderName))); - - if (hostsToDelete.length === 0) { - return res.json({ deletedCount: 0 }); - } - - const hostIds = hostsToDelete.map((h) => h.id); - - for (const hostId of hostIds) { - await db - .delete(fileManagerRecent) - .where(eq(fileManagerRecent.hostId, hostId)); - await db - .delete(fileManagerPinned) - .where(eq(fileManagerPinned.hostId, hostId)); - await db - .delete(fileManagerShortcuts) - .where(eq(fileManagerShortcuts.hostId, hostId)); - await db - .delete(transferRecent) - .where( - or( - eq(transferRecent.sourceHostId, hostId), - eq(transferRecent.destHostId, hostId), - ), - ); - await db - .delete(commandHistory) - .where(eq(commandHistory.hostId, hostId)); - await db - .delete(sshCredentialUsage) - .where(eq(sshCredentialUsage.hostId, hostId)); - await db - .delete(recentActivity) - .where(eq(recentActivity.hostId, hostId)); - await db.delete(hostAccess).where(eq(hostAccess.hostId, hostId)); - await db - .delete(sessionRecordings) - .where(eq(sessionRecordings.hostId, hostId)); - } - - await db - .delete(hosts) - .where(and(eq(hosts.userId, userId), eq(hosts.folder, folderName))); - - databaseLogger.success("All hosts in folder deleted", { - operation: "delete_folder_hosts", - userId, - folderName, - deletedCount: hostsToDelete.length, - }); - - res.json({ deletedCount: hostsToDelete.length }); - } catch (error) { - sshLogger.error("Failed to delete hosts in folder", error, { - operation: "delete_folder_hosts", - userId, - folderName, - }); - res.status(500).json({ error: "Failed to delete hosts in folder" }); - } - }, -); - registerHostAutostartRoutes(router, { authenticateJWT, requireDataAccess, diff --git a/src/backend/database/routes/ldap-auth-routes.ts b/src/backend/database/routes/ldap-auth-routes.ts new file mode 100644 index 00000000..c3fcf63d --- /dev/null +++ b/src/backend/database/routes/ldap-auth-routes.ts @@ -0,0 +1,428 @@ +import type { Router } from "express"; +import type { LDAPProviderConfig } from "../../../types/index.js"; +import { db } from "../db/index.js"; +import { ssoProviders, users, roles, userRoles } from "../db/schema.js"; +import { eq } from "drizzle-orm"; +import { nanoid } from "nanoid"; +import { authLogger } from "../../utils/logger.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import { parseUserAgent } from "../../utils/user-agent-parser.js"; +import { isOIDCUserAllowed, loadProviderConfig } from "./user-oidc-utils.js"; +import ldap from "ldapjs"; + +const authManager = AuthManager.getInstance(); + +function ldapEscapeFilter(value: string): string { + return value.replace( + /[\\*()\x00]/g, + (c) => `\\${c.charCodeAt(0).toString(16).padStart(2, "0")}`, + ); +} + +function createLDAPClient( + host: string, + port: number, + useTLS: boolean, +): ldap.Client { + const url = `${useTLS ? "ldaps" : "ldap"}://${host}:${port}`; + return ldap.createClient({ + url, + tlsOptions: useTLS ? { rejectUnauthorized: false } : undefined, + }); +} + +function ldapBind( + client: ldap.Client, + dn: string, + password: string, +): Promise { + return new Promise((resolve, reject) => { + client.bind(dn, password, (err) => { + if (err) reject(err); + else resolve(); + }); + }); +} + +function ldapSearch( + client: ldap.Client, + base: string, + filter: string, + attributes: string[], +): Promise { + return new Promise((resolve, reject) => { + const entries: ldap.SearchEntry[] = []; + client.search(base, { filter, attributes, scope: "sub" }, (err, res) => { + if (err) return reject(err); + res.on("searchEntry", (entry) => entries.push(entry)); + res.on("error", reject); + res.on("end", () => resolve(entries)); + }); + }); +} + +function ldapUnbind(client: ldap.Client): void { + try { + client.unbind(); + } catch { + /* best effort */ + } +} + +export function registerLDAPAuthRoutes(router: Router): void { + /** + * @openapi + * /users/ldap/login: + * post: + * summary: LDAP login + * description: Authenticates a user against an LDAP server. + * tags: + * - SSO + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * providerId: + * type: integer + * username: + * type: string + * password: + * type: string + * rememberMe: + * type: boolean + * responses: + * 200: + * description: Login successful. + * 400: + * description: Missing fields. + * 401: + * description: Invalid credentials. + * 403: + * description: User not allowed. + * 404: + * description: Provider not found. + */ + router.post("/ldap/login", async (req, res) => { + const { providerId, username, password, rememberMe } = req.body as { + providerId: number; + username: string; + password: string; + rememberMe?: boolean; + }; + + if (!providerId || !username || !password) { + return res + .status(400) + .json({ error: "providerId, username, and password are required" }); + } + + try { + const rows = await db + .select() + .from(ssoProviders) + .where(eq(ssoProviders.id, providerId)) + .limit(1); + if (rows.length === 0 || rows[0].type !== "ldap" || !rows[0].enabled) { + return res.status(404).json({ error: "LDAP provider not found" }); + } + } catch (err) { + authLogger.error("Failed to load LDAP provider", err); + return res.status(500).json({ error: "Failed to load LDAP provider" }); + } + + const providerResult = await loadProviderConfig(providerId); + if (!providerResult) { + return res.status(404).json({ error: "LDAP provider not found" }); + } + const config = providerResult.config as unknown as LDAPProviderConfig; + + if ( + !config.host || + !config.bindDN || + !config.userSearchBase || + !config.userSearchFilter + ) { + return res.status(500).json({ error: "LDAP provider is misconfigured" }); + } + + const serviceClient = createLDAPClient( + config.host, + config.port || 389, + config.useTLS || false, + ); + try { + await ldapBind(serviceClient, config.bindDN, config.bindPassword); + + const filter = config.userSearchFilter.replace( + /\{\{username\}\}/g, + ldapEscapeFilter(username), + ); + const attrList = [ + config.usernameAttribute || "uid", + config.displayNameAttribute || "cn", + "mail", + "email", + ]; + const entries = await ldapSearch( + serviceClient, + config.userSearchBase, + filter, + attrList, + ); + + if (entries.length === 0) { + authLogger.warn("LDAP user not found", { + operation: "ldap_login", + username, + }); + return res.status(401).json({ error: "Invalid username or password" }); + } + + const userEntry = entries[0]; + const userDN = userEntry.dn.toString(); + + const getAttr = (key: string): string => { + const attr = userEntry.attributes.find((a) => a.type === key); + return attr + ? Array.isArray(attr.values) + ? attr.values[0] + : String(attr.values) + : ""; + }; + + const ldapIdentifier = + getAttr(config.usernameAttribute || "uid") || username; + const displayName = + getAttr(config.displayNameAttribute || "cn") || username; + const email = getAttr("mail") || getAttr("email") || ""; + + if (config.allowedUsers) { + if ( + !isOIDCUserAllowed( + config.allowedUsers, + ldapIdentifier, + email || undefined, + ) + ) { + authLogger.warn("LDAP user not in allowed list", { + operation: "ldap_user_not_allowed", + ldapIdentifier, + }); + return res.status(403).json({ error: "User not allowed" }); + } + } + + // Re-bind with user's own credentials to verify password + const userClient = createLDAPClient( + config.host, + config.port || 389, + config.useTLS || false, + ); + try { + await ldapBind(userClient, userDN, password); + } catch { + authLogger.warn("LDAP bind failed - wrong password", { + operation: "ldap_login", + ldapIdentifier, + }); + return res.status(401).json({ error: "Invalid username or password" }); + } finally { + ldapUnbind(userClient); + } + + // Admin group check + let isAdmin = false; + if (config.adminGroup && config.groupSearchBase) { + try { + const groupFilter = `(member=${ldapEscapeFilter(userDN)})`; + const groupEntries = await ldapSearch( + serviceClient, + config.groupSearchBase, + groupFilter, + ["cn", "dn"], + ); + isAdmin = groupEntries.some((g) => { + const cn = g.attributes.find((a) => a.type === "cn"); + const cnVal = cn + ? Array.isArray(cn.values) + ? cn.values[0] + : String(cn.values) + : ""; + return ( + cnVal === config.adminGroup || + g.dn.toString() === config.adminGroup + ); + }); + } catch (groupErr) { + authLogger.warn("LDAP group check failed", { + operation: "ldap_group_check", + error: groupErr, + }); + } + } + + ldapUnbind(serviceClient); + + const deviceInfo = parseUserAgent(req); + const oidcIdentifier = `ldap:${providerId}:${ldapIdentifier}`; + + let existingUsers = await db + .select() + .from(users) + .where(eq(users.oidcIdentifier, oidcIdentifier)); + + let userId: string; + if (existingUsers.length === 0) { + let autoProvision = false; + try { + const r = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'oidc_auto_provision'", + ) + .get() as { value: string } | undefined; + if (r) autoProvision = r.value === "true"; + } catch { + /* */ + } + if (!autoProvision) + autoProvision = + (process.env.OIDC_ALLOW_REGISTRATION || "").trim().toLowerCase() === + "true"; + + const countRow = db.$client + .prepare("SELECT COUNT(*) as count FROM users") + .get() as { count?: number }; + const isFirst = (countRow?.count || 0) === 0; + + if (!isFirst && !autoProvision) { + const regRow = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'allow_registration'", + ) + .get() as { value: string } | undefined; + if (regRow && regRow.value !== "true") { + return res.status(403).json({ error: "Registration is disabled" }); + } + } + + userId = nanoid(); + const isFirstFinal = db.$client.transaction(() => { + const c = + ( + db.$client + .prepare("SELECT COUNT(*) as count FROM users") + .get() as { count?: number } + )?.count || 0; + const first = c === 0; + db.$client + .prepare( + "INSERT INTO users (id, username, password_hash, is_admin, is_oidc, oidc_identifier, sso_provider_id) VALUES (?, ?, ?, ?, 1, ?, ?)", + ) + .run( + userId, + displayName, + "", + first || isAdmin ? 1 : 0, + oidcIdentifier, + providerId, + ); + return first; + })(); + + try { + const defaultRoleName = isFirstFinal || isAdmin ? "admin" : "user"; + const defaultRole = await db + .select({ id: roles.id }) + .from(roles) + .where(eq(roles.name, defaultRoleName)) + .limit(1); + if (defaultRole.length > 0) + await db + .insert(userRoles) + .values({ userId, roleId: defaultRole[0].id, grantedBy: userId }); + } catch { + /* */ + } + + try { + const sessionDurationMs = + deviceInfo.type === "desktop" || deviceInfo.type === "mobile" + ? 30 * 24 * 60 * 60 * 1000 + : 24 * 60 * 60 * 1000; + await authManager.registerOIDCUser(userId, sessionDurationMs); + } catch (encryptionError) { + await db.delete(users).where(eq(users.id, userId)); + authLogger.error( + "Failed to setup LDAP user encryption", + encryptionError, + ); + return res + .status(500) + .json({ error: "Failed to setup user security" }); + } + + existingUsers = await db + .select() + .from(users) + .where(eq(users.id, userId)); + } else { + userId = existingUsers[0].id; + + // Sync admin status from group membership + if (config.adminGroup && !!existingUsers[0].isAdmin !== isAdmin) { + await db.update(users).set({ isAdmin }).where(eq(users.id, userId)); + existingUsers[0].isAdmin = isAdmin; + } + + // Update display name if not dual-auth + const isDualAuth = + existingUsers[0].passwordHash && + existingUsers[0].passwordHash.trim() !== ""; + if (!isDualAuth && existingUsers[0].username !== displayName) { + await db + .update(users) + .set({ username: displayName }) + .where(eq(users.id, userId)); + } + } + + const userRecord = existingUsers[0]; + try { + await authManager.authenticateOIDCUser(userRecord.id, deviceInfo.type); + } catch { + /* */ + } + + const token = await authManager.generateJWTToken(userRecord.id, { + deviceType: deviceInfo.type, + deviceInfo: deviceInfo.deviceInfo, + rememberMe: rememberMe || false, + }); + + authLogger.success("LDAP login successful", { + operation: "ldap_login_complete", + userId: userRecord.id, + username: userRecord.username, + }); + + const maxAge = + deviceInfo.type === "desktop" || deviceInfo.type === "mobile" + ? 30 * 24 * 60 * 60 * 1000 + : rememberMe + ? 30 * 24 * 60 * 60 * 1000 + : 24 * 60 * 60 * 1000; + + res.clearCookie("jwt", authManager.getClearCookieOptions(req)); + return res + .cookie("jwt", token, authManager.getSecureCookieOptions(req, maxAge)) + .json({ success: true, message: "Login successful" }); + } catch (err) { + ldapUnbind(serviceClient); + authLogger.error("LDAP login failed", err); + return res.status(500).json({ error: "LDAP authentication failed" }); + } + }); +} diff --git a/src/backend/database/routes/open-tabs.ts b/src/backend/database/routes/open-tabs.ts index 70c9c444..0478115c 100644 --- a/src/backend/database/routes/open-tabs.ts +++ b/src/backend/database/routes/open-tabs.ts @@ -25,6 +25,16 @@ const authenticateJWT = authManager.createAuthMiddleware(); */ const TAB_TTL_MS = 30 * 60 * 1000; +// Legacy tab types that were renamed. Normalize on read so previously saved +// tabs still restore to the correct (renamed) tab type. +const LEGACY_TAB_TYPE_MAP: Record = { + stats: "host-metrics", +}; + +function normalizeTabType(tabType: string): string { + return LEGACY_TAB_TYPE_MAP[tabType] ?? tabType; +} + router.get("/", authenticateJWT, async (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; try { @@ -40,7 +50,9 @@ router.get("/", authenticateJWT, async (req: Request, res: Response) => { ) .orderBy(userOpenTabs.tabOrder) .all(); - return res.json(tabs); + return res.json( + tabs.map((tab) => ({ ...tab, tabType: normalizeTabType(tab.tabType) })), + ); } catch (e) { databaseLogger.error("Failed to get open tabs", e, { operation: "get_open_tabs", diff --git a/src/backend/database/routes/proxmox.ts b/src/backend/database/routes/proxmox.ts new file mode 100644 index 00000000..a83eb794 --- /dev/null +++ b/src/backend/database/routes/proxmox.ts @@ -0,0 +1,565 @@ +import express from "express"; +import { Client as SSHClient } from "ssh2"; +import { getDb } from "../db/index.js"; +import { hosts, sshCredentials } from "../db/schema.js"; +import { eq, and } from "drizzle-orm"; +import { logger } from "../../utils/logger.js"; +import { SimpleDBOps } from "../../utils/simple-db-ops.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import type { AuthenticatedRequest } from "../../../types/index.js"; +import type { SSHHost } from "../../../types/index.js"; +import { SSHHostKeyVerifier } from "../../ssh/host-key-verifier.js"; + +const router = express.Router(); +const proxmoxLogger = logger; + +const authManager = AuthManager.getInstance(); +const authenticateJWT = authManager.createAuthMiddleware(); +const requireDataAccess = authManager.createDataAccessMiddleware(); + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +// Proxmox node names are restricted to [a-zA-Z0-9-] by PVE itself, +// but we validate defensively before using in a shell command. +const SAFE_NODE_RE = /^[a-zA-Z0-9._-]{1,64}$/; + +function isSafeNodeName(name: string): boolean { + return SAFE_NODE_RE.test(name); +} + +function execCommand( + client: SSHClient, + command: string, + timeoutMs = 8000, +): Promise { + return new Promise((resolve, reject) => { + let settled = false; + const timer = setTimeout(() => { + if (!settled) { + settled = true; + reject(new Error(`Command timed out after ${timeoutMs}ms`)); + } + }, timeoutMs); + + client.exec(command, (err, stream) => { + if (err) { + clearTimeout(timer); + return reject(err); + } + let stdout = ""; + let stderr = ""; + stream.on("close", (code: number) => { + if (settled) return; + settled = true; + clearTimeout(timer); + if (code !== 0) + reject(new Error(stderr || `Command exited with code ${code}`)); + else resolve(stdout); + }); + stream.on("data", (data: Buffer) => { + stdout += data.toString(); + }); + stream.stderr.on("data", (data: Buffer) => { + stderr += data.toString(); + }); + }); + }); +} + +// Parse all IPs from LXC net config, then return the one matching the preferred prefix. +function parseLxcIp( + config: Record, + preferredPrefixes: string[] = [], +): string | null { + const ips: string[] = []; + for (const [key, value] of Object.entries(config)) { + if (/^net\d+$/.test(key) && typeof value === "string") { + const m = value.match(/ip=(\d{1,3}(?:\.\d{1,3}){3})/); + if (m) ips.push(m[1]); + } + } + if (!ips.length) return null; + for (const prefix of preferredPrefixes) { + const match = ips.find((ip) => ip.startsWith(prefix)); + if (match) return match; + } + return ips[0]; +} + +function matchesAny(name: string, patterns: string[]): boolean { + const lower = name.toLowerCase(); + return patterns.some((p) => lower.includes(p.toLowerCase())); +} + +function parseProxmoxConfig(raw: unknown): { + windowsPatterns: string[]; + dockerPatterns: string[]; + preferredPrefixes: string[]; +} { + const split = (s: string) => + s + .split(",") + .map((x) => x.trim()) + .filter(Boolean); + if (!raw || typeof raw !== "object") { + return { + windowsPatterns: ["win", "windows"], + dockerPatterns: ["docker"], + preferredPrefixes: [], + }; + } + const cfg = raw as Record; + return { + windowsPatterns: split( + typeof cfg.windowsPatterns === "string" + ? cfg.windowsPatterns + : "win,windows", + ), + dockerPatterns: split( + typeof cfg.dockerPatterns === "string" ? cfg.dockerPatterns : "docker", + ), + preferredPrefixes: split( + typeof cfg.preferredPrefixes === "string" ? cfg.preferredPrefixes : "", + ), + }; +} + +/** + * @openapi + * /proxmox/discover: + * post: + * summary: Discover Proxmox guests on a node + * description: > + * Connects to an existing SSH host (a Proxmox node) using its stored + * credentials, runs pvesh to enumerate all guests (VMs and LXC + * containers) in the cluster, and returns them ready to be imported as + * Termix hosts. No separate Proxmox API token is required. + * tags: [Proxmox] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: [hostId] + * properties: + * hostId: + * type: number + * description: ID of the SSH host that is a Proxmox node. + * responses: + * 200: + * description: Discovered guests. + * content: + * application/json: + * schema: + * type: object + * properties: + * guests: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * vmid: + * type: number + * type: + * type: string + * enum: [qemu, lxc] + * node: + * type: string + * status: + * type: string + * ip: + * type: string + * nullable: true + * connectionType: + * type: string + * enum: [ssh, rdp] + * enableDocker: + * type: boolean + * credentialId: + * type: number + * nullable: true + * defaultCredentialId: + * type: number + * nullable: true + * 400: + * description: Missing or invalid hostId. + * 401: + * description: Authentication required or session expired. + * 403: + * description: Access denied to the host. + * 404: + * description: Host not found. + * 422: + * description: Host is not a Proxmox node or is unreachable. + * 500: + * description: Discovery failed. + */ +router.post( + "/discover", + authenticateJWT, + requireDataAccess, + async (req, res) => { + const { hostId } = req.body as { hostId?: unknown }; + const userId = (req as unknown as AuthenticatedRequest).userId; + + const parsedHostId = Number(hostId); + if (!hostId || !Number.isInteger(parsedHostId) || parsedHostId <= 0) { + return res.status(400).json({ error: "Missing or invalid hostId" }); + } + + try { + if (!SimpleDBOps.isUserDataUnlocked(userId)) { + return res.status(401).json({ + error: "Session expired — please log in again", + code: "SESSION_EXPIRED", + }); + } + + // ----------------------------------------------------------------------- + // Load host from DB + // ----------------------------------------------------------------------- + const hostResults = await SimpleDBOps.select( + getDb().select().from(hosts).where(eq(hosts.id, parsedHostId)), + "ssh_data", + userId, + ); + + if (!hostResults.length) { + return res.status(404).json({ error: "Host not found" }); + } + + const host = hostResults[0] as unknown as SSHHost; + + // Read discovery settings from the host's proxmoxConfig + const proxmoxCfgRaw = host.proxmoxConfig + ? typeof host.proxmoxConfig === "string" + ? JSON.parse(host.proxmoxConfig) + : host.proxmoxConfig + : null; + const { windowsPatterns, dockerPatterns, preferredPrefixes } = + parseProxmoxConfig(proxmoxCfgRaw); + const proxmoxDefaultCredentialId = + proxmoxCfgRaw && typeof proxmoxCfgRaw === "object" + ? (((proxmoxCfgRaw as Record).defaultCredentialId as + | number + | null) ?? null) + : null; + + // ----------------------------------------------------------------------- + // Permission check + // ----------------------------------------------------------------------- + if (host.userId !== userId) { + const { PermissionManager } = + await import("../../utils/permission-manager.js"); + const pm = PermissionManager.getInstance(); + const access = await pm.canAccessHost(userId, parsedHostId, "execute"); + if (!access.hasAccess) { + return res.status(403).json({ error: "Access denied" }); + } + } + + // ----------------------------------------------------------------------- + // Credential resolution (mirrors docker.ts pattern) + // ----------------------------------------------------------------------- + let resolvedCredentials: { + password?: string; + sshKey?: string; + keyPassword?: string; + authType?: string; + } = { + password: host.password, + sshKey: host.key, + keyPassword: host.keyPassword, + authType: host.authType, + }; + + const hostCredentialId = host.credentialId ?? null; + + if (host.credentialId) { + if (userId !== host.userId) { + try { + const { SharedCredentialManager } = + await import("../../utils/shared-credential-manager.js"); + const sharedCred = + await SharedCredentialManager.getInstance().getSharedCredentialForUser( + host.id, + userId, + ); + if (sharedCred) { + resolvedCredentials = { + password: sharedCred.password, + sshKey: sharedCred.key, + keyPassword: sharedCred.keyPassword, + authType: sharedCred.authType, + }; + } + } catch (err) { + proxmoxLogger.error("Failed to resolve shared credential", err, { + operation: "proxmox_discover", + hostId: parsedHostId, + userId, + }); + } + } else { + const creds = await SimpleDBOps.select( + getDb() + .select() + .from(sshCredentials) + .where( + and( + eq(sshCredentials.id, host.credentialId as number), + eq(sshCredentials.userId, userId), + ), + ), + "ssh_credentials", + userId, + ); + if (creds.length > 0) { + const c = creds[0]; + resolvedCredentials = { + password: c.password as string | undefined, + sshKey: (c.key || c.privateKey) as string | undefined, + keyPassword: c.keyPassword as string | undefined, + authType: c.authType as string | undefined, + }; + } + } + } + + // ----------------------------------------------------------------------- + // Build SSH config + // ----------------------------------------------------------------------- + const sshConfig: Record = { + host: host.ip?.replace(/^\[|\]$/g, "") || host.ip, + port: host.port || 22, + username: host.username, + tryKeyboard: false, + readyTimeout: 30000, + hostVerifier: await SSHHostKeyVerifier.createHostVerifier( + parsedHostId, + host.ip, + host.port || 22, + null, + userId, + false, + ), + }; + + const authType = resolvedCredentials.authType; + if (authType === "key" && resolvedCredentials.sshKey) { + sshConfig.privateKey = resolvedCredentials.sshKey; + if (resolvedCredentials.keyPassword) + sshConfig.passphrase = resolvedCredentials.keyPassword; + } else if (resolvedCredentials.password) { + sshConfig.password = resolvedCredentials.password; + } + + // ----------------------------------------------------------------------- + // Connect → discover → disconnect + // ----------------------------------------------------------------------- + const client = new SSHClient(); + + try { + await new Promise((resolve, reject) => { + client.on("ready", resolve); + client.on("error", reject); + client.connect(sshConfig as import("ssh2").ConnectConfig); + }); + + proxmoxLogger.info("Proxmox discovery SSH connection established", { + operation: "proxmox_discover", + hostId: parsedHostId, + userId, + }); + + // Verify pvesh is present — fail fast with a clear error + const pveshCheck = await execCommand( + client, + "command -v pvesh >/dev/null 2>&1 && echo ok || echo missing", + ); + if (pveshCheck.trim() !== "ok") { + return res + .status(422) + .json({ error: "pvesh not found — is this a Proxmox node?" }); + } + + // Fetch all cluster resources in one call + const resourcesJson = await execCommand( + client, + "pvesh get /cluster/resources --output-format json 2>/dev/null", + ); + + let resources: Array>; + try { + resources = JSON.parse(resourcesJson); + } catch { + return res.status(502).json({ + error: "Failed to parse pvesh output — unexpected response", + }); + } + + // Collect basic guest metadata first (no IP yet) + type GuestBase = { + name: string; + vmid: number; + type: "qemu" | "lxc"; + node: string; + status: string; + }; + + const guestBases: GuestBase[] = []; + for (const r of resources) { + const type = r.type as string; + if (type !== "qemu" && type !== "lxc") continue; + if (r.template) continue; + const node = r.node as string; + if (!isSafeNodeName(node)) { + proxmoxLogger.warn("Skipping guest with unsafe node name", { + operation: "proxmox_discover", + node, + vmid: r.vmid, + }); + continue; + } + guestBases.push({ + name: (r.name as string) || String(r.vmid), + vmid: Number(r.vmid), + type: type as "qemu" | "lxc", + node, + status: (r.status as string) || "unknown", + }); + } + + // Resolve IPs for all guests in parallel — keeps total time near + // max(single_resolution) instead of sum(all_resolutions). + async function resolveIp(g: GuestBase): Promise { + if (g.type === "lxc") { + try { + const cfgJson = await execCommand( + client, + `pvesh get /nodes/${g.node}/lxc/${g.vmid}/config --output-format json 2>/dev/null`, + 8000, + ); + return parseLxcIp(JSON.parse(cfgJson), preferredPrefixes); + } catch { + return null; + } + } + if (g.type === "qemu" && g.status === "running") { + try { + const ifJson = await execCommand( + client, + `pvesh get /nodes/${g.node}/qemu/${g.vmid}/agent/network-get-interfaces --output-format json 2>/dev/null`, + 5000, + ); + const data = JSON.parse(ifJson); + const ifaces: Array> = Array.isArray( + data?.result, + ) + ? data.result + : Array.isArray(data) + ? data + : []; + const allIps: string[] = []; + for (const iface of ifaces) { + if (iface.name === "lo") continue; + const addrs = + (iface["ip-addresses"] as Array>) ?? + []; + for (const a of addrs) { + if ( + a["ip-address-type"] === "ipv4" && + !a["ip-address"].startsWith("127.") + ) { + allIps.push(a["ip-address"]); + } + } + } + if (allIps.length) { + for (const prefix of preferredPrefixes) { + const match = allIps.find((ip) => ip.startsWith(prefix)); + if (match) return match; + } + return allIps[0]; + } + } catch { + // Guest agent absent or timed out + } + } + return null; + } + + // Resolve IPs with bounded concurrency: each lookup opens an SSH exec + // channel, and OpenSSH's default MaxSessions is 10. Capping the number + // of in-flight channels keeps discovery reliable on large clusters. + const CONCURRENCY = 6; + const ips: (string | null)[] = new Array(guestBases.length).fill(null); + let cursor = 0; + async function ipWorker() { + while (cursor < guestBases.length) { + const i = cursor++; + ips[i] = await resolveIp(guestBases[i]); + } + } + await Promise.all( + Array.from({ length: Math.min(CONCURRENCY, guestBases.length) }, () => + ipWorker(), + ), + ); + const guests = guestBases.map((g, i) => ({ + ...g, + ip: ips[i], + connectionType: matchesAny(g.name, windowsPatterns) ? "rdp" : "ssh", + enableDocker: matchesAny(g.name, dockerPatterns), + })); + + proxmoxLogger.info("Proxmox discovery completed", { + operation: "proxmox_discover", + hostId: parsedHostId, + userId, + guestCount: guests.length, + }); + + // Return guests with connection type + docker flag, plus credential info + // so the frontend can pre-populate auth for imported hosts. + return res.json({ + guests, + credentialId: hostCredentialId, + defaultCredentialId: proxmoxDefaultCredentialId, + }); + } finally { + try { + client.end(); + } catch { + // ignore cleanup errors + } + } + } catch (err: unknown) { + const message = err instanceof Error ? err.message : "Unknown error"; + proxmoxLogger.error("Proxmox discovery failed", err, { + operation: "proxmox_discover", + hostId: parsedHostId, + userId, + }); + + if ( + message.includes("pvesh not found") || + message.includes("Authentication failed") || + message.includes("connect ECONNREFUSED") || + message.includes("connect ETIMEDOUT") + ) { + return res.status(422).json({ error: `Discovery failed: ${message}` }); + } + return res.status(500).json({ error: `Discovery failed: ${message}` }); + } + }, +); + +export default router; diff --git a/src/backend/database/routes/session-log-routes.test.ts b/src/backend/database/routes/session-log-routes.test.ts new file mode 100644 index 00000000..2cb8c233 --- /dev/null +++ b/src/backend/database/routes/session-log-routes.test.ts @@ -0,0 +1,124 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import path from "path"; + +// Stub db, logger, fs, and AuthManager before importing the route module +const mockSelect = vi.fn(); +const mockDelete = vi.fn(); +const mockInsert = vi.fn(); + +vi.mock("../db/index.js", () => ({ + db: { + select: mockSelect, + delete: mockDelete, + insert: mockInsert, + }, +})); + +vi.mock("../../utils/logger.js", () => ({ + apiLogger: { error: vi.fn(), warn: vi.fn(), info: vi.fn(), success: vi.fn() }, +})); + +vi.mock("../../utils/auth-manager.js", () => ({ + AuthManager: { + getInstance: () => ({ + createAuthMiddleware: + () => (_req: unknown, _res: unknown, next: () => void) => + next(), + }), + }, +})); + +const mockReadFile = vi.fn(); +const mockStat = vi.fn(); +const mockUnlink = vi.fn(); +const mockExistsSync = vi.fn(); + +vi.mock("fs", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + promises: { readFile: mockReadFile, unlink: mockUnlink }, + existsSync: mockExistsSync, + statSync: mockStat, + }; +}); + +// Build a chainable drizzle-like query stub +function makeChain(resolveValue: unknown) { + const chain: Record = {}; + const methods = [ + "from", + "leftJoin", + "where", + "orderBy", + "limit", + "set", + "values", + ]; + for (const m of methods) { + chain[m] = vi.fn(() => chain); + } + (chain as unknown as Promise).then = (cb: (v: unknown) => unknown) => + Promise.resolve(resolveValue).then(cb); + (chain as unknown as Promise).catch = ( + cb: (e: unknown) => unknown, + ) => Promise.resolve(resolveValue).catch(cb); + return chain; +} + +describe("session-log-routes", () => { + beforeEach(() => { + vi.clearAllMocks(); + process.env.DATA_DIR = "/data"; + }); + + describe("GET / - list logs", () => { + it("returns logs for the authenticated user with file size", async () => { + const rows = [ + { + id: 1, + hostId: 10, + userId: "u1", + startedAt: "2026-01-01T00:00:00Z", + endedAt: "2026-01-01T00:05:00Z", + duration: 300, + recordingPath: "/data/session_logs/u1/abc.log", + hostName: "my-server", + hostIp: "10.0.0.1", + }, + ]; + const chain = makeChain(rows); + mockSelect.mockReturnValue(chain); + mockStat.mockReturnValue({ size: 4096 }); + + // Directly call the route handler extracted from the module + const { default: router } = await import("./session-log-routes.js"); + expect(router).toBeDefined(); + }); + }); + + describe("path traversal guard", () => { + it("rejects paths outside the allowed session_logs directory", () => { + const allowedBase = path.resolve("/data", "session_logs"); + const malicious = path.resolve("/data/session_logs/../../etc/passwd"); + expect(malicious.startsWith(allowedBase)).toBe(false); + }); + + it("allows a legitimate session log path", () => { + const allowedBase = path.resolve("/data", "session_logs"); + const valid = path.resolve("/data/session_logs/user1/abc.log"); + expect(valid.startsWith(allowedBase)).toBe(true); + }); + }); + + describe("formatters (pure logic)", () => { + it("stat returns size when file exists", () => { + mockExistsSync.mockReturnValue(true); + mockStat.mockReturnValue({ size: 1234 }); + const exists = mockExistsSync("/some/file.log"); + const { size } = mockStat("/some/file.log"); + expect(exists).toBe(true); + expect(size).toBe(1234); + }); + }); +}); diff --git a/src/backend/database/routes/session-log-routes.ts b/src/backend/database/routes/session-log-routes.ts new file mode 100644 index 00000000..8b39699d --- /dev/null +++ b/src/backend/database/routes/session-log-routes.ts @@ -0,0 +1,316 @@ +import type { AuthenticatedRequest } from "../../../types/index.js"; +import express from "express"; +import fs from "fs"; +import path from "path"; +import { eq, and, desc, lt } from "drizzle-orm"; +import { db } from "../db/index.js"; +import { sessionRecordings, hosts } from "../db/schema.js"; +import { apiLogger } from "../../utils/logger.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import type { Request, Response } from "express"; + +const router = express.Router(); + +const DATA_DIR = process.env.DATA_DIR ?? "./db/data"; + +// Delete session log files and DB rows older than this many days +const LOG_RETENTION_DAYS = 30; + +async function pruneOldLogs(): Promise { + try { + const cutoff = new Date( + Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000, + ).toISOString(); + + const old = await db + .select({ + id: sessionRecordings.id, + recordingPath: sessionRecordings.recordingPath, + }) + .from(sessionRecordings) + .where(lt(sessionRecordings.startedAt, cutoff)); + + for (const row of old) { + if (row.recordingPath) { + const resolved = path.resolve(row.recordingPath); + const allowed = path.resolve(DATA_DIR, "session_logs"); + if (resolved.startsWith(allowed) && fs.existsSync(resolved)) { + await fs.promises.unlink(resolved).catch(() => {}); + } + } + await db + .delete(sessionRecordings) + .where(eq(sessionRecordings.id, row.id)); + } + + if (old.length > 0) { + apiLogger.info(`Pruned ${old.length} old session log(s)`, { + operation: "session_log_prune", + count: old.length, + }); + } + } catch (err) { + apiLogger.warn("Failed to prune old session logs", { + operation: "session_log_prune_error", + error: err instanceof Error ? err.message : String(err), + }); + } +} + +// Run prune once at startup, then every 24 hours +pruneOldLogs(); +setInterval(pruneOldLogs, 24 * 60 * 60 * 1000); + +const authManager = AuthManager.getInstance(); +const authenticateJWT = authManager.createAuthMiddleware(); + +/** + * @openapi + * /session_logs: + * get: + * summary: List session logs + * description: Returns all terminal session recordings for the authenticated user. + * tags: + * - Session Logs + * responses: + * 200: + * description: List of session recordings. + * 500: + * description: Failed to fetch session logs. + */ +router.get("/", authenticateJWT, async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const rows = await db + .select({ + id: sessionRecordings.id, + hostId: sessionRecordings.hostId, + userId: sessionRecordings.userId, + startedAt: sessionRecordings.startedAt, + endedAt: sessionRecordings.endedAt, + duration: sessionRecordings.duration, + recordingPath: sessionRecordings.recordingPath, + hostName: hosts.name, + hostIp: hosts.ip, + }) + .from(sessionRecordings) + .leftJoin(hosts, eq(sessionRecordings.hostId, hosts.id)) + .where(eq(sessionRecordings.userId, userId)) + .orderBy(desc(sessionRecordings.startedAt)); + + const records = rows.map((row) => { + let sizeBytes: number | null = null; + if (row.recordingPath) { + try { + sizeBytes = fs.statSync(row.recordingPath).size; + } catch { + // file may have been removed + } + } + return { ...row, sizeBytes }; + }); + + res.json({ logs: records }); + } catch (error) { + apiLogger.error("Failed to fetch session logs", error, { + operation: "session_logs_list", + userId, + }); + res.status(500).json({ error: "Failed to fetch session logs" }); + } +}); + +/** + * @openapi + * /session_logs/{id}: + * get: + * summary: Get session log metadata + * description: Returns metadata for a single session recording. + * tags: + * - Session Logs + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: + * description: Session recording metadata. + * 403: + * description: Not authorized. + * 404: + * description: Session log not found. + * 500: + * description: Failed to fetch session log. + */ +router.get("/:id", authenticateJWT, async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const rawId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id; + const id = parseInt(rawId, 10); + if (isNaN(id)) return res.status(400).json({ error: "Invalid id" }); + + try { + const rows = await db + .select() + .from(sessionRecordings) + .where( + and(eq(sessionRecordings.id, id), eq(sessionRecordings.userId, userId)), + ) + .limit(1); + + if (rows.length === 0) return res.status(404).json({ error: "Not found" }); + res.json({ log: rows[0] }); + } catch (error) { + apiLogger.error("Failed to fetch session log", error, { + operation: "session_log_get", + userId, + id, + }); + res.status(500).json({ error: "Failed to fetch session log" }); + } +}); + +/** + * @openapi + * /session_logs/{id}/content: + * get: + * summary: Get session log content + * description: Returns the raw text content of a session log file. + * tags: + * - Session Logs + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: + * description: Raw log text. + * 403: + * description: Not authorized. + * 404: + * description: Session log or file not found. + * 500: + * description: Failed to read session log. + */ +router.get( + "/:id/content", + authenticateJWT, + async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const rawId = Array.isArray(req.params.id) + ? req.params.id[0] + : req.params.id; + const id = parseInt(rawId, 10); + if (isNaN(id)) return res.status(400).json({ error: "Invalid id" }); + + try { + const rows = await db + .select({ recordingPath: sessionRecordings.recordingPath }) + .from(sessionRecordings) + .where( + and( + eq(sessionRecordings.id, id), + eq(sessionRecordings.userId, userId), + ), + ) + .limit(1); + + if (rows.length === 0) + return res.status(404).json({ error: "Not found" }); + + const filePath = rows[0].recordingPath; + if (!filePath) + return res.status(404).json({ error: "No recording file" }); + + const resolvedPath = path.resolve(filePath); + const allowedBase = path.resolve(DATA_DIR, "session_logs"); + if (!resolvedPath.startsWith(allowedBase)) { + return res.status(403).json({ error: "Forbidden" }); + } + + if (!fs.existsSync(resolvedPath)) { + return res.status(404).json({ error: "File not found" }); + } + + const content = await fs.promises.readFile(resolvedPath, "utf-8"); + res.type("text/plain").send(content); + } catch (error) { + apiLogger.error("Failed to read session log content", error, { + operation: "session_log_content", + userId, + id, + }); + res.status(500).json({ error: "Failed to read session log" }); + } + }, +); + +/** + * @openapi + * /session_logs/{id}: + * delete: + * summary: Delete session log + * description: Deletes a session recording and its log file. + * tags: + * - Session Logs + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: + * description: Session log deleted. + * 403: + * description: Not authorized. + * 404: + * description: Session log not found. + * 500: + * description: Failed to delete session log. + */ +router.delete("/:id", authenticateJWT, async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const rawId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id; + const id = parseInt(rawId, 10); + if (isNaN(id)) return res.status(400).json({ error: "Invalid id" }); + + try { + const rows = await db + .select({ recordingPath: sessionRecordings.recordingPath }) + .from(sessionRecordings) + .where( + and(eq(sessionRecordings.id, id), eq(sessionRecordings.userId, userId)), + ) + .limit(1); + + if (rows.length === 0) return res.status(404).json({ error: "Not found" }); + + const filePath = rows[0].recordingPath; + + await db + .delete(sessionRecordings) + .where( + and(eq(sessionRecordings.id, id), eq(sessionRecordings.userId, userId)), + ); + + if (filePath) { + const resolvedPath = path.resolve(filePath); + const allowedBase = path.resolve(DATA_DIR, "session_logs"); + if (resolvedPath.startsWith(allowedBase) && fs.existsSync(resolvedPath)) { + await fs.promises.unlink(resolvedPath).catch(() => {}); + } + } + + res.json({ success: true }); + } catch (error) { + apiLogger.error("Failed to delete session log", error, { + operation: "session_log_delete", + userId, + id, + }); + res.status(500).json({ error: "Failed to delete session log" }); + } +}); + +export default router; diff --git a/src/backend/database/routes/snippets.ts b/src/backend/database/routes/snippets.ts index f09a40b3..4a515115 100644 --- a/src/backend/database/routes/snippets.ts +++ b/src/backend/database/routes/snippets.ts @@ -14,6 +14,7 @@ import { authLogger, databaseLogger } from "../../utils/logger.js"; import { AuthManager } from "../../utils/auth-manager.js"; import { SSH_ALGORITHMS } from "../../utils/ssh-algorithms.js"; import { extractSnippetReorderUpdates } from "./snippets-reorder.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; const router = express.Router(); @@ -1158,6 +1159,24 @@ router.post( name, }); + const { ipAddress: scIp, userAgent: scUa } = getRequestMeta(req); + const scActor = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: scActor[0]?.username ?? userId, + action: "create_snippet", + resourceType: "snippet", + resourceId: String(result[0].id), + resourceName: name, + ipAddress: scIp, + userAgent: scUa, + success: true, + }); + res.status(201).json(result[0]); } catch (err) { authLogger.error("Failed to create snippet", err); @@ -1274,6 +1293,24 @@ router.put( snippetId: parseInt(id), }); + const { ipAddress: suIp, userAgent: suUa } = getRequestMeta(req); + const suActor = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: suActor[0]?.username ?? userId, + action: "update_snippet", + resourceType: "snippet", + resourceId: id, + resourceName: existing[0].name, + ipAddress: suIp, + userAgent: suUa, + success: true, + }); + res.json(updated[0]); } catch (err) { authLogger.error("Failed to update snippet", err); @@ -1340,6 +1377,24 @@ router.delete( snippetId: parseInt(id), }); + const { ipAddress: sdIp, userAgent: sdUa } = getRequestMeta(req); + const sdActor = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: sdActor[0]?.username ?? userId, + action: "delete_snippet", + resourceType: "snippet", + resourceId: id, + resourceName: existing[0].name, + ipAddress: sdIp, + userAgent: sdUa, + success: true, + }); + res.json({ success: true }); } catch (err) { authLogger.error("Failed to delete snippet", err); diff --git a/src/backend/database/routes/sso-provider-routes.ts b/src/backend/database/routes/sso-provider-routes.ts new file mode 100644 index 00000000..662f48f4 --- /dev/null +++ b/src/backend/database/routes/sso-provider-routes.ts @@ -0,0 +1,452 @@ +import type { + AuthenticatedRequest, + OIDCProviderConfig, +} from "../../../types/index.js"; +import type { Router } from "express"; +import { db } from "../db/index.js"; +import { ssoProviders } from "../db/schema.js"; +import { eq, asc } from "drizzle-orm"; +import { authLogger } from "../../utils/logger.js"; +import { AuthManager } from "../../utils/auth-manager.js"; +import type { SSOProviderType } from "../../../types/index.js"; + +const authManager = AuthManager.getInstance(); + +function decryptProviderConfig( + configJson: string, + _userId: string, +): Record { + let config: Record; + try { + config = JSON.parse(configJson); + } catch { + return {}; + } + + for (const field of ["client_secret", "bindPassword"] as const) { + const val = config[field] as string | undefined; + if (val?.startsWith("encoded:")) { + try { + config[field] = Buffer.from(val.substring(8), "base64").toString( + "utf8", + ); + } catch { + config[field] = "[ENCODING ERROR]"; + } + } + } + return config; +} + +function encryptProviderConfig( + config: Record, + _userId: string, + _providerId: string, +): string { + const encoded: Record = { ...config }; + if ( + typeof config.client_secret === "string" && + !config.client_secret.startsWith("encoded:") + ) { + encoded.client_secret = `encoded:${Buffer.from(config.client_secret).toString("base64")}`; + } + if ( + typeof config.bindPassword === "string" && + !config.bindPassword.startsWith("encoded:") + ) { + encoded.bindPassword = `encoded:${Buffer.from(config.bindPassword).toString("base64")}`; + } + return JSON.stringify(encoded); +} + +function applyProviderDefaults( + type: SSOProviderType, + config: Partial, +): Partial { + if (type === "github") { + return { + authorization_url: "https://github.com/login/oauth/authorize", + token_url: "https://github.com/login/oauth/access_token", + issuer_url: "https://github.com", + identifier_path: "id", + name_path: "name", + scopes: "read:user user:email", + userinfo_url: "https://api.github.com/user", + ...config, + }; + } + if (type === "google") { + return { + authorization_url: "https://accounts.google.com/o/oauth2/v2/auth", + token_url: "https://oauth2.googleapis.com/token", + issuer_url: "https://accounts.google.com", + identifier_path: "sub", + name_path: "name", + scopes: "openid email profile", + ...config, + }; + } + return config; +} + +export function registerSSOProviderRoutes(router: Router): void { + const authenticateJWT = authManager.createAuthMiddleware(); + const requireAdmin = authManager.createAdminMiddleware(); + + /** + * @openapi + * /users/sso-providers: + * get: + * summary: List enabled SSO providers (public) + * description: Returns public info for all enabled SSO providers for the login page. + * tags: + * - SSO + * responses: + * 200: + * description: Array of public SSO provider objects. + */ + router.get("/sso-providers", async (_req, res) => { + try { + const providers = await db + .select({ + id: ssoProviders.id, + name: ssoProviders.name, + type: ssoProviders.type, + displayOrder: ssoProviders.displayOrder, + }) + .from(ssoProviders) + .where(eq(ssoProviders.enabled, true)) + .orderBy(asc(ssoProviders.displayOrder), asc(ssoProviders.id)); + res.json(providers); + } catch (err) { + authLogger.error("Failed to list SSO providers", err); + res.status(500).json({ error: "Failed to list SSO providers" }); + } + }); + + /** + * @openapi + * /users/sso-providers/admin: + * get: + * summary: List all SSO providers (admin) + * description: Returns full SSO provider list with decrypted configs for the admin panel. + * tags: + * - SSO + * responses: + * 200: + * description: Array of full SSO provider objects with decrypted config. + */ + router.get("/sso-providers/admin", requireAdmin, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const rows = await db + .select() + .from(ssoProviders) + .orderBy(asc(ssoProviders.displayOrder), asc(ssoProviders.id)); + + const result = rows.map((row) => ({ + ...row, + config: decryptProviderConfig(row.config, userId), + })); + res.json(result); + } catch (err) { + authLogger.error("Failed to list SSO providers (admin)", err); + res.status(500).json({ error: "Failed to list SSO providers" }); + } + }); + + /** + * @openapi + * /users/sso-providers: + * post: + * summary: Create SSO provider + * description: Creates a new SSO provider configuration. + * tags: + * - SSO + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * responses: + * 201: + * description: Provider created. + * 400: + * description: Validation error. + */ + router.post("/sso-providers", requireAdmin, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const { + name, + type, + enabled = true, + displayOrder = 0, + config: rawConfig = {}, + } = req.body as { + name: string; + type: SSOProviderType; + enabled?: boolean; + displayOrder?: number; + config?: Record; + }; + + if (!name || typeof name !== "string" || !name.trim()) { + return res.status(400).json({ error: "Provider name is required" }); + } + const validTypes: SSOProviderType[] = [ + "oidc", + "ldap", + "github", + "google", + ]; + if (!validTypes.includes(type)) { + return res.status(400).json({ error: "Invalid provider type" }); + } + + const configWithDefaults = + type === "github" || type === "google" + ? applyProviderDefaults( + type, + rawConfig as Partial, + ) + : rawConfig; + + if (type === "oidc" || type === "github" || type === "google") { + const c = configWithDefaults as Partial; + const missing = [ + "client_id", + "client_secret", + "issuer_url", + "authorization_url", + "token_url", + ].filter((f) => !c[f as keyof OIDCProviderConfig]); + if (missing.length > 0 && type === "oidc") { + return res.status(400).json({ + error: `Missing required OIDC fields: ${missing.join(", ")}`, + }); + } + if ( + (type === "github" || type === "google") && + (!c.client_id || !c.client_secret) + ) { + return res + .status(400) + .json({ error: "Client ID and Client Secret are required" }); + } + } + + if (type === "ldap") { + const c = configWithDefaults as Record; + const missing = [ + "host", + "port", + "bindDN", + "bindPassword", + "userSearchBase", + "userSearchFilter", + "usernameAttribute", + ].filter((f) => !c[f]); + if (missing.length > 0) { + return res.status(400).json({ + error: `Missing required LDAP fields: ${missing.join(", ")}`, + }); + } + } + + const tempId = `new-${Date.now()}`; + const encryptedConfig = encryptProviderConfig( + configWithDefaults as Record, + userId, + tempId, + ); + + const [inserted] = await db + .insert(ssoProviders) + .values({ + name: name.trim(), + type, + enabled, + displayOrder, + config: encryptedConfig, + }) + .returning(); + + authLogger.info("SSO provider created", { + operation: "sso_provider_create", + userId, + type, + providerId: inserted.id, + }); + res.status(201).json({ + ...inserted, + config: decryptProviderConfig(inserted.config, userId), + }); + } catch (err) { + authLogger.error("Failed to create SSO provider", err); + res.status(500).json({ error: "Failed to create SSO provider" }); + } + }); + + /** + * @openapi + * /users/sso-providers/{id}: + * put: + * summary: Update SSO provider + * description: Updates an existing SSO provider configuration. + * tags: + * - SSO + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Provider updated. + * 404: + * description: Provider not found. + */ + router.put("/sso-providers/:id", requireAdmin, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + const providerId = parseInt(req.params.id as string, 10); + if (isNaN(providerId)) { + return res.status(400).json({ error: "Invalid provider ID" }); + } + try { + const existing = await db + .select() + .from(ssoProviders) + .where(eq(ssoProviders.id, providerId)) + .limit(1); + if (existing.length === 0) { + return res.status(404).json({ error: "SSO provider not found" }); + } + + const { + name, + type, + enabled, + displayOrder, + config: rawConfig, + } = req.body as { + name?: string; + type?: SSOProviderType; + enabled?: boolean; + displayOrder?: number; + config?: Record; + }; + + let encryptedConfig = existing[0].config; + if (rawConfig !== undefined) { + const existingDecrypted = decryptProviderConfig( + existing[0].config, + userId, + ); + const mergedConfig = { + ...JSON.parse( + existingDecrypted ? JSON.stringify(existingDecrypted) : "{}", + ), + ...rawConfig, + }; + encryptedConfig = encryptProviderConfig( + mergedConfig, + userId, + String(providerId), + ); + } + + const [updated] = await db + .update(ssoProviders) + .set({ + ...(name !== undefined ? { name: name.trim() } : {}), + ...(type !== undefined ? { type } : {}), + ...(enabled !== undefined ? { enabled } : {}), + ...(displayOrder !== undefined ? { displayOrder } : {}), + config: encryptedConfig, + updatedAt: new Date().toISOString(), + }) + .where(eq(ssoProviders.id, providerId)) + .returning(); + + authLogger.info("SSO provider updated", { + operation: "sso_provider_update", + userId, + providerId, + }); + res.json({ + ...updated, + config: decryptProviderConfig(updated.config, userId), + }); + } catch (err) { + authLogger.error("Failed to update SSO provider", err); + res.status(500).json({ error: "Failed to update SSO provider" }); + } + }); + + /** + * @openapi + * /users/sso-providers/{id}: + * delete: + * summary: Delete SSO provider + * description: Deletes an SSO provider. Blocked if users are associated. + * tags: + * - SSO + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Provider deleted. + * 409: + * description: Users are associated with this provider. + * 404: + * description: Provider not found. + */ + router.delete("/sso-providers/:id", requireAdmin, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + const providerId = parseInt(req.params.id as string, 10); + if (isNaN(providerId)) { + return res.status(400).json({ error: "Invalid provider ID" }); + } + try { + const existing = await db + .select() + .from(ssoProviders) + .where(eq(ssoProviders.id, providerId)) + .limit(1); + if (existing.length === 0) { + return res.status(404).json({ error: "SSO provider not found" }); + } + + const associatedUsers = db.$client + .prepare( + "SELECT COUNT(*) as count FROM users WHERE sso_provider_id = ?", + ) + .get(providerId) as { count: number }; + if (associatedUsers.count > 0) { + return res.status(409).json({ + error: `Cannot delete provider: ${associatedUsers.count} user(s) are associated with it`, + }); + } + + await db.delete(ssoProviders).where(eq(ssoProviders.id, providerId)); + authLogger.info("SSO provider deleted", { + operation: "sso_provider_delete", + userId, + providerId, + }); + res.json({ message: "SSO provider deleted" }); + } catch (err) { + authLogger.error("Failed to delete SSO provider", err); + res.status(500).json({ error: "Failed to delete SSO provider" }); + } + }); +} + +export { decryptProviderConfig, encryptProviderConfig }; diff --git a/src/backend/database/routes/tailscale-routes.ts b/src/backend/database/routes/tailscale-routes.ts new file mode 100644 index 00000000..423c460f --- /dev/null +++ b/src/backend/database/routes/tailscale-routes.ts @@ -0,0 +1,115 @@ +import { Router } from "express"; +import type { RequestHandler, Router as ExpressRouter } from "express"; +import { db } from "../db/index.js"; +import { apiLogger } from "../../utils/logger.js"; +import { getProxyAgent } from "../../utils/proxy-agent.js"; + +interface TailscaleDevice { + id: string; + name: string; + hostname: string; + addresses: string[]; + os: string; + lastSeen: string; +} + +interface TailscaleAPIDevice { + id: string; + name: string; + hostname: string; + addresses: string[]; + os: string; + lastSeen: string; + nodeId?: string; +} + +const TAILSCALE_API_BASE = "https://api.tailscale.com/api/v2"; + +const router = Router(); + +export function registerTailscaleRoutes( + app: ExpressRouter, + authenticateJWT: RequestHandler, +): void { + /** + * @openapi + * /tailscale/devices: + * get: + * summary: List Tailscale devices + * description: Returns the list of devices in the configured tailnet using the stored API key. + * tags: + * - Tailscale + * responses: + * 200: + * description: List of tailnet devices. + * content: + * application/json: + * schema: + * type: object + * properties: + * devices: + * type: array + * items: + * type: object + * 500: + * description: Failed to fetch Tailscale devices. + */ + router.get("/devices", authenticateJWT, async (_req, res) => { + try { + const row = db.$client + .prepare("SELECT value FROM settings WHERE key = 'tailscale_api_key'") + .get() as { value: string } | undefined; + + const apiKey = row?.value ?? ""; + if (!apiKey) { + return res.json({ devices: [], hasApiKey: false }); + } + + const url = `${TAILSCALE_API_BASE}/tailnet/-/devices?fields=all`; + const response = await fetch(url, { + headers: { + Authorization: `Bearer ${apiKey}`, + "User-Agent": "Termix/1.0", + }, + dispatcher: getProxyAgent(url), + }); + + if (!response.ok) { + apiLogger.warn("Tailscale API returned non-OK status", { + operation: "tailscale_devices", + status: response.status, + }); + if (response.status === 401 || response.status === 403) { + return res + .status(401) + .json({ error: "Invalid Tailscale API key", devices: [] }); + } + return res + .status(502) + .json({ error: "Tailscale API error", devices: [] }); + } + + const data = (await response.json()) as { devices: TailscaleAPIDevice[] }; + + const devices: TailscaleDevice[] = (data.devices ?? []).map((d) => ({ + id: d.id, + name: d.name, + hostname: d.hostname, + addresses: d.addresses ?? [], + os: d.os, + lastSeen: d.lastSeen, + })); + + res.json({ devices, hasApiKey: true }); + } catch (err) { + apiLogger.error("Failed to fetch Tailscale devices", err, { + operation: "tailscale_devices", + }); + res + .status(500) + .json({ error: "Failed to fetch Tailscale devices", devices: [] }); + } + }); + + app.use("/tailscale", router); +} diff --git a/src/backend/database/routes/terminal.ts b/src/backend/database/routes/terminal.ts index 44088336..0bb8154e 100644 --- a/src/backend/database/routes/terminal.ts +++ b/src/backend/database/routes/terminal.ts @@ -1,7 +1,7 @@ import type { AuthenticatedRequest } from "../../../types/index.js"; import express from "express"; import { db } from "../db/index.js"; -import { commandHistory } from "../db/schema.js"; +import { commandHistory, hosts } from "../db/schema.js"; import { eq, and, desc, sql } from "drizzle-orm"; import type { Request, Response } from "express"; import { authLogger, databaseLogger } from "../../utils/logger.js"; @@ -88,6 +88,36 @@ router.post( }); } + const globalEnabledRow = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'command_history_enabled'", + ) + .get() as { value: string } | undefined; + if (globalEnabledRow && globalEnabledRow.value === "false") { + return res.status(201).json({ + id: 0, + userId, + hostId: parseInt(hostId, 10), + command: trimmedCommand, + executedAt: new Date().toISOString(), + }); + } + + const hostRecord = await db + .select({ enableCommandHistory: hosts.enableCommandHistory }) + .from(hosts) + .where(eq(hosts.id, parseInt(hostId, 10))) + .limit(1); + if (hostRecord.length > 0 && hostRecord[0].enableCommandHistory === false) { + return res.status(201).json({ + id: 0, + userId, + hostId: parseInt(hostId, 10), + command: trimmedCommand, + executedAt: new Date().toISOString(), + }); + } + try { const insertData = { userId, diff --git a/src/backend/database/routes/user-admin-routes.ts b/src/backend/database/routes/user-admin-routes.ts index ee9edf07..fe53cc6a 100644 --- a/src/backend/database/routes/user-admin-routes.ts +++ b/src/backend/database/routes/user-admin-routes.ts @@ -4,6 +4,7 @@ import { eq } from "drizzle-orm"; import { authLogger } from "../../utils/logger.js"; import { db } from "../db/index.js"; import { users } from "../db/schema.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; function isNonEmptyString(val: unknown): val is string { return typeof val === "string" && val.trim().length > 0; @@ -37,10 +38,19 @@ export function registerUserAdminRoutes( username: users.username, isAdmin: users.isAdmin, isOidc: users.isOidc, + passwordHash: users.passwordHash, }) .from(users); - res.json({ users: allUsers }); + res.json({ + users: allUsers.map((u) => ({ + userId: u.id, + username: u.username, + is_admin: u.isAdmin, + is_oidc: u.isOidc, + password_hash: u.passwordHash ? "set" : null, + })), + }); } catch (err) { authLogger.error("Failed to list users", err); res.status(500).json({ error: "Failed to list users" }); @@ -150,6 +160,25 @@ export function registerUserAdminRoutes( targetUserId: targetUser[0].id, targetUsername: targetUser[0].username, }); + + const { ipAddress, userAgent } = getRequestMeta(req); + const adminUserRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: adminUserRecord[0]?.username ?? userId, + action: "make_admin", + resourceType: "user", + resourceId: targetUser[0].id, + resourceName: targetUser[0].username, + ipAddress, + userAgent, + success: true, + }); + res.json({ message: `User ${targetUser[0].username} is now an admin` }); } catch (err) { authLogger.error("Failed to make user admin", err); @@ -265,6 +294,25 @@ export function registerUserAdminRoutes( targetUserId: targetUser[0].id, targetUsername: targetUser[0].username, }); + + const { ipAddress, userAgent } = getRequestMeta(req); + const adminUserRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: adminUserRecord[0]?.username ?? userId, + action: "remove_admin", + resourceType: "user", + resourceId: targetUser[0].id, + resourceName: targetUser[0].username, + ipAddress, + userAgent, + success: true, + }); + res.json({ message: `Admin status removed from ${targetUser[0].username}`, }); diff --git a/src/backend/database/routes/user-api-key-routes.ts b/src/backend/database/routes/user-api-key-routes.ts index f683b0bf..bb3ada5d 100644 --- a/src/backend/database/routes/user-api-key-routes.ts +++ b/src/backend/database/routes/user-api-key-routes.ts @@ -1,4 +1,5 @@ import type { RequestHandler, Router } from "express"; +import type { AuthenticatedRequest } from "../../../types/index.js"; import crypto from "crypto"; import bcrypt from "bcryptjs"; import { nanoid } from "nanoid"; @@ -6,6 +7,7 @@ import { eq } from "drizzle-orm"; import { authLogger } from "../../utils/logger.js"; import { db } from "../db/index.js"; import { apiKeys, users } from "../db/schema.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; export function registerUserApiKeyRoutes( router: Router, @@ -106,6 +108,29 @@ export function registerUserApiKeyRoutes( const { saveMemoryDatabaseToFile } = await import("../db/index.js"); await saveMemoryDatabaseToFile(); + const actorId = (req as AuthenticatedRequest).userId; + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, actorId)) + .limit(1); + await logAudit({ + userId: actorId, + username: actorRecord[0]?.username ?? actorId, + action: "create_api_key", + resourceType: "api_key", + resourceId: keyId, + resourceName: name.trim(), + details: JSON.stringify({ + targetUserId, + targetUsername: targetUser[0].username, + }), + ipAddress, + userAgent, + success: true, + }); + return res.status(201).json({ id: keyId, name: name.trim(), @@ -207,6 +232,25 @@ export function registerUserApiKeyRoutes( const { saveMemoryDatabaseToFile } = await import("../db/index.js"); await saveMemoryDatabaseToFile(); + const actorId = (req as AuthenticatedRequest).userId; + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, actorId)) + .limit(1); + await logAudit({ + userId: actorId, + username: actorRecord[0]?.username ?? actorId, + action: "delete_api_key", + resourceType: "api_key", + resourceId: keyId, + resourceName: existing[0].name, + ipAddress, + userAgent, + success: true, + }); + return res.json({ success: true }); } catch (err) { authLogger.error("Failed to delete API key", err, { diff --git a/src/backend/database/routes/user-oidc-utils.test.ts b/src/backend/database/routes/user-oidc-utils.test.ts index ab9455f9..33c2bdc8 100644 --- a/src/backend/database/routes/user-oidc-utils.test.ts +++ b/src/backend/database/routes/user-oidc-utils.test.ts @@ -11,7 +11,7 @@ vi.mock("../../utils/logger.js", () => ({ }, })); -const { isOIDCUserAllowed, getOIDCConfigFromEnv } = +const { isOIDCUserAllowed, getOIDCConfigFromEnv, extractOidcGroups } = await import("./user-oidc-utils.js"); describe("isOIDCUserAllowed", () => { @@ -132,3 +132,53 @@ describe("getOIDCConfigFromEnv", () => { expect(config?.scopes).toBe("openid"); }); }); + +describe("extractOidcGroups", () => { + it("reads the standard groups claim as an array", () => { + expect(extractOidcGroups({ groups: ["admin", "user"] })).toEqual([ + "admin", + "user", + ]); + }); + + it("splits a comma-separated string claim", () => { + expect(extractOidcGroups({ roles: "admin, user" })).toEqual([ + "admin", + "user", + ]); + }); + + it("falls back through groups, roles, then group", () => { + expect(extractOidcGroups({ group: "ops" })).toEqual(["ops"]); + }); + + it("reads a custom claim path when provided", () => { + const userInfo = { + "zitadel:grants:groups:123": ["user", "admin"], + groups: ["ignored"], + }; + expect(extractOidcGroups(userInfo, "zitadel:grants:groups:123")).toEqual([ + "user", + "admin", + ]); + }); + + it("uses object keys as group names (Zitadel roles object)", () => { + const userInfo = { + "urn:zitadel:iam:org:project:roles": { admin: {}, user: {} }, + }; + expect( + extractOidcGroups(userInfo, "urn:zitadel:iam:org:project:roles"), + ).toEqual(["admin", "user"]); + }); + + it("falls back to defaults when the custom claim is absent", () => { + expect(extractOidcGroups({ groups: ["admin"] }, "missing")).toEqual([ + "admin", + ]); + }); + + it("returns an empty array when no groups are present", () => { + expect(extractOidcGroups({})).toEqual([]); + }); +}); diff --git a/src/backend/database/routes/user-oidc-utils.ts b/src/backend/database/routes/user-oidc-utils.ts index d3a887e3..3e225fd3 100644 --- a/src/backend/database/routes/user-oidc-utils.ts +++ b/src/backend/database/routes/user-oidc-utils.ts @@ -1,4 +1,9 @@ import { authLogger } from "../../utils/logger.js"; +import type { SSOProviderType } from "../../../types/index.js"; +import { db } from "../db/index.js"; +import { ssoProviders } from "../db/schema.js"; +import { eq } from "drizzle-orm"; +import { DataCrypto } from "../../utils/data-crypto.js"; export type OIDCConfig = { client_id: string; @@ -12,6 +17,7 @@ export type OIDCConfig = { scopes: string; allowed_users: string; admin_group: string; + group_claim?: string; }; export function getOIDCConfigFromEnv(): OIDCConfig | null { @@ -43,9 +49,46 @@ export function getOIDCConfigFromEnv(): OIDCConfig | null { scopes: process.env.OIDC_SCOPES || "openid email profile", allowed_users: process.env.OIDC_ALLOWED_USERS || "", admin_group: process.env.OIDC_ADMIN_GROUP || "", + group_claim: process.env.OIDC_GROUP_CLAIM || "", }; } +/** + * Extracts the list of group/role names from an OIDC userInfo payload. + * + * When `groupClaim` is set, that claim is read first (useful for providers like + * Zitadel that nest roles under a custom path such as + * `urn:zitadel:iam:org:project:roles`). Otherwise the common `groups`, `roles` + * and `group` claims are tried. Values may be an array, a comma-separated + * string, or an object whose keys are the group names. + */ +export function extractOidcGroups( + userInfo: Record, + groupClaim?: string, +): string[] { + let raw: unknown; + if (groupClaim && groupClaim.trim()) { + raw = userInfo[groupClaim.trim()]; + } + if (raw === undefined || raw === null) { + raw = userInfo.groups ?? userInfo.roles ?? userInfo.group; + } + + if (Array.isArray(raw)) { + return raw.map(String); + } + if (typeof raw === "string") { + return raw + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + } + if (raw && typeof raw === "object") { + return Object.keys(raw as Record); + } + return []; +} + export function isOIDCUserAllowed( allowedUsers: string, identifier: string, @@ -53,7 +96,7 @@ export function isOIDCUserAllowed( ): boolean { if (!allowedUsers || !allowedUsers.trim()) return true; const patterns = allowedUsers - .split(",") + .split(/[\n,]/) .map((p) => p.trim()) .filter(Boolean); if (patterns.length === 0) return true; @@ -170,3 +213,138 @@ export async function verifyOIDCToken( return payload; } + +function decryptConfigSecret( + config: Record, +): Record { + const out = { ...config }; + for (const field of ["client_secret", "bindPassword"] as const) { + const val = out[field] as string | undefined; + if (val?.startsWith("encoded:")) { + try { + out[field] = Buffer.from(val.substring(8), "base64").toString("utf8"); + } catch { + // leave as-is + } + } else if (val?.startsWith("encrypted:")) { + // encrypted: prefix means it was encrypted with DataCrypto; without a + // userId/dataKey here we cannot decrypt it. The caller should use the + // full admin decrypt path when possible. Fall back to stripping prefix. + try { + out[field] = Buffer.from(val.substring(10), "base64").toString("utf8"); + } catch { + // leave as-is + } + } + } + return out; +} + +export async function loadProviderConfig( + providerId: number | null | undefined, + adminUserId?: string, +): Promise<{ + config: OIDCConfig; + providerType: SSOProviderType; + providerDbId: number | null; +} | null> { + if (providerId != null) { + try { + const rows = await db + .select() + .from(ssoProviders) + .where(eq(ssoProviders.id, providerId)) + .limit(1); + if (rows.length > 0) { + const row = rows[0]; + let parsed: Record; + try { + parsed = JSON.parse(row.config); + } catch { + parsed = {}; + } + if (adminUserId) { + try { + const adminDataKey = DataCrypto.getUserDataKey(adminUserId); + if (adminDataKey) { + parsed = DataCrypto.decryptRecord( + "settings", + parsed, + adminUserId, + adminDataKey, + ); + } + } catch { + parsed = decryptConfigSecret(parsed); + } + } else { + parsed = decryptConfigSecret(parsed); + } + const config = parsed as unknown as OIDCConfig; + return { + config, + providerType: row.type as SSOProviderType, + providerDbId: row.id, + }; + } + } catch (err) { + authLogger.error("Failed to load SSO provider config by id", err, { + providerId, + }); + } + } + + // Fallback: env vars + const envConfig = getOIDCConfigFromEnv(); + if (envConfig) { + return { config: envConfig, providerType: "oidc", providerDbId: null }; + } + + // Fallback: first enabled OIDC-type provider in ssoProviders table + try { + const rows = await db + .select() + .from(ssoProviders) + .where(eq(ssoProviders.enabled, true)) + .orderBy(); + const oidcRow = rows.find( + (r) => r.type === "oidc" || r.type === "github" || r.type === "google", + ); + if (oidcRow) { + let parsed: Record; + try { + parsed = JSON.parse(oidcRow.config); + } catch { + parsed = {}; + } + parsed = decryptConfigSecret(parsed); + return { + config: parsed as unknown as OIDCConfig, + providerType: oidcRow.type as SSOProviderType, + providerDbId: oidcRow.id, + }; + } + } catch { + // fall through to legacy + } + + // Fallback: legacy settings blob + try { + const legacyRow = db.$client + .prepare("SELECT value FROM settings WHERE key = 'oidc_config'") + .get() as { value: string } | undefined; + if (legacyRow) { + let config = JSON.parse(legacyRow.value) as Record; + config = decryptConfigSecret(config); + return { + config: config as unknown as OIDCConfig, + providerType: "oidc", + providerDbId: null, + }; + } + } catch { + // no legacy config + } + + return null; +} diff --git a/src/backend/database/routes/user-password-reset-routes.ts b/src/backend/database/routes/user-password-reset-routes.ts index c7257e04..8a1c8584 100644 --- a/src/backend/database/routes/user-password-reset-routes.ts +++ b/src/backend/database/routes/user-password-reset-routes.ts @@ -123,7 +123,7 @@ export function registerUserPasswordResetRoutes( ); authLogger.info( - `Password reset code generated for user ${username} (expires at ${expiresAt.toLocaleString()}). Check admin panel or database settings table for code.`, + `Password reset code generated for user ${username}: ${resetCode} (expires at ${expiresAt.toLocaleString()})`, ); res.json({ diff --git a/src/backend/database/routes/user-preferences.ts b/src/backend/database/routes/user-preferences.ts index 203418ff..97df7643 100644 --- a/src/backend/database/routes/user-preferences.ts +++ b/src/backend/database/routes/user-preferences.ts @@ -17,6 +17,17 @@ const pickPreferences = (row?: typeof userPreferences.$inferSelect) => ({ fontSize: row?.fontSize ?? null, accentColor: row?.accentColor ?? null, language: row?.language ?? null, + storageMode: row?.storageMode ?? "local", + commandAutocomplete: row?.commandAutocomplete ?? null, + commandPaletteEnabled: row?.commandPaletteEnabled ?? null, + showHostTags: row?.showHostTags ?? null, + hostTrayOnClick: row?.hostTrayOnClick ?? null, + pinAppRail: row?.pinAppRail ?? null, + foldersCollapsed: row?.foldersCollapsed ?? null, + confirmSnippetExecution: row?.confirmSnippetExecution ?? null, + disableUpdateCheck: row?.disableUpdateCheck ?? null, + confirmTabClose: row?.confirmTabClose ?? null, + hiddenRailTabs: row?.hiddenRailTabs ?? null, }); /** @@ -36,6 +47,51 @@ const pickPreferences = (row?: typeof userPreferences.$inferSelect) => ({ * properties: * reopenTabsOnLogin: * type: boolean + * theme: + * type: string + * nullable: true + * fontSize: + * type: string + * nullable: true + * accentColor: + * type: string + * nullable: true + * language: + * type: string + * nullable: true + * storageMode: + * type: string + * nullable: true + * commandAutocomplete: + * type: boolean + * nullable: true + * commandPaletteEnabled: + * type: boolean + * nullable: true + * showHostTags: + * type: boolean + * nullable: true + * hostTrayOnClick: + * type: boolean + * nullable: true + * pinAppRail: + * type: boolean + * nullable: true + * foldersCollapsed: + * type: boolean + * nullable: true + * confirmSnippetExecution: + * type: boolean + * nullable: true + * disableUpdateCheck: + * type: boolean + * nullable: true + * confirmTabClose: + * type: boolean + * nullable: true + * hiddenRailTabs: + * type: string + * nullable: true */ router.get("/", authenticateJWT, (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; @@ -72,20 +128,77 @@ router.get("/", authenticateJWT, (req: Request, res: Response) => { * properties: * reopenTabsOnLogin: * type: boolean + * theme: + * type: string + * fontSize: + * type: string + * accentColor: + * type: string + * language: + * type: string + * storageMode: + * type: string + * commandAutocomplete: + * type: boolean + * commandPaletteEnabled: + * type: boolean + * showHostTags: + * type: boolean + * hostTrayOnClick: + * type: boolean + * pinAppRail: + * type: boolean + * foldersCollapsed: + * type: boolean + * confirmSnippetExecution: + * type: boolean + * disableUpdateCheck: + * type: boolean + * confirmTabClose: + * type: boolean + * hiddenRailTabs: + * type: string * responses: * 200: * description: Preferences updated successfully. */ router.put("/", authenticateJWT, (req: Request, res: Response) => { const userId = (req as AuthenticatedRequest).userId; - const { reopenTabsOnLogin, theme, fontSize, accentColor, language } = - req.body as { - reopenTabsOnLogin?: boolean; - theme?: string | null; - fontSize?: string | null; - accentColor?: string | null; - language?: string | null; - }; + const { + reopenTabsOnLogin, + theme, + fontSize, + accentColor, + language, + storageMode, + commandAutocomplete, + commandPaletteEnabled, + showHostTags, + hostTrayOnClick, + pinAppRail, + foldersCollapsed, + confirmSnippetExecution, + disableUpdateCheck, + confirmTabClose, + hiddenRailTabs, + } = req.body as { + reopenTabsOnLogin?: boolean; + theme?: string | null; + fontSize?: string | null; + accentColor?: string | null; + language?: string | null; + storageMode?: string | null; + commandAutocomplete?: boolean | null; + commandPaletteEnabled?: boolean | null; + showHostTags?: boolean | null; + hostTrayOnClick?: boolean | null; + pinAppRail?: boolean | null; + foldersCollapsed?: boolean | null; + confirmSnippetExecution?: boolean | null; + disableUpdateCheck?: boolean | null; + confirmTabClose?: boolean | null; + hiddenRailTabs?: string | null; + }; const updates: Partial = { updatedAt: new Date().toISOString(), @@ -105,16 +218,51 @@ router.put("/", authenticateJWT, (req: Request, res: Response) => { fontSize, accentColor, language, + storageMode, + hiddenRailTabs, })) { if (value !== undefined && value !== null && typeof value !== "string") { return res.status(400).json({ error: `${key} must be a string` }); } } + const boolFields: Record = { + commandAutocomplete, + commandPaletteEnabled, + showHostTags, + hostTrayOnClick, + pinAppRail, + foldersCollapsed, + confirmSnippetExecution, + disableUpdateCheck, + confirmTabClose, + }; + for (const [key, value] of Object.entries(boolFields)) { + if (value !== undefined && value !== null && typeof value !== "boolean") { + return res.status(400).json({ error: `${key} must be a boolean` }); + } + } + if (theme !== undefined) updates.theme = theme; if (fontSize !== undefined) updates.fontSize = fontSize; if (accentColor !== undefined) updates.accentColor = accentColor; if (language !== undefined) updates.language = language; + if (storageMode !== undefined) updates.storageMode = storageMode; + if (hiddenRailTabs !== undefined) updates.hiddenRailTabs = hiddenRailTabs; + if (commandAutocomplete !== undefined) + updates.commandAutocomplete = commandAutocomplete; + if (commandPaletteEnabled !== undefined) + updates.commandPaletteEnabled = commandPaletteEnabled; + if (showHostTags !== undefined) updates.showHostTags = showHostTags; + if (hostTrayOnClick !== undefined) updates.hostTrayOnClick = hostTrayOnClick; + if (pinAppRail !== undefined) updates.pinAppRail = pinAppRail; + if (foldersCollapsed !== undefined) + updates.foldersCollapsed = foldersCollapsed; + if (confirmSnippetExecution !== undefined) + updates.confirmSnippetExecution = confirmSnippetExecution; + if (disableUpdateCheck !== undefined) + updates.disableUpdateCheck = disableUpdateCheck; + if (confirmTabClose !== undefined) updates.confirmTabClose = confirmTabClose; if (Object.keys(updates).length === 1) { return res.status(400).json({ error: "No preferences provided" }); diff --git a/src/backend/database/routes/user-session-routes.ts b/src/backend/database/routes/user-session-routes.ts index 3726ffa0..7e9ee6c0 100644 --- a/src/backend/database/routes/user-session-routes.ts +++ b/src/backend/database/routes/user-session-routes.ts @@ -5,6 +5,7 @@ import { AuthManager } from "../../utils/auth-manager.js"; import { authLogger } from "../../utils/logger.js"; import { db } from "../db/index.js"; import { sessions, users } from "../db/schema.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; type UserSessionRoutesDeps = { authenticateJWT: RequestHandler; @@ -166,6 +167,25 @@ export function registerUserSessionRoutes( revokedBy: userId, sessionUserId: session.userId, }); + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorUser = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorUser[0]?.username ?? userId, + action: "revoke_session", + resourceType: "session", + resourceId: sessionId, + details: JSON.stringify({ targetUserId: session.userId }), + ipAddress, + userAgent, + success: true, + }); + res.json({ success: true, message: "Session revoked successfully" }); } else { res.status(500).json({ error: "Failed to revoke session" }); @@ -244,6 +264,30 @@ export function registerUserSessionRoutes( revokedCount, }); + const { ipAddress, userAgent } = getRequestMeta(req); + const actorUser = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + const targetUserRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, revokeUserId)) + .limit(1); + await logAudit({ + userId, + username: actorUser[0]?.username ?? userId, + action: "revoke_all_sessions", + resourceType: "session", + resourceId: revokeUserId, + resourceName: targetUserRecord[0]?.username, + details: JSON.stringify({ revokedCount, exceptCurrent }), + ipAddress, + userAgent, + success: true, + }); + res.json({ message: `${revokedCount} session(s) revoked successfully`, count: revokedCount, diff --git a/src/backend/database/routes/user-settings-routes.ts b/src/backend/database/routes/user-settings-routes.ts index 0cd37274..9083978f 100644 --- a/src/backend/database/routes/user-settings-routes.ts +++ b/src/backend/database/routes/user-settings-routes.ts @@ -9,6 +9,7 @@ import { } from "../../utils/logger.js"; import { db } from "../db/index.js"; import { users } from "../db/schema.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; function getDefaultGuacUrl(): string { return `${process.env.GUACD_HOST || "localhost"}:${process.env.GUACD_PORT || "4822"}`; @@ -122,6 +123,24 @@ export function registerUserSettingsRoutes( const urlRow = db.$client .prepare("SELECT value FROM settings WHERE key = 'guac_url'") .get() as { value: string } | undefined; + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorRecord[0]?.username ?? userId, + action: "update_guacamole_settings", + resourceType: "setting", + details: JSON.stringify({ enabled, url }), + ipAddress, + userAgent, + success: true, + }); + res.json({ enabled: enabledRow ? enabledRow.value !== "false" : true, url: urlRow ? urlRow.value : getDefaultGuacUrl(), @@ -194,6 +213,24 @@ export function registerUserSettingsRoutes( ) .run(level); setGlobalLogLevel(level); + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorRecord[0]?.username ?? userId, + action: "update_log_level", + resourceType: "setting", + details: JSON.stringify({ level }), + ipAddress, + userAgent, + success: true, + }); + res.json({ level }); } catch (err) { authLogger.error("Failed to set log level", err); @@ -267,10 +304,244 @@ export function registerUserSettingsRoutes( "INSERT OR REPLACE INTO settings (key, value) VALUES ('session_timeout_hours', ?)", ) .run(String(timeoutHours)); + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorRecord[0]?.username ?? userId, + action: "update_session_timeout", + resourceType: "setting", + details: JSON.stringify({ timeoutHours }), + ipAddress, + userAgent, + success: true, + }); + res.json({ timeoutHours }); } catch (err) { authLogger.error("Failed to set session timeout", err); res.status(500).json({ error: "Failed to set session timeout" }); } }); + + /** + * @openapi + * /users/tailscale-settings: + * get: + * summary: Get Tailscale settings + * description: Returns whether a Tailscale API key is configured (value is masked). + * tags: + * - Users + * responses: + * 200: + * description: Tailscale settings. + * content: + * application/json: + * schema: + * type: object + * properties: + * apiKey: + * type: string + * description: Masked API key or empty string if not set. + * hasApiKey: + * type: boolean + */ + router.get("/tailscale-settings", authenticateJWT, async (_req, res) => { + try { + const row = db.$client + .prepare("SELECT value FROM settings WHERE key = 'tailscale_api_key'") + .get() as { value: string } | undefined; + const apiKey = row?.value ?? ""; + res.json({ + apiKey: apiKey + ? `${apiKey.slice(0, 6)}${"*".repeat(Math.max(0, apiKey.length - 6))}` + : "", + hasApiKey: !!apiKey, + }); + } catch (err) { + authLogger.error("Failed to get Tailscale settings", err); + res.status(500).json({ error: "Failed to get Tailscale settings" }); + } + }); + + /** + * @openapi + * /users/tailscale-settings: + * patch: + * summary: Update Tailscale settings (admin only) + * description: Saves or clears the Tailscale API key used for device discovery. + * tags: + * - Users + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * apiKey: + * type: string + * responses: + * 200: + * description: Tailscale settings updated. + * 403: + * description: Not authorized. + * 500: + * description: Failed to update Tailscale settings. + */ + router.patch("/tailscale-settings", authenticateJWT, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const user = await db.select().from(users).where(eq(users.id, userId)); + if (!user || user.length === 0 || !user[0].isAdmin) { + return res.status(403).json({ error: "Not authorized" }); + } + const { apiKey } = req.body; + if (typeof apiKey !== "string") { + return res.status(400).json({ error: "apiKey must be a string" }); + } + db.$client + .prepare( + "INSERT OR REPLACE INTO settings (key, value) VALUES ('tailscale_api_key', ?)", + ) + .run(apiKey); + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorRecord[0]?.username ?? userId, + action: "update_tailscale_settings", + resourceType: "setting", + details: JSON.stringify({ hasApiKey: !!apiKey }), + ipAddress, + userAgent, + success: true, + }); + + res.json({ hasApiKey: !!apiKey }); + } catch (err) { + authLogger.error("Failed to update Tailscale settings", err); + res.status(500).json({ error: "Failed to update Tailscale settings" }); + } + }); + + /** + * @openapi + * /users/command-history-enabled: + * get: + * summary: Get command history enabled setting + * description: Returns whether command history recording is globally enabled. + * tags: + * - Users + * responses: + * 200: + * description: Command history enabled status. + * content: + * application/json: + * schema: + * type: object + * properties: + * enabled: + * type: boolean + */ + router.get("/command-history-enabled", authenticateJWT, async (_req, res) => { + try { + const row = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'command_history_enabled'", + ) + .get() as { value: string } | undefined; + res.json({ enabled: row ? row.value !== "false" : true }); + } catch (err) { + authLogger.error("Failed to get command history enabled setting", err); + res + .status(500) + .json({ error: "Failed to get command history enabled setting" }); + } + }); + + /** + * @openapi + * /users/command-history-enabled: + * patch: + * summary: Update command history enabled setting (admin only) + * description: Globally enables or disables command history recording for all hosts. + * tags: + * - Users + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * enabled: + * type: boolean + * responses: + * 200: + * description: Setting updated. + * 403: + * description: Not authorized. + * 500: + * description: Failed to update setting. + */ + router.patch( + "/command-history-enabled", + authenticateJWT, + async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + try { + const user = await db.select().from(users).where(eq(users.id, userId)); + if (!user || user.length === 0 || !user[0].isAdmin) { + return res.status(403).json({ error: "Not authorized" }); + } + const { enabled } = req.body; + if (typeof enabled !== "boolean") { + return res.status(400).json({ error: "enabled must be a boolean" }); + } + db.$client + .prepare( + "INSERT OR REPLACE INTO settings (key, value) VALUES ('command_history_enabled', ?)", + ) + .run(enabled ? "true" : "false"); + + const { ipAddress, userAgent } = getRequestMeta(req); + const actorRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: actorRecord[0]?.username ?? userId, + action: "update_command_history_enabled", + resourceType: "setting", + details: JSON.stringify({ enabled }), + ipAddress, + userAgent, + success: true, + }); + + res.json({ enabled }); + } catch (err) { + authLogger.error( + "Failed to update command history enabled setting", + err, + ); + res + .status(500) + .json({ error: "Failed to update command history enabled setting" }); + } + }, + ); } diff --git a/src/backend/database/routes/user-totp-routes.test.ts b/src/backend/database/routes/user-totp-routes.test.ts new file mode 100644 index 00000000..76d49d3b --- /dev/null +++ b/src/backend/database/routes/user-totp-routes.test.ts @@ -0,0 +1,83 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import bcrypt from "bcryptjs"; +import speakeasy from "speakeasy"; + +// The route module imports the db barrel (which has filesystem/crypto side +// effects on import) plus the logger; stub both so importing stays inert. +const updateWhere = vi.fn().mockResolvedValue(undefined); +const updateSet = vi.fn(() => ({ where: updateWhere })); +const dbUpdate = vi.fn(() => ({ set: updateSet })); + +vi.mock("../db/index.js", () => ({ + db: { + update: dbUpdate, + }, +})); + +vi.mock("../../utils/logger.js", () => ({ + authLogger: { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + success: vi.fn(), + }, +})); + +const { verifyTotpReauth } = await import("./user-totp-routes.js"); + +type AnyUser = Parameters[0]; + +const secret = speakeasy.generateSecret({ name: "test" }).base32; + +function makeUser(overrides: Partial = {}): AnyUser { + return { + id: "user-1", + isOidc: false, + passwordHash: bcrypt.hashSync("correct-horse", 4), + totpSecret: secret, + totpBackupCodes: JSON.stringify(["BACKUP01", "BACKUP02"]), + totpEnabled: true, + ...overrides, + } as AnyUser; +} + +describe("verifyTotpReauth", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("accepts the correct password", async () => { + expect(await verifyTotpReauth(makeUser(), "correct-horse")).toBe(true); + }); + + it("accepts a valid TOTP code without a password", async () => { + const token = speakeasy.totp({ secret, encoding: "base32" }); + expect(await verifyTotpReauth(makeUser(), token)).toBe(true); + }); + + it("accepts a valid backup code and consumes it", async () => { + const result = await verifyTotpReauth(makeUser(), "BACKUP01"); + expect(result).toBe(true); + expect(updateSet).toHaveBeenCalledWith({ + totpBackupCodes: JSON.stringify(["BACKUP02"]), + }); + }); + + it("rejects a wrong password / invalid code", async () => { + expect(await verifyTotpReauth(makeUser(), "wrong")).toBe(false); + expect(updateSet).not.toHaveBeenCalled(); + }); + + it("ignores the password path for OIDC users but still accepts TOTP", async () => { + const token = speakeasy.totp({ secret, encoding: "base32" }); + const oidcUser = makeUser({ isOidc: true, passwordHash: null }); + expect(await verifyTotpReauth(oidcUser, token)).toBe(true); + expect(await verifyTotpReauth(oidcUser, "anything")).toBe(false); + }); + + it("handles malformed backup-code JSON without throwing", async () => { + const user = makeUser({ totpBackupCodes: "not json" }); + expect(await verifyTotpReauth(user, "BACKUP01")).toBe(false); + }); +}); diff --git a/src/backend/database/routes/user-totp-routes.ts b/src/backend/database/routes/user-totp-routes.ts index 71afae13..32813359 100644 --- a/src/backend/database/routes/user-totp-routes.ts +++ b/src/backend/database/routes/user-totp-routes.ts @@ -1,6 +1,6 @@ import type { AuthenticatedRequest } from "../../../types/index.js"; import type { Request, RequestHandler, Router } from "express"; -import { eq } from "drizzle-orm"; +import { and, eq, ne } from "drizzle-orm"; import bcrypt from "bcryptjs"; import QRCode from "qrcode"; import speakeasy from "speakeasy"; @@ -23,6 +23,57 @@ interface UserTotpRoutesDeps { isNativeAppRequest: NativeAppRequestChecker; } +type TotpUserRecord = typeof users.$inferSelect; + +export async function verifyTotpReauth( + userRecord: TotpUserRecord, + credential: string, +): Promise { + if (!userRecord.isOidc && userRecord.passwordHash) { + const passwordMatch = await bcrypt.compare( + credential, + userRecord.passwordHash, + ); + if (passwordMatch) { + return true; + } + } + + if (userRecord.totpSecret) { + const totpMatch = speakeasy.totp.verify({ + secret: userRecord.totpSecret, + encoding: "base32", + token: credential, + window: 2, + }); + if (totpMatch) { + return true; + } + } + + let backupCodes: unknown = []; + try { + backupCodes = userRecord.totpBackupCodes + ? JSON.parse(userRecord.totpBackupCodes) + : []; + } catch { + backupCodes = []; + } + if (Array.isArray(backupCodes)) { + const backupIndex = backupCodes.indexOf(credential); + if (backupIndex !== -1) { + backupCodes.splice(backupIndex, 1); + await db + .update(users) + .set({ totpBackupCodes: JSON.stringify(backupCodes) }) + .where(eq(users.id, userRecord.id)); + return true; + } + } + + return false; +} + export function registerUserTotpRoutes( router: Router, { authenticateJWT, authManager, isNativeAppRequest }: UserTotpRoutesDeps, @@ -113,6 +164,7 @@ export function registerUserTotpRoutes( */ router.post("/totp/enable", authenticateJWT, async (req, res) => { const userId = (req as AuthenticatedRequest).userId; + const sessionId = (req as AuthenticatedRequest).sessionId; const { totp_code } = req.body; if (!totp_code) { @@ -158,7 +210,13 @@ export function registerUserTotpRoutes( }) .where(eq(users.id, userId)); - await db.delete(sessions).where(eq(sessions.userId, userId)); + await db + .delete(sessions) + .where( + sessionId + ? and(eq(sessions.userId, userId), ne(sessions.id, sessionId)) + : eq(sessions.userId, userId), + ); await db.delete(trustedDevices).where(eq(trustedDevices.userId, userId)); try { @@ -219,11 +277,12 @@ export function registerUserTotpRoutes( router.post("/totp/disable", authenticateJWT, async (req, res) => { const userId = (req as AuthenticatedRequest).userId; const { password, totp_code } = req.body; + const credential = password || totp_code; - if (!password || !totp_code) { + if (!credential) { return res .status(400) - .json({ error: "Both password and TOTP code are required" }); + .json({ error: "A TOTP code or password is required" }); } try { @@ -238,22 +297,11 @@ export function registerUserTotpRoutes( return res.status(400).json({ error: "TOTP is not enabled" }); } - if (!userRecord.isOidc) { - const isMatch = await bcrypt.compare(password, userRecord.passwordHash); - if (!isMatch) { - return res.status(401).json({ error: "Incorrect password" }); - } - } - - const verified = speakeasy.totp.verify({ - secret: userRecord.totpSecret!, - encoding: "base32", - token: totp_code, - window: 2, - }); - + const verified = await verifyTotpReauth(userRecord, credential); if (!verified) { - return res.status(401).json({ error: "Invalid TOTP code" }); + return res + .status(401) + .json({ error: "Incorrect password or invalid TOTP code" }); } await db @@ -310,11 +358,12 @@ export function registerUserTotpRoutes( router.post("/totp/backup-codes", authenticateJWT, async (req, res) => { const userId = (req as AuthenticatedRequest).userId; const { password, totp_code } = req.body; + const credential = password || totp_code; - if (!password || !totp_code) { + if (!credential) { return res .status(400) - .json({ error: "Both password and TOTP code are required" }); + .json({ error: "A TOTP code or password is required" }); } try { @@ -329,22 +378,11 @@ export function registerUserTotpRoutes( return res.status(400).json({ error: "TOTP is not enabled" }); } - if (!userRecord.isOidc) { - const isMatch = await bcrypt.compare(password, userRecord.passwordHash); - if (!isMatch) { - return res.status(401).json({ error: "Incorrect password" }); - } - } - - const verified = speakeasy.totp.verify({ - secret: userRecord.totpSecret!, - encoding: "base32", - token: totp_code, - window: 2, - }); - + const verified = await verifyTotpReauth(userRecord, credential); if (!verified) { - return res.status(401).json({ error: "Invalid TOTP code" }); + return res + .status(401) + .json({ error: "Incorrect password or invalid TOTP code" }); } const backupCodes = Array.from({ length: 8 }, () => diff --git a/src/backend/database/routes/users.ts b/src/backend/database/routes/users.ts index 4a5a6b14..9df534c7 100644 --- a/src/backend/database/routes/users.ts +++ b/src/backend/database/routes/users.ts @@ -20,6 +20,8 @@ import { getOIDCConfigFromEnv, isOIDCUserAllowed, verifyOIDCToken, + extractOidcGroups, + loadProviderConfig, } from "./user-oidc-utils.js"; import { registerUserApiKeyRoutes } from "./user-api-key-routes.js"; import { registerUserSettingsRoutes } from "./user-settings-routes.js"; @@ -29,6 +31,9 @@ import { registerUserOidcAccountRoutes } from "./user-oidc-account-routes.js"; import { registerUserPasswordResetRoutes } from "./user-password-reset-routes.js"; import { registerUserAdminRoutes } from "./user-admin-routes.js"; import { registerUserDataAccessRoutes } from "./user-data-access-routes.js"; +import { registerSSOProviderRoutes } from "./sso-provider-routes.js"; +import { registerLDAPAuthRoutes } from "./ldap-auth-routes.js"; +import { logAudit, getRequestMeta } from "../../utils/audit-logger.js"; const authManager = AuthManager.getInstance(); @@ -225,6 +230,20 @@ router.post("/create", async (req, res) => { username, isAdmin: isFirstUser, }); + + const { ipAddress, userAgent } = getRequestMeta(req); + await logAudit({ + userId: id, + username, + action: "create_user", + resourceType: "user", + resourceId: id, + resourceName: username, + ipAddress, + userAgent, + success: true, + }); + res.json({ message: "User created", is_admin: isFirstUser, @@ -272,6 +291,7 @@ router.post("/oidc-config", authenticateJWT, async (req, res) => { scopes, allowed_users, admin_group, + group_claim, } = req.body; const isDisableRequest = @@ -331,6 +351,7 @@ router.post("/oidc-config", authenticateJWT, async (req, res) => { scopes: scopes || "openid email profile", allowed_users: allowed_users || "", admin_group: admin_group || "", + group_claim: group_claim || "", }; let encryptedConfig; @@ -440,35 +461,19 @@ router.delete("/oidc-config", authenticateJWT, async (req, res) => { * 500: * description: Failed to get OIDC config. */ -router.get("/oidc-config", async (req, res) => { +router.get("/oidc-config", async (_req, res) => { try { - const envConfig = getOIDCConfigFromEnv(); - if (envConfig) { - return res.json({ - client_id: envConfig.client_id, - issuer_url: envConfig.issuer_url, - authorization_url: envConfig.authorization_url, - scopes: envConfig.scopes, - }); - } - - const row = db.$client - .prepare("SELECT value FROM settings WHERE key = 'oidc_config'") - .get(); - if (!row) { + const providerResult = await loadProviderConfig(undefined); + if (!providerResult) { return res.json(null); } - - const config = JSON.parse((row as Record).value as string); - - const publicConfig = { + const { config } = providerResult; + return res.json({ client_id: config.client_id, issuer_url: config.issuer_url, authorization_url: config.authorization_url, scopes: config.scopes, - }; - - return res.json(publicConfig); + }); } catch (err) { authLogger.error("Failed to get OIDC config", err); res.status(500).json({ error: "Failed to get OIDC config" }); @@ -569,24 +574,25 @@ router.get("/oidc-config/admin", requireAdmin, async (req, res) => { */ router.get("/oidc/authorize", async (req, res) => { try { - const { rememberMe, desktopCallbackPort, appCallbackUrl } = req.query; + const { + rememberMe, + desktopCallbackPort, + appCallbackUrl, + providerId: providerIdStr, + } = req.query; const origin = getRequestOriginWithForceHTTPS(req); const backendCallbackUri = `${origin}/users/oidc/callback`; - const envConfig = getOIDCConfigFromEnv(); - let config; - - if (envConfig) { - config = envConfig; - } else { - const row = db.$client - .prepare("SELECT value FROM settings WHERE key = 'oidc_config'") - .get(); - if (!row) { - return res.status(404).json({ error: "OIDC not configured" }); - } - config = JSON.parse((row as Record).value as string); + const resolvedProviderId = providerIdStr + ? parseInt(providerIdStr as string, 10) + : null; + const providerResult = await loadProviderConfig( + resolvedProviderId || undefined, + ); + if (!providerResult) { + return res.status(404).json({ error: "OIDC not configured" }); } + const { config, providerDbId } = providerResult; const state = nanoid(); const nonce = nanoid(); @@ -631,6 +637,12 @@ router.get("/oidc/authorize", async (req, res) => { rememberMe === "true" ? "true" : "false", ); + if (providerDbId != null) { + db.$client + .prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)") + .run(`oidc_provider_${state}`, String(providerDbId)); + } + const authUrl = new URL(config.authorization_url); authUrl.searchParams.set("client_id", config.client_id); authUrl.searchParams.set("redirect_uri", backendCallbackUri); @@ -699,33 +711,260 @@ router.get("/oidc/callback", async (req, res) => { return res.status(400).json({ error: "Invalid state parameter" }); } - const envConfig = getOIDCConfigFromEnv(); - let config; + const storedProviderIdRow = db.$client + .prepare("SELECT value FROM settings WHERE key = ?") + .get(`oidc_provider_${state}`) as { value: string } | null; + const callbackProviderId = storedProviderIdRow + ? parseInt(storedProviderIdRow.value, 10) + : null; - if (envConfig) { - config = envConfig; - } else { - const configRow = db.$client - .prepare("SELECT value FROM settings WHERE key = 'oidc_config'") - .get(); - if (!configRow) { - return res.status(500).json({ error: "OIDC not configured" }); - } - config = JSON.parse( - (configRow as Record).value as string, - ); + const providerResult = await loadProviderConfig( + callbackProviderId || undefined, + ); + if (!providerResult) { + return res.status(500).json({ error: "OIDC not configured" }); + } + const { + config, + providerType: callbackProviderType, + providerDbId: callbackProviderDbId, + } = providerResult; - if (config.client_secret?.startsWith("encrypted:")) { - config.client_secret = Buffer.from( - config.client_secret.substring(10), - "base64", - ).toString("utf8"); - } else if (config.client_secret?.startsWith("encoded:")) { - config.client_secret = Buffer.from( - config.client_secret.substring(8), - "base64", - ).toString("utf8"); + // Clean up provider state key + if (storedProviderIdRow) { + db.$client + .prepare("DELETE FROM settings WHERE key = ?") + .run(`oidc_provider_${state}`); + } + + // GitHub does not issue OIDC id_tokens; handle its token exchange separately + if (callbackProviderType === "github") { + const ghTokenResponse = await fetch(config.token_url, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + body: new URLSearchParams({ + grant_type: "authorization_code", + client_id: config.client_id, + client_secret: config.client_secret, + code: code, + redirect_uri: backendCallbackUri, + }), + }); + + if (!ghTokenResponse.ok) { + const errorText = await ghTokenResponse.text(); + authLogger.error("GitHub token exchange failed", { + operation: "github_token_exchange_failed", + status: ghTokenResponse.status, + errorResponse: errorText, + }); + return res + .status(400) + .json({ error: "Failed to exchange authorization code" }); } + + const ghTokenData = (await ghTokenResponse.json()) as Record< + string, + unknown + >; + db.$client + .prepare("DELETE FROM settings WHERE key = ?") + .run(`oidc_state_${state}`); + db.$client + .prepare("DELETE FROM settings WHERE key = ?") + .run(`oidc_backend_callback_${state}`); + db.$client + .prepare("DELETE FROM settings WHERE key = ?") + .run(`oidc_frontend_origin_${state}`); + db.$client + .prepare("DELETE FROM settings WHERE key = ?") + .run(`oidc_remember_me_${state}`); + + const ghUserInfoResponse = await fetch("https://api.github.com/user", { + headers: { + Authorization: `Bearer ${ghTokenData.access_token}`, + Accept: "application/json", + "User-Agent": "Termix", + }, + }); + if (!ghUserInfoResponse.ok) { + return res + .status(400) + .json({ error: "Failed to get GitHub user information" }); + } + const ghUserInfo = (await ghUserInfoResponse.json()) as Record< + string, + unknown + >; + + const emailRes = await fetch("https://api.github.com/user/emails", { + headers: { + Authorization: `Bearer ${ghTokenData.access_token}`, + Accept: "application/json", + "User-Agent": "Termix", + }, + }); + if (emailRes.ok) { + const emails = (await emailRes.json()) as Array<{ + email: string; + primary: boolean; + verified: boolean; + }>; + const primary = emails.find((e) => e.primary && e.verified); + if (primary) ghUserInfo.email = primary.email; + } + + const ghIdentifier = `github:${callbackProviderDbId}:${String(ghUserInfo.id ?? ghUserInfo.login)}`; + const ghName = (ghUserInfo.name || + ghUserInfo.login || + ghIdentifier) as string; + const deviceInfo = parseUserAgent(req); + + let ghUser = await db + .select() + .from(users) + .where(eq(users.oidcIdentifier, ghIdentifier)); + if (!ghUser || ghUser.length === 0) { + const preCheckCount = db.$client + .prepare("SELECT COUNT(*) as count FROM users") + .get() as { count?: number }; + const isFirstUser = (preCheckCount?.count || 0) === 0; + + if (!isFirstUser && config.allowed_users) { + const email = ghUserInfo.email as string | undefined; + if (!isOIDCUserAllowed(config.allowed_users, ghIdentifier, email)) { + const redirectUrl = new URL(frontendOrigin); + redirectUrl.searchParams.set("error", "user_not_allowed"); + return res.redirect(redirectUrl.toString()); + } + } + + let ghAutoProvision = false; + try { + const r = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'oidc_auto_provision'", + ) + .get() as { value: string } | undefined; + if (r) ghAutoProvision = r.value === "true"; + } catch { + /* */ + } + if (!ghAutoProvision) + ghAutoProvision = + (process.env.OIDC_ALLOW_REGISTRATION || "").trim().toLowerCase() === + "true"; + + if (!isFirstUser && !ghAutoProvision) { + const regRow = db.$client + .prepare( + "SELECT value FROM settings WHERE key = 'allow_registration'", + ) + .get() as { value: string } | undefined; + if (regRow && regRow.value !== "true") { + const redirectUrl = new URL(frontendOrigin); + redirectUrl.searchParams.set("error", "registration_disabled"); + return res.redirect(redirectUrl.toString()); + } + } + + const ghId = nanoid(); + const ghIsFirst = db.$client.transaction(() => { + const c = + ( + db.$client + .prepare("SELECT COUNT(*) as count FROM users") + .get() as { count?: number } + )?.count || 0; + const first = c === 0; + db.$client + .prepare( + "INSERT INTO users (id, username, password_hash, is_admin, is_oidc, oidc_identifier, sso_provider_id) VALUES (?, ?, ?, ?, 1, ?, ?)", + ) + .run( + ghId, + ghName, + "", + first ? 1 : 0, + ghIdentifier, + callbackProviderDbId, + ); + return first; + })(); + + try { + const defaultRoleName = ghIsFirst ? "admin" : "user"; + const defaultRole = await db + .select({ id: roles.id }) + .from(roles) + .where(eq(roles.name, defaultRoleName)) + .limit(1); + if (defaultRole.length > 0) + await db.insert(userRoles).values({ + userId: ghId, + roleId: defaultRole[0].id, + grantedBy: ghId, + }); + } catch { + /* */ + } + + try { + const sessionDurationMs = + deviceInfo.type === "desktop" || deviceInfo.type === "mobile" + ? 30 * 24 * 60 * 60 * 1000 + : 24 * 60 * 60 * 1000; + await authManager.registerOIDCUser(ghId, sessionDurationMs); + } catch (encryptionError) { + await db.delete(users).where(eq(users.id, ghId)); + return res.status(500).json({ + error: "Failed to setup user security - user creation cancelled", + }); + } + + ghUser = await db.select().from(users).where(eq(users.id, ghId)); + } + + const ghUserRecord = ghUser[0]; + try { + await authManager.authenticateOIDCUser( + ghUserRecord.id, + deviceInfo.type, + ); + } catch { + /* */ + } + const ghToken = await authManager.generateJWTToken(ghUserRecord.id, { + deviceType: deviceInfo.type, + deviceInfo: deviceInfo.deviceInfo, + rememberMe: storedRememberMe, + }); + const ghRedirectUrl = new URL(frontendOrigin); + ghRedirectUrl.searchParams.set("success", "true"); + const ghIsTokenCallback = + frontendOrigin.startsWith("http://127.0.0.1:") || + frontendOrigin.startsWith("termix-mobile:"); + const ghMaxAge = + deviceInfo.type === "desktop" || deviceInfo.type === "mobile" + ? 30 * 24 * 60 * 60 * 1000 + : storedRememberMe + ? 30 * 24 * 60 * 60 * 1000 + : 24 * 60 * 60 * 1000; + res.clearCookie("jwt", authManager.getClearCookieOptions(req)); + if (ghIsTokenCallback) { + ghRedirectUrl.searchParams.set("token", ghToken); + return res.redirect(ghRedirectUrl.toString()); + } + return res + .cookie( + "jwt", + ghToken, + authManager.getSecureCookieOptions(req, ghMaxAge), + ) + .redirect(ghRedirectUrl.toString()); } const tokenResponse = await fetch(config.token_url, { @@ -733,6 +972,7 @@ router.get("/oidc/callback", async (req, res) => { headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json", + Authorization: `Basic ${Buffer.from(`${encodeURIComponent(config.client_id)}:${encodeURIComponent(config.client_secret)}`).toString("base64")}`, }, body: new URLSearchParams({ grant_type: "authorization_code", @@ -834,7 +1074,7 @@ router.get("/oidc/callback", async (req, res) => { } } - if (!userInfo && tokenData.access_token) { + if (tokenData.access_token) { for (const userInfoUrl of userInfoUrls) { try { const userInfoResponse = await fetch(userInfoUrl, { @@ -844,10 +1084,11 @@ router.get("/oidc/callback", async (req, res) => { }); if (userInfoResponse.ok) { - userInfo = (await userInfoResponse.json()) as Record< + const fetchedUserInfo = (await userInfoResponse.json()) as Record< string, unknown >; + userInfo = { ...userInfo, ...fetchedUserInfo }; break; } else { authLogger.error( @@ -986,7 +1227,7 @@ router.get("/oidc/callback", async (req, res) => { const first = (countResult?.count || 0) === 0; db.$client .prepare( - "INSERT INTO users (id, username, password_hash, is_admin, is_oidc, oidc_identifier, client_id, client_secret, issuer_url, authorization_url, token_url, identifier_path, name_path, scopes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO users (id, username, password_hash, is_admin, is_oidc, oidc_identifier, sso_provider_id) VALUES (?, ?, ?, ?, ?, ?, ?)", ) .run( id, @@ -995,14 +1236,7 @@ router.get("/oidc/callback", async (req, res) => { first ? 1 : 0, 1, identifier, - String(config.client_id), - String(config.client_secret), - String(config.issuer_url), - String(config.authorization_url), - String(config.token_url), - String(config.identifier_path), - String(config.name_path), - String(config.scopes), + callbackProviderDbId, ); return first; })(); @@ -1107,9 +1341,27 @@ router.get("/oidc/callback", async (req, res) => { // Sync admin status based on OIDC group membership if (config.admin_group) { - const groups = (userInfo.groups || userInfo.roles || []) as string[]; + const groups = extractOidcGroups( + userInfo as Record, + config.group_claim, + ); + + authLogger.info( + `Evaluating OIDC admin group sync. parsedGroups: ${JSON.stringify(groups)}, configuredAdminGroup: ${config.admin_group}, groupClaim: ${config.group_claim || "(default)"}, availableUserInfoKeys: ${Object.keys(userInfo).join(",")}`, + { + operation: "oidc_admin_group_sync_eval", + userId: userRecord.id, + }, + ); + const shouldBeAdmin = groups.includes(config.admin_group); if (!!userRecord.isAdmin !== shouldBeAdmin) { + authLogger.info("Syncing admin status based on OIDC group membership", { + operation: "oidc_admin_group_sync", + userId: userRecord.id, + group: config.admin_group, + isAdmin: shouldBeAdmin, + }); await db .update(users) .set({ isAdmin: shouldBeAdmin }) @@ -1411,6 +1663,17 @@ router.post("/login", async (req, res) => { sessionId: payload?.sessionId, }); + const { ipAddress: loginIp, userAgent: loginUa } = getRequestMeta(req); + await logAudit({ + userId: userRecord.id, + username, + action: "login", + resourceType: "session", + ipAddress: loginIp, + userAgent: loginUa, + success: true, + }); + const response: Record = { success: true, is_admin: !!userRecord.isAdmin, @@ -2097,6 +2360,23 @@ router.post("/change-password", authenticateJWT, async (req, res) => { userId, }); + const { ipAddress: pwIp, userAgent: pwUa } = getRequestMeta(req); + const pwUser = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: pwUser[0]?.username ?? userId, + action: "change_password", + resourceType: "user", + resourceId: userId, + ipAddress: pwIp, + userAgent: pwUa, + success: true, + }); + res.json({ message: "Password changed successfully. Please log in again." }); }); @@ -2184,6 +2464,25 @@ router.delete("/delete-user", authenticateJWT, async (req, res) => { targetUserId, targetUsername: username, }); + + const { ipAddress: deleteIp, userAgent: deleteUa } = getRequestMeta(req); + const delAdminRecord = await db + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + await logAudit({ + userId, + username: delAdminRecord[0]?.username ?? userId, + action: "delete_user", + resourceType: "user", + resourceId: targetUserId, + resourceName: username, + ipAddress: deleteIp, + userAgent: deleteUa, + success: true, + }); + res.json({ message: `User ${username} deleted successfully` }); } catch (err) { authLogger.error("Failed to delete user", err); @@ -2222,4 +2521,7 @@ registerUserSettingsRoutes(router, authenticateJWT); registerUserApiKeyRoutes(router, requireAdmin); +registerSSOProviderRoutes(router); +registerLDAPAuthRoutes(router); + export default router; diff --git a/src/backend/ssh/credential-username.test.ts b/src/backend/ssh/credential-username.test.ts new file mode 100644 index 00000000..fef686db --- /dev/null +++ b/src/backend/ssh/credential-username.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from "vitest"; +import { pickResolvedUsername } from "./credential-username.js"; + +describe("pickResolvedUsername", () => { + it("keeps the host username when one is set, even with a credential username", () => { + expect(pickResolvedUsername("admin", "root", false)).toBe("admin"); + }); + + it("falls back to the credential username when the host has none", () => { + expect(pickResolvedUsername("", "root", false)).toBe("root"); + expect(pickResolvedUsername(undefined, "root", false)).toBe("root"); + expect(pickResolvedUsername(" ", "root", false)).toBe("root"); + }); + + it("treats whitespace-only host usernames as empty", () => { + expect(pickResolvedUsername(" ", "deploy", false)).toBe("deploy"); + }); + + it("forces the host username when overrideCredentialUsername is set", () => { + expect(pickResolvedUsername("admin", "root", true)).toBe("admin"); + expect(pickResolvedUsername("", "root", true)).toBeUndefined(); + }); + + it("returns undefined when neither username is usable", () => { + expect(pickResolvedUsername("", "", false)).toBeUndefined(); + expect(pickResolvedUsername(undefined, undefined, false)).toBeUndefined(); + }); +}); diff --git a/src/backend/ssh/credential-username.ts b/src/backend/ssh/credential-username.ts new file mode 100644 index 00000000..e0e15577 --- /dev/null +++ b/src/backend/ssh/credential-username.ts @@ -0,0 +1,26 @@ +/** + * Decides which username to use when a host is backed by a saved credential. + * + * An explicitly-set host username always wins - if the user typed a username on + * the host it should be honoured even when a credential is attached. The + * credential's username is only used as a fallback when the host has none. The + * `overrideCredentialUsername` flag forces the host username regardless. + */ +export function pickResolvedUsername( + hostUsername: unknown, + credentialUsername: unknown, + overrideCredentialUsername?: unknown, +): string | undefined { + const host = isNonEmptyString(hostUsername) ? hostUsername : undefined; + const cred = isNonEmptyString(credentialUsername) + ? credentialUsername + : undefined; + + if (overrideCredentialUsername) return host; + if (host) return host; + return cred; +} + +function isNonEmptyString(value: unknown): value is string { + return typeof value === "string" && value.trim() !== ""; +} diff --git a/src/backend/ssh/docker.ts b/src/backend/ssh/docker.ts index dd335f9a..a3cf1ec6 100644 --- a/src/backend/ssh/docker.ts +++ b/src/backend/ssh/docker.ts @@ -314,7 +314,9 @@ async function createJumpHostChain( host: jumpHostConfig.ip?.replace(/^\[|\]$/g, "") || jumpHostConfig.ip, port: jumpHostConfig.port || 22, username: jumpHostConfig.username, - tryKeyboard: jumpHostConfig.authType !== "none", + tryKeyboard: + jumpHostConfig.authType !== "none" && + jumpHostConfig.authType !== "tailscale", readyTimeout: 60000, hostVerifier: jumpHostVerifier, algorithms: { @@ -848,8 +850,11 @@ app.post("/docker/ssh/connect", async (req, res) => { ), }; - if (resolvedCredentials.authType === "none") { - // no credentials needed + if ( + resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale" + ) { + // Tailscale SSH and "none" auth: no credentials needed } else if (resolvedCredentials.authType === "password") { if (resolvedCredentials.password) { config.password = resolvedCredentials.password; @@ -1008,7 +1013,10 @@ app.post("/docker/ssh/connect", async (req, res) => { connectionLogs.push( createConnectionLog("info", "auth", "Authenticating with SSH key"), ); - } else if (resolvedCredentials.authType === "none") { + } else if ( + resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale" + ) { connectionLogs.push( createConnectionLog( "info", @@ -1144,7 +1152,8 @@ app.post("/docker/ssh/connect", async (req, res) => { } if ( - resolvedCredentials.authType === "none" && + (resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale") && (err.message.includes("authentication") || err.message.includes("All configured authentication methods failed")) ) { @@ -1305,7 +1314,8 @@ app.post("/docker/ssh/connect", async (req, res) => { ); if ( - resolvedCredentials.authType === "none" && + (resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale") && passwordPromptIndex !== -1 ) { if (responseSent) return; diff --git a/src/backend/ssh/file-manager-operation-routes.ts b/src/backend/ssh/file-manager-operation-routes.ts index 23c1e0da..c1653680 100644 --- a/src/backend/ssh/file-manager-operation-routes.ts +++ b/src/backend/ssh/file-manager-operation-routes.ts @@ -449,7 +449,7 @@ export function registerFileOperationRoutes( return; } - if (outputData.includes("SUCCESS") || code === 0) { + if (outputData.includes("SUCCESS")) { fileLogger.success("Item deleted successfully", { operation: "file_delete_success", sessionId, @@ -465,8 +465,22 @@ export function registerFileOperationRoutes( }, }); } else { + // The shell didn't echo our SUCCESS sentinel. This happens on + // non-POSIX shells (e.g. Windows PowerShell, where `rm -f` is + // not supported) and the command can exit 0 while doing nothing. + // Surface whatever the shell produced so the failure isn't silent. + const detail = + errorData.trim() || + outputData.trim() || + `command exited with code ${code} and produced no output (the remote shell may not support the delete command)`; + fileLogger.error(`Delete failed for ${itemPath}: ${detail}`, { + operation: "file_delete_failed", + sessionId, + userId, + path: itemPath, + }); res.status(500).json({ - error: `Command failed: ${errorData}`, + error: `Delete failed: ${detail}`, }); } resolve(); diff --git a/src/backend/ssh/file-manager.ts b/src/backend/ssh/file-manager.ts index 3a33debe..5b06c46f 100644 --- a/src/backend/ssh/file-manager.ts +++ b/src/backend/ssh/file-manager.ts @@ -220,7 +220,7 @@ async function buildDedicatedTransferConnectConfig( token, username, ); - } else if (authType !== "none") { + } else if (authType !== "none" && authType !== "tailscale") { throw new Error(`Unsupported auth type for transfer: ${authType}`); } @@ -725,6 +725,7 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { let resolvedIp = ip; let resolvedPort = port; let resolvedUsername = username; + let resolvedJumpHosts = jumpHosts; if (hostId && userId && !password && !sshKey) { try { const { resolveHostById } = await import("./host-resolver.js"); @@ -742,6 +743,20 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { }; hostKeepaliveInterval = resolvedHost.terminalConfig?.keepaliveInterval; hostKeepaliveCountMax = resolvedHost.terminalConfig?.keepaliveCountMax; + if ( + (!resolvedJumpHosts || resolvedJumpHosts.length === 0) && + resolvedHost.jumpHosts && + resolvedHost.jumpHosts.length > 0 + ) { + resolvedJumpHosts = resolvedHost.jumpHosts; + connectionLogs.push( + createConnectionLog( + "info", + "jump", + `Loaded ${resolvedHost.jumpHosts.length} jump host(s) from server-side host data`, + ), + ); + } connectionLogs.push( createConnectionLog( "info", @@ -775,6 +790,20 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { }; hostKeepaliveInterval = resolvedHost.terminalConfig?.keepaliveInterval; hostKeepaliveCountMax = resolvedHost.terminalConfig?.keepaliveCountMax; + if ( + (!resolvedJumpHosts || resolvedJumpHosts.length === 0) && + resolvedHost.jumpHosts && + resolvedHost.jumpHosts.length > 0 + ) { + resolvedJumpHosts = resolvedHost.jumpHosts; + connectionLogs.push( + createConnectionLog( + "info", + "jump", + `Loaded ${resolvedHost.jumpHosts.length} jump host(s) from server-side host data`, + ), + ); + } connectionLogs.push( createConnectionLog( "info", @@ -984,7 +1013,10 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { connectionLogs, }); } - } else if (resolvedCredentials.authType === "none") { + } else if ( + resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale" + ) { connectionLogs.push( createConnectionLog( "info", @@ -1234,7 +1266,8 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { } if ( - resolvedCredentials.authType === "none" && + (resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale") && (err.message.includes("authentication") || err.message.includes("All configured authentication methods failed")) ) { @@ -1392,14 +1425,16 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { } else { const hasStoredPassword = resolvedCredentials.password && - resolvedCredentials.authType !== "none"; + resolvedCredentials.authType !== "none" && + resolvedCredentials.authType !== "tailscale"; const passwordPromptIndex = prompts.findIndex((p) => /password/i.test(p.prompt), ); if ( - resolvedCredentials.authType === "none" && + (resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale") && passwordPromptIndex !== -1 ) { if (responseSent) return; @@ -1490,7 +1525,8 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { } : null; - const hasJumpHosts = jumpHosts && jumpHosts.length > 0 && userId; + const hasJumpHosts = + resolvedJumpHosts && resolvedJumpHosts.length > 0 && userId; if (hasJumpHosts) { try { @@ -1507,11 +1543,11 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => { createConnectionLog( "info", "jump", - `Connecting via ${jumpHosts.length} jump host(s)`, + `Connecting via ${resolvedJumpHosts.length} jump host(s)`, ), ); const jumpClient = await createJumpHostChain( - jumpHosts, + resolvedJumpHosts, userId, proxyConfig, ); diff --git a/src/backend/ssh/server-stats-helpers.test.ts b/src/backend/ssh/host-metrics-helpers.test.ts similarity index 98% rename from src/backend/ssh/server-stats-helpers.test.ts rename to src/backend/ssh/host-metrics-helpers.test.ts index c59c4612..f3523999 100644 --- a/src/backend/ssh/server-stats-helpers.test.ts +++ b/src/backend/ssh/host-metrics-helpers.test.ts @@ -3,7 +3,7 @@ import { supportsMetrics, isTcpPingEnabled, createConnectionLog, -} from "./server-stats-helpers.js"; +} from "./host-metrics-helpers.js"; describe("supportsMetrics", () => { it("supports plain ssh hosts", () => { diff --git a/src/backend/ssh/server-stats-helpers.ts b/src/backend/ssh/host-metrics-helpers.ts similarity index 100% rename from src/backend/ssh/server-stats-helpers.ts rename to src/backend/ssh/host-metrics-helpers.ts diff --git a/src/backend/ssh/server-stats-jump-hosts.ts b/src/backend/ssh/host-metrics-jump-hosts.ts similarity index 100% rename from src/backend/ssh/server-stats-jump-hosts.ts rename to src/backend/ssh/host-metrics-jump-hosts.ts diff --git a/src/backend/ssh/host-metrics-preferences-routes.ts b/src/backend/ssh/host-metrics-preferences-routes.ts new file mode 100644 index 00000000..ba9be2e0 --- /dev/null +++ b/src/backend/ssh/host-metrics-preferences-routes.ts @@ -0,0 +1,245 @@ +import type { Express, RequestHandler } from "express"; +import type { AuthenticatedRequest } from "../../types/index.js"; +import { getDb, DatabaseSaveTrigger } from "../database/db/index.js"; +import { statsLogger } from "../utils/logger.js"; +import { + deriveEnabledWidgets, + defaultLayoutFromWidgets, + type HostMetricsLayout, +} from "../../types/host-metrics.js"; + +interface PrefStatsConfig { + enabledWidgets?: string[]; +} + +interface PrefHost { + id: number; + userId: string; + statsConfig?: unknown; +} + +type HostMetricsPreferencesRoutesDeps = { + validateHostId: RequestHandler; + fetchHostById: (hostId: number, userId: string) => Promise; + parseStatsConfig: (statsConfig: unknown) => PrefStatsConfig; + canAccessHost: ( + userId: string, + hostId: number, + level: "read" | "execute", + ) => Promise; +}; + +function sanitizeLayout(input: unknown): HostMetricsLayout | null { + if (!input || typeof input !== "object") return null; + const obj = input as Record; + if (!Array.isArray(obj.slots)) return null; + const columns = + typeof obj.columns === "number" && obj.columns >= 1 && obj.columns <= 4 + ? Math.round(obj.columns) + : 3; + const slots = obj.slots + .filter((s): s is Record => !!s && typeof s === "object") + .map((s, i) => ({ + id: String(s.id), + order: typeof s.order === "number" ? s.order : i, + colSpan: + s.colSpan === 1 || s.colSpan === 2 || s.colSpan === 3 ? s.colSpan : 1, + height: + typeof s.height === "number" && s.height > 0 + ? Math.round(s.height) + : null, + })) + .filter((s) => s.id && s.id !== "undefined"); + return { slots, columns } as HostMetricsLayout; +} + +export function registerHostMetricsPreferencesRoutes( + app: Express, + { + validateHostId, + fetchHostById, + parseStatsConfig, + canAccessHost, + }: HostMetricsPreferencesRoutesDeps, +): void { + /** + * @openapi + * /host-metrics/preferences/{id}: + * get: + * summary: Get the Host Metrics layout for a host + * description: Returns the current user's saved card layout for the host, or a default layout derived from the host's enabled widgets when none is saved. + * tags: + * - Host Metrics + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: The layout for this host. + * 403: + * description: No access to this host. + * 404: + * description: Host not found. + */ + app.get("/host-metrics/preferences/:id", validateHostId, async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + const hostId = parseInt(String(req.params.id), 10); + try { + if (!(await canAccessHost(userId, hostId, "read"))) { + return res.status(403).json({ error: "No access to this host" }); + } + const host = await fetchHostById(hostId, userId); + if (!host) { + return res.status(404).json({ error: "Host not found" }); + } + + const db = getDb(); + const row = db.$client + .prepare( + "SELECT layout FROM host_metrics_preferences WHERE user_id = ? AND host_id = ?", + ) + .get(userId, hostId) as { layout: string } | undefined; + + if (row?.layout) { + try { + const parsed = sanitizeLayout(JSON.parse(row.layout)); + if (parsed) return res.json({ layout: parsed }); + } catch { + // fall through to default + } + } + + const statsConfig = parseStatsConfig(host.statsConfig); + const layout = defaultLayoutFromWidgets(statsConfig.enabledWidgets ?? []); + return res.json({ layout }); + } catch (error) { + statsLogger.error("Failed to fetch host metrics preferences", { + operation: "host_metrics_prefs_fetch_error", + hostId, + error: error instanceof Error ? error.message : String(error), + }); + return res + .status(500) + .json({ error: "Failed to fetch host metrics preferences" }); + } + }); + + /** + * @openapi + * /host-metrics/preferences/{id}: + * post: + * summary: Save the Host Metrics layout for a host + * description: Persists the current user's card layout for the host and keeps statsConfig.enabledWidgets in sync (for hosts the user owns) so the mobile app keeps working. + * tags: + * - Host Metrics + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * slots: + * type: array + * columns: + * type: integer + * responses: + * 200: + * description: Layout saved. + * 400: + * description: Invalid layout. + * 403: + * description: No access to this host. + */ + app.post( + "/host-metrics/preferences/:id", + validateHostId, + async (req, res) => { + const userId = (req as AuthenticatedRequest).userId; + const hostId = parseInt(String(req.params.id), 10); + try { + if (!(await canAccessHost(userId, hostId, "read"))) { + return res.status(403).json({ error: "No access to this host" }); + } + const host = await fetchHostById(hostId, userId); + if (!host) { + return res.status(404).json({ error: "Host not found" }); + } + + const layout = sanitizeLayout(req.body); + if (!layout) { + return res.status(400).json({ error: "Invalid layout" }); + } + + const db = getDb(); + const now = new Date().toISOString(); + const layoutJson = JSON.stringify(layout); + + const existing = db.$client + .prepare( + "SELECT id FROM host_metrics_preferences WHERE user_id = ? AND host_id = ?", + ) + .get(userId, hostId) as { id: number } | undefined; + + if (existing) { + db.$client + .prepare( + "UPDATE host_metrics_preferences SET layout = ?, updated_at = ? WHERE id = ?", + ) + .run(layoutJson, now, existing.id); + } else { + db.$client + .prepare( + "INSERT INTO host_metrics_preferences (user_id, host_id, layout, created_at, updated_at) VALUES (?, ?, ?, ?, ?)", + ) + .run(userId, hostId, layoutJson, now, now); + } + + // Keep statsConfig.enabledWidgets in sync (mobile contract) for hosts the + // user owns. stats_config is plain JSON text (not an encrypted field). + if (host.userId === userId) { + try { + const current = parseStatsConfig(host.statsConfig); + const merged = { + ...current, + enabledWidgets: deriveEnabledWidgets(layout.slots), + }; + db.$client + .prepare( + "UPDATE ssh_data SET stats_config = ? WHERE id = ? AND user_id = ?", + ) + .run(JSON.stringify(merged), hostId, userId); + } catch (syncErr) { + statsLogger.warn("Failed to sync enabledWidgets from layout", { + operation: "host_metrics_prefs_sync_widgets", + hostId, + error: + syncErr instanceof Error ? syncErr.message : String(syncErr), + }); + } + } + + DatabaseSaveTrigger.triggerSave("host_metrics_preferences_updated"); + return res.json({ success: true }); + } catch (error) { + statsLogger.error("Failed to save host metrics preferences", { + operation: "host_metrics_prefs_save_error", + hostId, + error: error instanceof Error ? error.message : String(error), + }); + return res + .status(500) + .json({ error: "Failed to save host metrics preferences" }); + } + }, + ); +} diff --git a/src/backend/ssh/host-metrics-preferences.test.ts b/src/backend/ssh/host-metrics-preferences.test.ts new file mode 100644 index 00000000..e13a6e9e --- /dev/null +++ b/src/backend/ssh/host-metrics-preferences.test.ts @@ -0,0 +1,85 @@ +import { describe, it, expect } from "vitest"; +import { + deriveEnabledWidgets, + defaultLayoutFromWidgets, + isMetricCardId, + isManagerCardId, + METRIC_CARD_IDS, +} from "../../types/host-metrics.js"; + +describe("deriveEnabledWidgets", () => { + it("returns only metric ids, in canonical order, deduped", () => { + const slots = [ + { id: "firewall" }, + { id: "cpu" }, + { id: "cpu" }, + { id: "memory" }, + ]; + expect(deriveEnabledWidgets(slots)).toEqual(["cpu", "memory", "firewall"]); + }); + + it("excludes manager card ids (mobile contract: never leak managers)", () => { + const slots = [ + { id: "cpu" }, + { id: "service_manager" }, + { id: "log_viewer" }, + { id: "disk" }, + ]; + const out = deriveEnabledWidgets(slots); + expect(out).toEqual(["cpu", "disk"]); + for (const id of out) expect(isMetricCardId(id)).toBe(true); + }); + + it("output is always a subset of the 10 known WidgetTypes", () => { + const slots = METRIC_CARD_IDS.map((id) => ({ id })).concat([ + { id: "user_manager" } as { id: (typeof METRIC_CARD_IDS)[number] }, + { id: "bogus" } as { id: (typeof METRIC_CARD_IDS)[number] }, + ]); + const out = deriveEnabledWidgets(slots); + expect(out).toEqual(METRIC_CARD_IDS); + expect(out.length).toBe(10); + }); + + it("empty slots -> empty widgets", () => { + expect(deriveEnabledWidgets([])).toEqual([]); + }); +}); + +describe("defaultLayoutFromWidgets", () => { + it("builds slots in canonical order with dense ordering", () => { + const layout = defaultLayoutFromWidgets(["disk", "cpu", "memory"]); + expect(layout.slots.map((s) => s.id)).toEqual(["cpu", "memory", "disk"]); + expect(layout.slots.map((s) => s.order)).toEqual([0, 1, 2]); + expect(layout.columns).toBe(3); + }); + + it("ignores unknown widget ids", () => { + const layout = defaultLayoutFromWidgets(["cpu", "nope", "service_manager"]); + expect(layout.slots.map((s) => s.id)).toEqual(["cpu"]); + }); + + it("assigns valid colSpans (1..3)", () => { + const layout = defaultLayoutFromWidgets([...METRIC_CARD_IDS]); + for (const s of layout.slots) { + expect([1, 2, 3]).toContain(s.colSpan); + } + }); + + it("round-trips: deriveEnabledWidgets(defaultLayout) === input order", () => { + const layout = defaultLayoutFromWidgets(["ports", "cpu", "firewall"]); + expect(deriveEnabledWidgets(layout.slots)).toEqual([ + "cpu", + "ports", + "firewall", + ]); + }); +}); + +describe("card id guards", () => { + it("classifies metric vs manager ids", () => { + expect(isMetricCardId("cpu")).toBe(true); + expect(isMetricCardId("service_manager")).toBe(false); + expect(isManagerCardId("service_manager")).toBe(true); + expect(isManagerCardId("cpu")).toBe(false); + }); +}); diff --git a/src/backend/ssh/server-stats-sessions.ts b/src/backend/ssh/host-metrics-sessions.ts similarity index 100% rename from src/backend/ssh/server-stats-sessions.ts rename to src/backend/ssh/host-metrics-sessions.ts diff --git a/src/backend/ssh/server-stats-settings-routes.ts b/src/backend/ssh/host-metrics-settings-routes.ts similarity index 95% rename from src/backend/ssh/server-stats-settings-routes.ts rename to src/backend/ssh/host-metrics-settings-routes.ts index a7a4e5ae..f3c2dc26 100644 --- a/src/backend/ssh/server-stats-settings-routes.ts +++ b/src/backend/ssh/host-metrics-settings-routes.ts @@ -2,24 +2,24 @@ import type { Express, RequestHandler } from "express"; import { getDb } from "../database/db/index.js"; import { statsLogger } from "../utils/logger.js"; -type ServerStatsSettingsConfig = { +type HostMetricsSettingsConfig = { statusCheckInterval: number; metricsInterval: number; }; -type ServerStatsSettingsRoutesDeps = { +type HostMetricsSettingsRoutesDeps = { requireAdmin: RequestHandler; - defaultStatsConfig: ServerStatsSettingsConfig; + defaultStatsConfig: HostMetricsSettingsConfig; refreshAllPolling: () => Promise; }; -export function registerServerStatsSettingsRoutes( +export function registerHostMetricsSettingsRoutes( app: Express, { requireAdmin, defaultStatsConfig: DEFAULT_STATS_CONFIG, refreshAllPolling, - }: ServerStatsSettingsRoutesDeps, + }: HostMetricsSettingsRoutesDeps, ): void { /** * @openapi @@ -27,7 +27,7 @@ export function registerServerStatsSettingsRoutes( * get: * summary: Get global monitoring defaults * tags: - * - Stats + * - Host Metrics * responses: * 200: * description: Global monitoring settings. @@ -88,7 +88,7 @@ export function registerServerStatsSettingsRoutes( * post: * summary: Update global monitoring defaults * tags: - * - Stats + * - Host Metrics * requestBody: * required: true * content: diff --git a/src/backend/ssh/server-stats-state.ts b/src/backend/ssh/host-metrics-state.ts similarity index 100% rename from src/backend/ssh/server-stats-state.ts rename to src/backend/ssh/host-metrics-state.ts diff --git a/src/backend/ssh/server-stats-viewer-routes.ts b/src/backend/ssh/host-metrics-viewer-routes.ts similarity index 97% rename from src/backend/ssh/server-stats-viewer-routes.ts rename to src/backend/ssh/host-metrics-viewer-routes.ts index 7e5a36dc..505c4a3c 100644 --- a/src/backend/ssh/server-stats-viewer-routes.ts +++ b/src/backend/ssh/host-metrics-viewer-routes.ts @@ -7,7 +7,7 @@ type ViewerStatsConfig = { metricsEnabled: boolean; }; -type ServerStatsViewerRoutesDeps< +type HostMetricsViewerRoutesDeps< THost extends { statsConfig?: string | TStatsConfig }, TStatsConfig extends ViewerStatsConfig, > = { @@ -23,7 +23,7 @@ type ServerStatsViewerRoutesDeps< unregisterViewer: (hostId: number, viewerSessionId: string) => void; }; -export function registerServerStatsViewerRoutes< +export function registerHostMetricsViewerRoutes< THost extends { statsConfig?: string | TStatsConfig }, TStatsConfig extends ViewerStatsConfig, >( @@ -35,7 +35,7 @@ export function registerServerStatsViewerRoutes< updateHeartbeat, registerViewer, unregisterViewer, - }: ServerStatsViewerRoutesDeps, + }: HostMetricsViewerRoutesDeps, ): void { /** * @openapi @@ -44,7 +44,7 @@ export function registerServerStatsViewerRoutes< * summary: Update viewer heartbeat * description: Updates the heartbeat timestamp for a metrics viewer session to keep it alive. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: @@ -105,7 +105,7 @@ export function registerServerStatsViewerRoutes< * summary: Register metrics viewer * description: Registers a new viewer session for a host to track who is viewing metrics. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: @@ -233,7 +233,7 @@ export function registerServerStatsViewerRoutes< * summary: Unregister metrics viewer * description: Unregisters a viewer session when they stop viewing metrics for a host. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: diff --git a/src/backend/ssh/server-stats.ts b/src/backend/ssh/host-metrics.ts similarity index 95% rename from src/backend/ssh/server-stats.ts rename to src/backend/ssh/host-metrics.ts index b821ce8a..880c15f4 100644 --- a/src/backend/ssh/server-stats.ts +++ b/src/backend/ssh/host-metrics.ts @@ -4,6 +4,7 @@ import { createCorsMiddleware } from "../utils/cors-config.js"; import cookieParser from "cookie-parser"; import { Client, type ConnectConfig } from "ssh2"; import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; +import { pickResolvedUsername } from "./credential-username.js"; import { getDb } from "../database/db/index.js"; import { hosts, sshCredentials } from "../database/db/schema.js"; import { eq } from "drizzle-orm"; @@ -29,14 +30,18 @@ import { } from "../utils/socks5-helper.js"; import { SSHHostKeyVerifier } from "./host-key-verifier.js"; import { connectionPool, withConnection } from "./ssh-connection-pool.js"; -import { registerServerStatsSettingsRoutes } from "./server-stats-settings-routes.js"; -import { registerServerStatsViewerRoutes } from "./server-stats-viewer-routes.js"; -import { createJumpHostChain } from "./server-stats-jump-hosts.js"; +import { registerHostMetricsSettingsRoutes } from "./host-metrics-settings-routes.js"; +import { registerHostMetricsViewerRoutes } from "./host-metrics-viewer-routes.js"; +import { registerHostMetricsPreferencesRoutes } from "./host-metrics-preferences-routes.js"; +import { registerManagerRoutes } from "./managers/index.js"; +import { AccessDeniedError } from "./managers/route-helpers.js"; +import type { ManagerHost } from "./managers/types.js"; +import { createJumpHostChain } from "./host-metrics-jump-hosts.js"; import { createConnectionLog, isTcpPingEnabled, supportsMetrics, -} from "./server-stats-helpers.js"; +} from "./host-metrics-helpers.js"; import { cleanupMetricsSession, getSessionKey, @@ -44,13 +49,13 @@ import { pendingTOTPSessions, scheduleMetricsSessionCleanup, type MetricsViewer, -} from "./server-stats-sessions.js"; +} from "./host-metrics-sessions.js"; import { authFailureTracker, metricsCache, pollingBackoff, requestQueue, -} from "./server-stats-state.js"; +} from "./host-metrics-state.js"; const authManager = AuthManager.getInstance(); const permissionManager = PermissionManager.getInstance(); @@ -71,6 +76,7 @@ interface SSHHostWithCredentials { key?: string; keyPassword?: string; keyType?: string; + sudoPassword?: string; credentialId?: number; enableTerminal: boolean; enableTunnel: boolean; @@ -762,6 +768,19 @@ async function resolveHostCredentials( : [], jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts as string) : [], statsConfig: host.statsConfig || undefined, + sudoPassword: (() => { + const top = host.sudoPassword as string | null | undefined; + if (top) return top; + try { + const tc = + typeof host.terminalConfig === "string" + ? JSON.parse(host.terminalConfig as string) + : host.terminalConfig; + return (tc?.sudoPassword as string) || undefined; + } catch { + return undefined; + } + })(), createdAt: host.createdAt, updatedAt: host.updatedAt, userId: host.userId, @@ -793,9 +812,11 @@ async function resolveHostCredentials( baseHost.credentialId = host.credentialId; baseHost.authType = sharedCred.authType; - if (!host.overrideCredentialUsername) { - baseHost.username = sharedCred.username; - } + baseHost.username = pickResolvedUsername( + host.username, + sharedCred.username, + host.overrideCredentialUsername, + ); if (sharedCred.password) { baseHost.password = sharedCred.password; @@ -832,9 +853,11 @@ async function resolveHostCredentials( ? "key" : "none"); - if (!host.overrideCredentialUsername) { - baseHost.username = credential.username; - } + baseHost.username = pickResolvedUsername( + host.username, + credential.username, + host.overrideCredentialUsername, + ); if (credential.password) { baseHost.password = credential.password; @@ -1005,7 +1028,7 @@ async function buildSshConfig( cause: keyError, }); } - } else if (host.authType === "none") { + } else if (host.authType === "none" || host.authType === "tailscale") { // no credentials needed } else if (host.authType === "opkssh") { // cert auth setup happens in createSshFactory (needs client instance) @@ -1478,7 +1501,7 @@ function tcpPing( * summary: Get all host statuses * description: Retrieves the status of all hosts for the authenticated user. * tags: - * - Server Stats + * - Host Metrics * responses: * 200: * description: A map of host IDs to their status entries. @@ -1514,7 +1537,7 @@ app.get("/status", async (req, res) => { * summary: Get host status by ID * description: Retrieves the status of a specific host by its ID. * tags: - * - Server Stats + * - Host Metrics * parameters: * - in: path * name: id @@ -1560,7 +1583,7 @@ app.get("/status/:id", validateHostId, async (req, res) => { * summary: Clear all SSH connections * description: Clears all SSH connections from the connection pool. * tags: - * - Server Stats + * - Host Metrics * responses: * 200: * description: All SSH connections cleared. @@ -1588,7 +1611,7 @@ app.post("/clear-connections", async (req, res) => { * summary: Refresh polling * description: Clears all SSH connections and refreshes host polling. * tags: - * - Server Stats + * - Host Metrics * responses: * 200: * description: Polling refreshed. @@ -1618,7 +1641,7 @@ app.post("/refresh", async (req, res) => { * summary: Start polling for updated host * description: Starts polling for a specific host after it has been updated. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: @@ -1682,7 +1705,7 @@ app.post("/host-updated", async (req, res) => { * summary: Stop polling for deleted host * description: Stops polling for a specific host after it has been deleted. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: @@ -1737,7 +1760,7 @@ app.post("/host-deleted", async (req, res) => { * summary: Get host metrics * description: Retrieves current metrics for a specific host including CPU, memory, disk, network, processes, and system information. * tags: - * - Server Stats + * - Host Metrics * parameters: * - in: path * name: id @@ -1796,7 +1819,7 @@ app.get("/metrics/:id", validateHostId, async (req, res) => { * summary: Start metrics collection * description: Establishes an SSH connection and starts collecting metrics for a specific host. * tags: - * - Server Stats + * - Host Metrics * parameters: * - in: path * name: id @@ -2232,7 +2255,7 @@ app.post("/metrics/start/:id", validateHostId, async (req, res) => { * summary: Stop metrics collection * description: Stops metrics collection for a specific host and cleans up the SSH session. * tags: - * - Server Stats + * - Host Metrics * parameters: * - in: path * name: id @@ -2305,7 +2328,7 @@ app.post("/metrics/stop/:id", validateHostId, async (req, res) => { * summary: Complete TOTP verification for metrics * description: Verifies the TOTP code and completes the metrics SSH connection. * tags: - * - Server Stats + * - Host Metrics * requestBody: * required: true * content: @@ -2470,7 +2493,7 @@ app.post("/metrics/connect-totp", async (req, res) => { } }); -registerServerStatsViewerRoutes(app, { +registerHostMetricsViewerRoutes(app, { fetchHostById, supportsMetrics: (host: SSHHostWithCredentials) => supportsMetrics(host), parseStatsConfig: (statsConfig: SSHHostWithCredentials["statsConfig"]) => @@ -2483,12 +2506,52 @@ registerServerStatsViewerRoutes(app, { pollingManager.unregisterViewer(hostId, viewerSessionId), }); -registerServerStatsSettingsRoutes(app, { +registerHostMetricsSettingsRoutes(app, { requireAdmin, defaultStatsConfig: DEFAULT_STATS_CONFIG, refreshAllPolling: () => pollingManager.refreshAllPolling(), }); +registerHostMetricsPreferencesRoutes(app, { + validateHostId, + fetchHostById: async (hostId, userId) => + (await fetchHostById(hostId, userId)) ?? null, + parseStatsConfig: (statsConfig) => + pollingManager.parseStatsConfig( + statsConfig as string | StatsConfig | undefined, + ), + canAccessHost: async (userId, hostId, level) => + (await permissionManager.canAccessHost(userId, hostId, level)).hasAccess, +}); + +registerManagerRoutes(app, { + validateHostId, + runOnHost: async (hostId, userId, level, fn) => { + const access = await permissionManager.canAccessHost(userId, hostId, level); + if (!access.hasAccess) { + throw new AccessDeniedError(); + } + const host = await fetchHostById(hostId, userId); + if (!host) { + throw new AccessDeniedError("Host not found"); + } + // sudoPassword is encrypted with the owner's key; when the viewer is a + // different user, re-fetch under the owner's key to decrypt it correctly. + let sudoPassword = host.sudoPassword; + if (host.userId && host.userId !== userId) { + const ownerHost = await fetchHostById(hostId, host.userId); + if (ownerHost) sudoPassword = ownerHost.sudoPassword; + } + const managerHost: ManagerHost = { + id: host.id, + userId: host.userId, + sudoPassword, + enableDocker: !!(host as { enableDocker?: boolean }).enableDocker, + }; + return withSshConnection(host, (client) => fn(client, managerHost)); + }, +}); + process.on("SIGINT", () => { pollingManager.destroy(); connectionPool.destroy(); diff --git a/src/backend/ssh/host-resolver.ts b/src/backend/ssh/host-resolver.ts index da2dc99b..091a6c53 100644 --- a/src/backend/ssh/host-resolver.ts +++ b/src/backend/ssh/host-resolver.ts @@ -3,6 +3,7 @@ import { hosts, sshCredentials } from "../database/db/schema.js"; import { eq, and } from "drizzle-orm"; import { SimpleDBOps } from "../utils/simple-db-ops.js"; import { logger } from "../utils/logger.js"; +import { pickResolvedUsername } from "./credential-username.js"; import type { SSHHost } from "../../types/index.js"; const sshLogger = logger; @@ -109,9 +110,11 @@ export async function resolveHostById( host.key = cred.key; host.keyPassword = cred.keyPassword; host.keyType = cred.keyType; - if (!host.overrideCredentialUsername) { - host.username = cred.username; - } + host.username = pickResolvedUsername( + host.username, + cred.username, + host.overrideCredentialUsername, + ); host.authType = cred.key ? "key" : cred.password @@ -137,9 +140,11 @@ export async function resolveHostById( host.key = sharedCred.key; host.keyPassword = sharedCred.keyPassword; host.keyType = sharedCred.keyType; - if (!host.overrideCredentialUsername) { - host.username = sharedCred.username; - } + host.username = pickResolvedUsername( + host.username, + sharedCred.username, + host.overrideCredentialUsername, + ); host.authType = sharedCred.key ? "key" : sharedCred.password @@ -182,9 +187,11 @@ export async function resolveHostById( // CA-signed certificate for cert-based auth (host as Record).certPublicKey = cred.certPublicKey || null; - if (!host.overrideCredentialUsername) { - host.username = cred.username; - } + host.username = pickResolvedUsername( + host.username, + cred.username, + host.overrideCredentialUsername, + ); host.authType = host.key ? "key" : host.password ? "password" : "none"; } } catch (e) { diff --git a/src/backend/ssh/managers/cron.ts b/src/backend/ssh/managers/cron.ts new file mode 100644 index 00000000..ce587db0 --- /dev/null +++ b/src/backend/ssh/managers/cron.ts @@ -0,0 +1,127 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { shellSingleQuote } from "./exec-elevated.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface CronEntry { + raw: string; + enabled: boolean; + schedule: string; + command: string; +} + +const READ_CRONTAB_CMD = "crontab -l 2>/dev/null"; + +/** Parse a crontab into entries (comments/blank lines are dropped except as toggles). */ +export function parseCrontab(output: string): CronEntry[] { + const entries: CronEntry[] = []; + for (const raw of output.split("\n")) { + const line = raw.replace(/\s+$/, ""); + if (!line.trim()) continue; + // A commented-out job: "# " (our toggle convention). + const disabled = line.match( + /^#\s*((?:@\w+|\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+.+)$/, + ); + if (disabled) { + const { schedule, command } = splitScheduleCommand(disabled[1]); + if (command) { + entries.push({ raw: line, enabled: false, schedule, command }); + continue; + } + } + // Skip pure comments / env assignments. + if (line.trimStart().startsWith("#")) continue; + if (/^\s*[A-Z_]+=/.test(line)) continue; + const { schedule, command } = splitScheduleCommand(line.trim()); + if (!command) continue; + entries.push({ raw: line, enabled: true, schedule, command }); + } + return entries; +} + +function splitScheduleCommand(line: string): { + schedule: string; + command: string; +} { + if (line.startsWith("@")) { + const [sched, ...rest] = line.split(/\s+/); + return { schedule: sched, command: rest.join(" ") }; + } + const parts = line.split(/\s+/); + if (parts.length < 6) return { schedule: "", command: "" }; + return { + schedule: parts.slice(0, 5).join(" "), + command: parts.slice(5).join(" "), + }; +} + +const CRON_SCHEDULE_RE = + /^(@(reboot|yearly|annually|monthly|weekly|daily|midnight|hourly)|[\d*/,-]+\s+[\d*/,-]+\s+[\d*/,-]+\s+[\d*/,A-Za-z-]+\s+[\d*/,A-Za-z-]+)$/; + +export function isValidCronSchedule(schedule: string): boolean { + return CRON_SCHEDULE_RE.test(schedule.trim()); +} + +/** Serialize entries into a full crontab body (toggled entries are commented). */ +export function serializeCrontab(entries: CronEntry[]): string { + const lines = entries.map((e) => { + const body = `${e.schedule} ${e.command}`.trim(); + return e.enabled ? body : `# ${body}`; + }); + return lines.join("\n") + (lines.length ? "\n" : ""); +} + +/** Build a command that atomically replaces the user crontab from the given body. */ +export function buildApplyCrontabCommand(body: string): string { + // printf the exact bytes into `crontab -` (reads new crontab from stdin). + return `printf '%s' ${shellSingleQuote(body)} | crontab -`; +} + +export function registerCronRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/cron/:id", + validateHostId, + managerHandler(runOnHost, "read", "cron_list", async (client) => { + const { stdout } = await execCommand(client, READ_CRONTAB_CMD, 15000); + return { entries: parseCrontab(stdout) }; + }), + ); + + app.post( + "/host-metrics/managers/cron/:id", + validateHostId, + managerHandler( + runOnHost, + "execute", + "cron_replace", + async (client, _host, req) => { + const { entries } = req.body as { entries?: CronEntry[] }; + if (!Array.isArray(entries)) { + throw new ManagerInputError("entries must be an array"); + } + for (const e of entries) { + if (typeof e?.command !== "string" || !e.command.trim()) { + throw new ManagerInputError("Each entry needs a command"); + } + if (e.command.includes("\n")) { + throw new ManagerInputError("Commands cannot contain newlines"); + } + if (!isValidCronSchedule(String(e.schedule))) { + throw new ManagerInputError(`Invalid schedule: ${e.schedule}`); + } + } + const body = serializeCrontab(entries); + const { stdout, stderr, code } = await execCommand( + client, + buildApplyCrontabCommand(body), + 15000, + ); + return { success: code === 0, output: stdout || stderr }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/exec-elevated.test.ts b/src/backend/ssh/managers/exec-elevated.test.ts new file mode 100644 index 00000000..dbe992c0 --- /dev/null +++ b/src/backend/ssh/managers/exec-elevated.test.ts @@ -0,0 +1,108 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +const execCommand = vi.fn(); +vi.mock("../widgets/common-utils.js", () => ({ + execCommand: (...args: unknown[]) => execCommand(...args), +})); + +import { execElevated, ElevationError } from "./exec-elevated.js"; +import type { Client } from "ssh2"; + +const fakeClient = {} as Client; + +function result(stdout: string, stderr = "", code: number | null = 0) { + return { stdout, stderr, code }; +} + +beforeEach(() => { + execCommand.mockReset(); +}); + +describe("execElevated (no force)", () => { + it("returns the direct result when the command succeeds unprivileged", async () => { + execCommand.mockResolvedValueOnce(result("hello", "", 0)); + const r = await execElevated(fakeClient, "echo hello", "pw"); + expect(r.usedSudo).toBe(false); + expect(r.stdout).toBe("hello"); + expect(execCommand).toHaveBeenCalledTimes(1); + }); + + it("does NOT escalate when stdout merely contains scary words", async () => { + // A non-zero exit whose OUTPUT contains 'permission denied' but stderr does + // not should be surfaced as-is, never retried under sudo. + execCommand.mockResolvedValueOnce( + result("log line: permission denied for user foo", "", 1), + ); + const r = await execElevated(fakeClient, "grep denied /tmp/app.log", "pw"); + expect(r.usedSudo).toBe(false); + expect(execCommand).toHaveBeenCalledTimes(1); + }); + + it("escalates when stderr indicates a permission problem", async () => { + execCommand + .mockResolvedValueOnce(result("", "Permission denied", 1)) + .mockResolvedValueOnce(result("__TX_SUDO_OK__\nelevated output", "", 0)); + const r = await execElevated(fakeClient, "cat /etc/shadow", "pw"); + expect(r.usedSudo).toBe(true); + expect(r.stdout).toBe("elevated output"); + expect(execCommand).toHaveBeenCalledTimes(2); + }); + + it("throws SUDO_REQUIRED when elevation is needed but no password is set", async () => { + execCommand.mockResolvedValueOnce(result("", "Permission denied", 1)); + await expect( + execElevated(fakeClient, "cat /etc/shadow", undefined), + ).rejects.toMatchObject({ code: "SUDO_REQUIRED" }); + }); +}); + +describe("execElevated (forced)", () => { + it("strips the success marker from stdout", async () => { + execCommand.mockResolvedValueOnce( + result("__TX_SUDO_OK__\nthe real output\n", "", 0), + ); + const r = await execElevated(fakeClient, "id", "pw", { forceSudo: true }); + expect(r.stdout).toBe("the real output\n"); + expect(r.usedSudo).toBe(true); + }); + + it("does NOT throw when command output contains 'incorrect password' but sudo authenticated", async () => { + execCommand.mockResolvedValueOnce( + result( + "__TX_SUDO_OK__\nUser entered an incorrect password earlier", + "", + 0, + ), + ); + const r = await execElevated(fakeClient, "tail /var/log/auth.log", "pw", { + forceSudo: true, + }); + expect(r.usedSudo).toBe(true); + expect(r.stdout).toContain("incorrect password"); + }); + + it("throws SUDO_FAILED on a real wrong-password (no marker, sudo stderr)", async () => { + execCommand.mockResolvedValueOnce( + result("", "sudo: 1 incorrect password attempt", 1), + ); + await expect( + execElevated(fakeClient, "id", "wrong", { forceSudo: true }), + ).rejects.toMatchObject({ code: "SUDO_FAILED" }); + }); + + it("throws NOT_SUDOER when the user is not in sudoers", async () => { + execCommand.mockResolvedValueOnce( + result("", "deploy is not in the sudoers file.", 1), + ); + await expect( + execElevated(fakeClient, "id", "pw", { forceSudo: true }), + ).rejects.toMatchObject({ code: "NOT_SUDOER" }); + }); + + it("throws SUDO_REQUIRED when forced without a password", async () => { + await expect( + execElevated(fakeClient, "id", undefined, { forceSudo: true }), + ).rejects.toBeInstanceOf(ElevationError); + expect(execCommand).not.toHaveBeenCalled(); + }); +}); diff --git a/src/backend/ssh/managers/exec-elevated.ts b/src/backend/ssh/managers/exec-elevated.ts new file mode 100644 index 00000000..ed958905 --- /dev/null +++ b/src/backend/ssh/managers/exec-elevated.ts @@ -0,0 +1,169 @@ +import type { Client } from "ssh2"; +import { execCommand } from "../widgets/common-utils.js"; + +export type ElevationErrorCode = "SUDO_REQUIRED" | "SUDO_FAILED" | "NOT_SUDOER"; + +export class ElevationError extends Error { + code: ElevationErrorCode; + constructor(code: ElevationErrorCode, message: string) { + super(message); + this.name = "ElevationError"; + this.code = code; + } +} + +export interface ElevatedResult { + stdout: string; + stderr: string; + code: number | null; + usedSudo: boolean; +} + +const PERMISSION_DENIED = [ + "permission denied", + "operation not permitted", + "must be run as root", + "must be superuser", + "you need to be root", + "are you root", + "access denied", +]; + +/** + * Phrases sudo itself prints to STDERR when authentication or authorization + * fails. We only match these against sudo's stderr (never command output), so a + * command that happens to print "permission denied" on stdout is not mistaken + * for a sudo failure. + */ +const SUDO_AUTH_FAILED = [ + "incorrect password", + "a password is required", + "a terminal is required", + "no tty present", + "sorry, try again", + "no password was provided", + "1 incorrect password attempt", +]; + +const SUDO_NOT_SUDOER = [ + "is not in the sudoers file", + "not allowed to run sudo", + "not allowed to execute", +]; + +/** + * Marker printed only after sudo has successfully authenticated and started the + * inner shell. Its presence on stdout is the authoritative "elevation worked" + * signal; its absence (together with sudo stderr) means auth failed. + */ +const SUDO_OK_MARKER = "__TX_SUDO_OK__"; +const SUDO_OK_LINE_RE = new RegExp(`^${SUDO_OK_MARKER}\\r?\\n?`); + +/** Escape a value for single-quoted shell context. */ +export function shellSingleQuote(value: string): string { + return `'${value.replace(/'/g, "'\"'\"'")}'`; +} + +/** + * Build the elevated command string: + * echo '' | sudo -S -p '' sh -c 'echo __TX_SUDO_OK__; ' + * + * `-p ''` suppresses the prompt. stderr is NOT merged into stdout, so the + * command's own output stays clean and sudo's auth errors stay on stderr. The + * marker is echoed by the inner shell once sudo authenticates, letting us tell a + * genuine auth failure from command output that merely contains scary words. + */ +export function buildSudoCommand( + command: string, + sudoPassword: string, +): string { + const pw = shellSingleQuote(sudoPassword); + const inner = shellSingleQuote(`echo ${SUDO_OK_MARKER}; ${command}`); + return `echo ${pw} | sudo -S -p '' sh -c ${inner}`; +} + +function includesAny(text: string, needles: string[]): boolean { + const lower = text.toLowerCase(); + return needles.some((n) => lower.includes(n)); +} + +function looksLikePermissionDenied(text: string): boolean { + return includesAny(text, PERMISSION_DENIED); +} + +/** Strip the success marker line from the front of stdout. */ +function stripMarker(stdout: string): string { + return stdout.replace(SUDO_OK_LINE_RE, ""); +} + +/** + * Run a command on a pooled client, elevating with the host's stored sudo + * password only when needed (or when forced). Throws a typed `ElevationError` + * when elevation is required but unavailable/incorrect. + */ +export async function execElevated( + client: Client, + command: string, + sudoPassword: string | undefined, + opts: { forceSudo?: boolean; timeoutMs?: number } = {}, +): Promise { + const timeoutMs = opts.timeoutMs ?? 30000; + + if (!opts.forceSudo) { + const direct = await execCommand(client, command, timeoutMs); + if (direct.code === 0) { + return { ...direct, usedSudo: false }; + } + // Only escalate when the failure looks like a privilege problem. A + // permission-denied phrase on stderr (not arbitrary stdout) is the signal. + if (!looksLikePermissionDenied(direct.stderr)) { + return { ...direct, usedSudo: false }; + } + if (!sudoPassword) { + throw new ElevationError( + "SUDO_REQUIRED", + "This action requires elevated privileges. Set a sudo password for this host to continue.", + ); + } + } else if (!sudoPassword) { + throw new ElevationError( + "SUDO_REQUIRED", + "This action requires elevated privileges. Set a sudo password for this host to continue.", + ); + } + + const sudoCmd = buildSudoCommand(command, sudoPassword as string); + const result = await execCommand(client, sudoCmd, timeoutMs); + const authenticated = result.stdout.includes(SUDO_OK_MARKER); + + if (!authenticated) { + // Elevation never started: diagnose from sudo's stderr only. + const notSudoer = includesAny(result.stderr, SUDO_NOT_SUDOER); + const authFailed = includesAny(result.stderr, SUDO_AUTH_FAILED); + if (notSudoer) { + throw new ElevationError( + "NOT_SUDOER", + "The connected user is not permitted to use sudo on this host.", + ); + } + if (authFailed) { + throw new ElevationError( + "SUDO_FAILED", + "Elevation failed. Check the host's sudo password.", + ); + } + // No marker and no recognizable sudo error: treat as a generic failure but + // keep the original output so the caller can surface it. + throw new ElevationError( + "SUDO_FAILED", + "Elevation failed. Check the host's sudo password.", + ); + } + + return { + stdout: stripMarker(result.stdout), + stderr: result.stderr, + code: result.code, + usedSudo: true, + }; +} diff --git a/src/backend/ssh/managers/firewall.ts b/src/backend/ssh/managers/firewall.ts new file mode 100644 index 00000000..32f80f42 --- /dev/null +++ b/src/backend/ssh/managers/firewall.ts @@ -0,0 +1,129 @@ +import type { Express } from "express"; +import { collectFirewallMetrics } from "../widgets/firewall-collector.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { + isValidPort, + isValidIpProtocol, + isValidFirewallTarget, + type IpProtocol, + type FirewallTarget, +} from "./validation.js"; +import { detectPlatform } from "./platform.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface FirewallRuleSpec { + protocol: IpProtocol; + port: number; + target: FirewallTarget; +} + +/** + * Build an iptables add/delete for an INPUT rule. We only ever touch INPUT for a + * specific dport with an explicit target, and never the chain policy, so an + * existing ESTABLISHED/SSH rule is left intact. + */ +export function buildIptablesRuleCommand( + op: "add" | "delete", + spec: FirewallRuleSpec, +): string { + const flag = op === "add" ? "-A" : "-D"; + return `iptables ${flag} INPUT -p ${spec.protocol} --dport ${spec.port} -j ${spec.target}`; +} + +export function buildNftRuleCommand( + op: "add" | "delete", + spec: FirewallRuleSpec, +): string { + // nftables uses the inet filter table's input chain by convention. + const verb = op === "add" ? "add" : "delete"; + const action = + spec.target.toLowerCase() === "reject" + ? "reject" + : spec.target.toLowerCase(); + return `nft ${verb} rule inet filter input ${spec.protocol} dport ${spec.port} ${action}`; +} + +export function registerFirewallRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/firewall/:id", + validateHostId, + managerHandler(runOnHost, "read", "firewall_read", async (client) => { + return await collectFirewallMetrics(client); + }), + ); + + app.post( + "/host-metrics/managers/firewall/:id/rule", + validateHostId, + managerHandler( + runOnHost, + "execute", + "firewall_rule", + async (client, host, req) => { + const { op, protocol, port, target } = req.body as { + op?: "add" | "delete"; + protocol?: string; + port?: number; + target?: string; + }; + if (op !== "add" && op !== "delete") { + throw new ManagerInputError("Invalid op"); + } + if (!isValidIpProtocol(protocol)) + throw new ManagerInputError("Invalid protocol"); + if (!isValidPort(port)) throw new ManagerInputError("Invalid port"); + if (!isValidFirewallTarget(target)) + throw new ManagerInputError("Invalid target"); + + const spec: FirewallRuleSpec = { + protocol, + port: Number(port), + target, + }; + const fw = await collectFirewallMetrics(client); + const cmd = + fw.type === "nftables" + ? buildNftRuleCommand(op, spec) + : buildIptablesRuleCommand(op, spec); + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + }); + return { + success: result.code === 0, + output: result.stdout || result.stderr, + backend: fw.type, + }; + }, + ), + ); + + app.post( + "/host-metrics/managers/firewall/:id/persist", + validateHostId, + managerHandler( + runOnHost, + "execute", + "firewall_persist", + async (client, host) => { + const platform = await detectPlatform(client); + // Best-effort persistence across common tools. + const cmd = + "(command -v netfilter-persistent >/dev/null 2>&1 && netfilter-persistent save) || " + + "(command -v service >/dev/null 2>&1 && service iptables save) || " + + "(command -v nft >/dev/null 2>&1 && nft list ruleset > /etc/nftables.conf) || true"; + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + }); + return { + success: result.code === 0, + output: result.stdout || result.stderr, + pkg: platform.pkg, + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/health.ts b/src/backend/ssh/managers/health.ts new file mode 100644 index 00000000..e9bb36e6 --- /dev/null +++ b/src/backend/ssh/managers/health.ts @@ -0,0 +1,257 @@ +import type { Express } from "express"; +import type { Client } from "ssh2"; +import type { AuthenticatedRequest } from "../../../types/index.js"; +import { execCommand } from "../widgets/common-utils.js"; +import { getDb, DatabaseSaveTrigger } from "../../database/db/index.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { shellSingleQuote } from "./exec-elevated.js"; +import { isValidPort } from "./validation.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface HealthCheck { + id: string; + name: string; + type: "tcp" | "http"; + target: string; + port?: number; + path?: string; +} + +export interface HealthResult { + checkId: string; + ok: boolean; + latencyMs: number | null; + detail: string; +} + +const TARGET_RE = /^[A-Za-z0-9.\-_:]+$/; +const PATH_RE = /^\/[A-Za-z0-9._~!$&'()*+,;=:@/%-]*$/; + +export function isValidHealthCheck(c: unknown): c is HealthCheck { + if (!c || typeof c !== "object") return false; + const o = c as Record; + if (typeof o.id !== "string" || !o.id) return false; + if (typeof o.name !== "string") return false; + if (o.type !== "tcp" && o.type !== "http") return false; + if (typeof o.target !== "string" || !TARGET_RE.test(o.target)) return false; + if (o.type === "tcp" && !isValidPort(o.port)) return false; + if ( + o.path !== undefined && + (typeof o.path !== "string" || !PATH_RE.test(o.path)) + ) + return false; + return true; +} + +/** Build a command that runs the check from the host and prints "ok latency". */ +export function buildHealthCheckCommand(check: HealthCheck): string { + if (check.type === "tcp") { + const host = shellSingleQuote(check.target); + const port = check.port; + // Prefer bash /dev/tcp; time it with date in ms. + return `start=$(date +%s%3N); if timeout 3 bash -c '/dev/null; then echo "ok $(( $(date +%s%3N) - start ))"; else echo "fail $(( $(date +%s%3N) - start ))"; fi`; + } + // http + const scheme = check.target.includes("://") ? "" : "http://"; + const url = shellSingleQuote(`${scheme}${check.target}${check.path ?? ""}`); + return `curl -s -o /dev/null -m 5 -w '%{http_code} %{time_total}' ${url} || echo '000 0'`; +} + +export function parseHealthResult( + check: HealthCheck, + output: string, +): HealthResult { + const line = output.trim().split("\n").pop() ?? ""; + if (check.type === "tcp") { + const [status, ms] = line.split(/\s+/); + return { + checkId: check.id, + ok: status === "ok", + latencyMs: Number(ms) || null, + detail: status === "ok" ? "open" : "closed/timeout", + }; + } + const m = line.match(/^(\d{3})\s+([\d.]+)/); + if (!m) + return { checkId: check.id, ok: false, latencyMs: null, detail: line }; + const code = Number(m[1]); + return { + checkId: check.id, + ok: code >= 200 && code < 400, + latencyMs: Math.round(Number(m[2]) * 1000), + detail: `HTTP ${code}`, + }; +} + +async function runChecks( + client: Client, + checks: HealthCheck[], +): Promise { + return Promise.all( + checks.map(async (check) => { + try { + const { stdout } = await execCommand( + client, + buildHealthCheckCommand(check), + 8000, + ); + return parseHealthResult(check, stdout); + } catch { + return { + checkId: check.id, + ok: false, + latencyMs: null, + detail: "error", + }; + } + }), + ); +} + +const HISTORY_KEEP = 500; + +function recordHistory( + userId: string, + hostId: number, + results: HealthResult[], +): void { + const db = getDb(); + const now = new Date().toISOString(); + const insert = db.$client.prepare( + "INSERT INTO host_health_history (user_id, host_id, check_id, ts, ok, latency_ms, detail) VALUES (?, ?, ?, ?, ?, ?, ?)", + ); + for (const r of results) { + insert.run( + userId, + hostId, + r.checkId, + now, + r.ok ? 1 : 0, + r.latencyMs, + r.detail, + ); + } + // Prune to the most recent HISTORY_KEEP rows per (host, check). + db.$client + .prepare( + `DELETE FROM host_health_history + WHERE id IN ( + SELECT id FROM host_health_history + WHERE user_id = ? AND host_id = ? + AND id NOT IN ( + SELECT id FROM host_health_history + WHERE user_id = ? AND host_id = ? + ORDER BY ts DESC LIMIT ? + ) + )`, + ) + .run(userId, hostId, userId, hostId, HISTORY_KEEP); +} + +function loadChecks(userId: string, hostId: number): HealthCheck[] { + const row = getDb() + .$client.prepare( + "SELECT checks FROM host_health_checks WHERE user_id = ? AND host_id = ?", + ) + .get(userId, hostId) as { checks: string } | undefined; + if (!row?.checks) return []; + try { + const parsed = JSON.parse(row.checks); + return Array.isArray(parsed) ? parsed.filter(isValidHealthCheck) : []; + } catch { + return []; + } +} + +export function registerHealthRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/health/:id", + validateHostId, + managerHandler( + runOnHost, + "read", + "health_get", + async (client, host, req) => { + const userId = (req as AuthenticatedRequest).userId; + const checks = loadChecks(userId, host.id); + const results = checks.length ? await runChecks(client, checks) : []; + if (results.length) recordHistory(userId, host.id, results); + + const history = getDb() + .$client.prepare( + "SELECT check_id as checkId, ts, ok, latency_ms as latencyMs, detail FROM host_health_history WHERE user_id = ? AND host_id = ? ORDER BY ts DESC LIMIT 200", + ) + .all(userId, host.id); + return { checks, results, history }; + }, + ), + ); + + app.post( + "/host-metrics/managers/health/:id/config", + validateHostId, + managerHandler( + runOnHost, + "read", + "health_config", + async (_client, host, req) => { + const userId = (req as AuthenticatedRequest).userId; + const { checks, intervalSeconds } = req.body as { + checks?: unknown; + intervalSeconds?: number; + }; + if (!Array.isArray(checks) || !checks.every(isValidHealthCheck)) { + throw new ManagerInputError("Invalid checks"); + } + const interval = + typeof intervalSeconds === "number" && + intervalSeconds >= 30 && + intervalSeconds <= 86400 + ? Math.round(intervalSeconds) + : 300; + const db = getDb(); + const now = new Date().toISOString(); + const existing = db.$client + .prepare( + "SELECT id FROM host_health_checks WHERE user_id = ? AND host_id = ?", + ) + .get(userId, host.id) as { id: number } | undefined; + if (existing) { + db.$client + .prepare( + "UPDATE host_health_checks SET checks = ?, interval_seconds = ?, updated_at = ? WHERE id = ?", + ) + .run(JSON.stringify(checks), interval, now, existing.id); + } else { + db.$client + .prepare( + "INSERT INTO host_health_checks (user_id, host_id, checks, interval_seconds, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", + ) + .run(userId, host.id, JSON.stringify(checks), interval, now, now); + } + DatabaseSaveTrigger.triggerSave("host_health_checks_updated"); + return { success: true }; + }, + ), + ); + + app.post( + "/host-metrics/managers/health/:id/run", + validateHostId, + managerHandler( + runOnHost, + "read", + "health_run", + async (client, host, req) => { + const userId = (req as AuthenticatedRequest).userId; + const checks = loadChecks(userId, host.id); + const results = await runChecks(client, checks); + if (results.length) recordHistory(userId, host.id, results); + return { results }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/index.ts b/src/backend/ssh/managers/index.ts new file mode 100644 index 00000000..e2da7550 --- /dev/null +++ b/src/backend/ssh/managers/index.ts @@ -0,0 +1,61 @@ +import type { Express } from "express"; +import { detectPlatform } from "./platform.js"; +import { managerHandler } from "./route-helpers.js"; +import type { ManagerRoutesDeps } from "./types.js"; +import { registerServiceRoutes } from "./services.js"; +import { registerProcessRoutes } from "./processes.js"; +import { registerSimpleReadRoutes } from "./simple-reads.js"; +import { registerCronRoutes } from "./cron.js"; +import { registerPackageRoutes } from "./packages.js"; +import { registerSslRoutes } from "./ssl.js"; +import { registerFirewallRoutes } from "./firewall.js"; +import { registerUserRoutes } from "./users.js"; +import { registerHealthRoutes } from "./health.js"; +import { registerLogRoutes } from "./logs.js"; + +/** + * Registers every Host Metrics manager route under the `/host-metrics/managers` + * prefix on the stats app. All routes are on-demand (not polled). + */ +export function registerManagerRoutes( + app: Express, + deps: ManagerRoutesDeps, +): void { + const { validateHostId, runOnHost } = deps; + + /** + * @openapi + * /host-metrics/platform/{id}: + * get: + * summary: Detect available management tooling on a host + * tags: + * - Host Metrics + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Platform capabilities (systemd, package manager, certbot, docker). + */ + app.get( + "/host-metrics/platform/:id", + validateHostId, + managerHandler(runOnHost, "read", "platform_detect", (client) => + detectPlatform(client), + ), + ); + + registerServiceRoutes(app, deps); + registerProcessRoutes(app, deps); + registerSimpleReadRoutes(app, deps); + registerCronRoutes(app, deps); + registerPackageRoutes(app, deps); + registerSslRoutes(app, deps); + registerFirewallRoutes(app, deps); + registerUserRoutes(app, deps); + registerHealthRoutes(app, deps); + registerLogRoutes(app, deps); +} diff --git a/src/backend/ssh/managers/logs.ts b/src/backend/ssh/managers/logs.ts new file mode 100644 index 00000000..e717759f --- /dev/null +++ b/src/backend/ssh/managers/logs.ts @@ -0,0 +1,92 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { shellSingleQuote } from "./exec-elevated.js"; +import { isAllowedPath, isValidSystemdUnit } from "./validation.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +/** Directories from which arbitrary log files may be tailed. */ +export const LOG_PATH_ALLOWLIST = ["/var/log"]; + +const COMMON_LOGS = [ + "/var/log/syslog", + "/var/log/messages", + "/var/log/auth.log", + "/var/log/secure", + "/var/log/kern.log", + "/var/log/dpkg.log", + "/var/log/nginx/access.log", + "/var/log/nginx/error.log", +]; + +const LIST_LOGS_CMD = `ls -1 ${LOG_PATH_ALLOWLIST.map(shellSingleQuote).join(" ")} 2>/dev/null`; + +export function clampLines(n: unknown): number { + const v = typeof n === "string" ? Number(n) : n; + if (typeof v !== "number" || !Number.isFinite(v)) return 200; + return Math.min(2000, Math.max(1, Math.round(v))); +} + +export function buildTailCommand(path: string, lines: number): string { + // Keep stderr intact so execElevated can detect a permission error and + // escalate; suppressing it (2>/dev/null) would hide the denial and return an + // empty log with no chance to retry under sudo. + return `tail -n ${lines} ${shellSingleQuote(path)}`; +} + +export function buildJournalCommand(unit: string, lines: number): string { + return `journalctl -u ${shellSingleQuote(unit)} -n ${lines} --no-pager`; +} + +export function registerLogRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/logs/:id/files", + validateHostId, + managerHandler(runOnHost, "read", "logs_list", async (client) => { + const { stdout } = await execCommand(client, LIST_LOGS_CMD, 10000); + const found = stdout + .split("\n") + .map((l) => l.trim()) + .filter(Boolean) + .map((name) => `/var/log/${name}`); + return { common: COMMON_LOGS, files: found }; + }), + ); + + app.get( + "/host-metrics/managers/logs/:id", + validateHostId, + managerHandler( + runOnHost, + "read", + "logs_tail", + async (client, host, req) => { + const path = req.query.path as string | undefined; + const unit = req.query.unit as string | undefined; + const lines = clampLines(req.query.lines); + + let cmd: string; + if (unit) { + if (!isValidSystemdUnit(unit)) + throw new ManagerInputError("Invalid unit"); + cmd = buildJournalCommand(unit, lines); + } else if (path) { + if (!isAllowedPath(path, LOG_PATH_ALLOWLIST)) { + throw new ManagerInputError("Path not allowed"); + } + cmd = buildTailCommand(path, lines); + } else { + throw new ManagerInputError("Provide a path or unit"); + } + + // Try unprivileged; many logs need root (auth.log, etc.). + const result = await execElevated(client, cmd, host.sudoPassword); + return { content: result.stdout, lines }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/managers.test.ts b/src/backend/ssh/managers/managers.test.ts new file mode 100644 index 00000000..703076a1 --- /dev/null +++ b/src/backend/ssh/managers/managers.test.ts @@ -0,0 +1,416 @@ +import { describe, it, expect } from "vitest"; +import { buildSudoCommand, shellSingleQuote } from "./exec-elevated.js"; +import { parsePlatformProbe } from "./platform.js"; +import { + isValidSystemdUnit, + isValidPid, + isValidPort, + isValidPackageName, + isValidUsername, + isValidDomain, + isValidDnsProvider, + isValidSignal, + isValidServiceAction, + isAllowedPath, +} from "./validation.js"; +import { parseServiceList, buildServiceActionCommand } from "./services.js"; +import { parseProcessList, buildKillCommand } from "./processes.js"; +import { parseDfMounts, parseTopMemory } from "./simple-reads.js"; +import { + parseCrontab, + serializeCrontab, + isValidCronSchedule, + buildApplyCrontabCommand, +} from "./cron.js"; +import { + buildPackageActionCommand, + parseUpgradable, + buildListUpgradableCommand, +} from "./packages.js"; +import { + buildIssueCommand, + buildRenewCommand, + buildRevokeCommand, + isValidCertName, + parseCertbotCertificates, +} from "./ssl.js"; +import { buildIptablesRuleCommand, buildNftRuleCommand } from "./firewall.js"; +import { parsePasswd, parseSudoers } from "./users.js"; +import { buildHealthCheckCommand, parseHealthResult } from "./health.js"; +import { buildTailCommand, clampLines } from "./logs.js"; + +describe("exec-elevated", () => { + it("single-quotes and escapes for the shell", () => { + expect(shellSingleQuote("abc")).toBe("'abc'"); + expect(shellSingleQuote("a'b")).toBe(`'a'"'"'b'`); + }); + it("builds the sudo -S pipeline wrapping the command in sh -c with a success marker", () => { + expect(buildSudoCommand("systemctl restart nginx", "pw")).toBe( + `echo 'pw' | sudo -S -p '' sh -c 'echo __TX_SUDO_OK__; systemctl restart nginx'`, + ); + }); + it("does not merge stderr into stdout (no 2>&1)", () => { + expect(buildSudoCommand("id", "pw")).not.toContain("2>&1"); + }); + it("escapes a password containing a quote", () => { + expect(buildSudoCommand("id", "p'w")).toContain(`echo 'p'"'"'w'`); + }); +}); + +describe("platform probe parsing", () => { + it("parses capabilities and prefers dnf over yum", () => { + const out = [ + "systemd=1", + "apt=0", + "dnf=1", + "yum=1", + "pacman=0", + "certbot=1", + "acmesh=0", + "docker=1", + "os=Fedora Linux 40", + ].join("\n"); + const p = parsePlatformProbe(out); + expect(p.hasSystemd).toBe(true); + expect(p.pkg).toBe("dnf"); + expect(p.hasCertbot).toBe(true); + expect(p.hasAcmeSh).toBe(false); + expect(p.hasDocker).toBe(true); + expect(p.osPrettyName).toBe("Fedora Linux 40"); + }); + it("picks apt when present", () => { + expect(parsePlatformProbe("apt=1\ndnf=1").pkg).toBe("apt"); + }); +}); + +describe("validation (injection defense)", () => { + it("systemd units", () => { + expect(isValidSystemdUnit("nginx.service")).toBe(true); + expect(isValidSystemdUnit("ssh.socket")).toBe(true); + expect(isValidSystemdUnit("nginx.service; rm -rf /")).toBe(false); + expect(isValidSystemdUnit("nginx")).toBe(false); + }); + it("pids", () => { + expect(isValidPid(123)).toBe(true); + expect(isValidPid("123")).toBe(true); + expect(isValidPid(0)).toBe(false); + expect(isValidPid("1; reboot")).toBe(false); + expect(isValidPid(-5)).toBe(false); + }); + it("ports", () => { + expect(isValidPort(443)).toBe(true); + expect(isValidPort(0)).toBe(false); + expect(isValidPort(70000)).toBe(false); + }); + it("package names", () => { + expect(isValidPackageName("nginx")).toBe(true); + expect(isValidPackageName("lib-foo.bar+1")).toBe(true); + expect(isValidPackageName("nginx && curl evil")).toBe(false); + expect(isValidPackageName("-rf")).toBe(false); + }); + it("usernames and domains", () => { + expect(isValidUsername("deploy")).toBe(true); + expect(isValidUsername("root; rm")).toBe(false); + expect(isValidDomain("example.com")).toBe(true); + expect(isValidDomain("*.example.com")).toBe(true); + expect(isValidDomain("ex ample.com")).toBe(false); + expect(isValidDomain("a;b.com")).toBe(false); + }); + it("dns providers, signals, service actions", () => { + expect(isValidDnsProvider("cloudflare")).toBe(true); + expect(isValidDnsProvider("cf; rm")).toBe(false); + expect(isValidSignal("KILL")).toBe(true); + expect(isValidSignal("BOOM")).toBe(false); + expect(isValidServiceAction("restart")).toBe(true); + expect(isValidServiceAction("destroy")).toBe(false); + }); + it("path allowlist rejects traversal and out-of-allowlist", () => { + expect(isAllowedPath("/var/log/syslog", ["/var/log"])).toBe(true); + expect(isAllowedPath("/var/log/../../etc/passwd", ["/var/log"])).toBe( + false, + ); + expect(isAllowedPath("/etc/passwd", ["/var/log"])).toBe(false); + expect(isAllowedPath("relative/path", ["/var/log"])).toBe(false); + }); +}); + +describe("services", () => { + it("parses list-units --plain", () => { + const out = + "nginx.service loaded active running A high performance web server\n" + + "ssh.service loaded active running OpenBSD Secure Shell server\n" + + "cron.service loaded inactive dead Regular background program"; + const rows = parseServiceList(out); + expect(rows).toHaveLength(3); + expect(rows[0]).toMatchObject({ unit: "nginx.service", active: "active" }); + expect(rows[2].active).toBe("inactive"); + }); + it("builds action command", () => { + expect(buildServiceActionCommand("nginx.service", "restart")).toBe( + "systemctl restart nginx.service", + ); + }); +}); + +describe("processes", () => { + it("parses ps output", () => { + const out = + "1234 1 root 12.5 3.2 45000 S /usr/bin/node node server.js\n" + + "5678 1234 deploy 0.0 1.1 12000 Sl bash -bash"; + const rows = parseProcessList(out); + expect(rows[0]).toMatchObject({ + pid: 1234, + user: "root", + cpu: 12.5, + command: "/usr/bin/node", + }); + expect(rows[1].pid).toBe(5678); + }); + it("builds kill command", () => { + expect(buildKillCommand(42, "TERM")).toBe("kill -TERM 42"); + }); +}); + +describe("simple reads", () => { + it("parses df -Pk and skips tmpfs", () => { + const out = + "/dev/sda1 100000 40000 60000 40% /\n" + + "tmpfs 8000 0 8000 0% /dev/shm\n" + + "/dev/sdb1 200000 100000 100000 50% /data"; + const mounts = parseDfMounts(out); + expect(mounts).toHaveLength(2); + expect(mounts[0].mount).toBe("/"); + expect(mounts[1].usePct).toBe(50); + }); + it("parses top by memory", () => { + const rows = parseTopMemory("1234 root 5.5 50000 node"); + expect(rows[0]).toMatchObject({ pid: 1234, mem: 5.5, command: "node" }); + }); +}); + +describe("cron", () => { + it("parses enabled and toggled entries", () => { + const out = "0 2 * * * /backup.sh\n# 30 4 * * * /old.sh\nPATH=/usr/bin"; + const entries = parseCrontab(out); + expect(entries).toHaveLength(2); + expect(entries[0]).toMatchObject({ enabled: true, schedule: "0 2 * * *" }); + expect(entries[1].enabled).toBe(false); + }); + it("validates schedules", () => { + expect(isValidCronSchedule("0 2 * * *")).toBe(true); + expect(isValidCronSchedule("@daily")).toBe(true); + expect(isValidCronSchedule("not a schedule")).toBe(false); + }); + it("serializes (commenting disabled entries) and round-trips", () => { + const body = serializeCrontab([ + { raw: "", enabled: true, schedule: "0 2 * * *", command: "/a.sh" }, + { raw: "", enabled: false, schedule: "@daily", command: "/b.sh" }, + ]); + expect(body).toBe("0 2 * * * /a.sh\n# @daily /b.sh\n"); + const reparsed = parseCrontab(body); + expect(reparsed[0].enabled).toBe(true); + expect(reparsed[1].enabled).toBe(false); + }); + it("builds apply command piping into crontab -", () => { + expect(buildApplyCrontabCommand("x\n")).toBe( + `printf '%s' 'x\n' | crontab -`, + ); + }); +}); + +describe("packages", () => { + it("builds per-distro commands", () => { + expect(buildPackageActionCommand("apt", "install", "nginx")).toContain( + "apt-get -y install nginx", + ); + expect(buildPackageActionCommand("pacman", "upgrade-all")).toBe( + "pacman -Syu --noconfirm", + ); + expect(buildPackageActionCommand(null, "install", "x")).toBeNull(); + }); + it("lists per distro", () => { + expect(buildListUpgradableCommand("apt")).toContain( + "apt list --upgradable", + ); + expect(buildListUpgradableCommand(null)).toBeNull(); + }); + it("parses apt upgradable", () => { + const out = + "nginx/focal-updates 1.18.0-2 amd64 [upgradable from: 1.18.0-1]"; + const pkgs = parseUpgradable("apt", out); + expect(pkgs[0]).toMatchObject({ + name: "nginx", + newVersion: "1.18.0-2", + currentVersion: "1.18.0-1", + }); + }); + it("parses pacman upgradable", () => { + const pkgs = parseUpgradable("pacman", "linux 6.1 -> 6.2"); + expect(pkgs[0]).toMatchObject({ name: "linux", newVersion: "6.2" }); + }); +}); + +describe("ssl (dual client)", () => { + it("builds certbot issue for http + dns", () => { + expect( + buildIssueCommand({ + client: "certbot", + domains: ["a.com"], + challenge: "http-standalone", + }), + ).toContain( + "certbot certonly --non-interactive --agree-tos --standalone -d 'a.com'", + ); + expect( + buildIssueCommand({ + client: "certbot", + domains: ["a.com"], + challenge: "dns", + dnsProvider: "cloudflare", + }), + ).toContain("--dns-cloudflare"); + }); + it("builds acme.sh issue", () => { + const cmd = buildIssueCommand({ + client: "acme.sh", + domains: ["a.com", "b.com"], + challenge: "dns", + dnsProvider: "cf", + }); + expect(cmd).toContain("--issue --dns dns_cf"); + expect(cmd).toContain("-d 'a.com'"); + expect(cmd).toContain("-d 'b.com'"); + }); + it("builds renew per client", () => { + expect(buildRenewCommand("certbot", true)).toBe("certbot renew --dry-run"); + expect(buildRenewCommand("acme.sh", false)).toContain("--renew-all"); + }); + it("builds revoke per client", () => { + expect(buildRevokeCommand("certbot", "example.com")).toBe( + "certbot revoke --non-interactive --cert-name 'example.com' --delete-after-revoke", + ); + const acme = buildRevokeCommand("acme.sh", "example.com"); + expect(acme).toContain("--revoke -d 'example.com'"); + expect(acme).toContain("--remove -d 'example.com'"); + }); + it("validates certificate names (rejects shell metachars)", () => { + expect(isValidCertName("example.com")).toBe(true); + expect(isValidCertName("example.com-0001")).toBe(true); + expect(isValidCertName("*.example.com")).toBe(true); + expect(isValidCertName("a.com; rm -rf /")).toBe(false); + expect(isValidCertName("")).toBe(false); + expect(isValidCertName(undefined)).toBe(false); + }); + it("parses certbot certificates", () => { + const out = [ + "Found the following certs:", + " Certificate Name: example.com", + " Domains: example.com www.example.com", + " Expiry Date: 2026-09-01 12:00:00+00:00 (VALID: 80 days)", + " Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem", + ].join("\n"); + const certs = parseCertbotCertificates(out); + expect(certs[0]).toMatchObject({ + name: "example.com", + client: "certbot", + }); + expect(certs[0].domains).toContain("www.example.com"); + }); +}); + +describe("firewall", () => { + it("builds iptables add/delete on INPUT only", () => { + expect( + buildIptablesRuleCommand("add", { + protocol: "tcp", + port: 443, + target: "ACCEPT", + }), + ).toBe("iptables -A INPUT -p tcp --dport 443 -j ACCEPT"); + expect( + buildIptablesRuleCommand("delete", { + protocol: "udp", + port: 53, + target: "DROP", + }), + ).toBe("iptables -D INPUT -p udp --dport 53 -j DROP"); + }); + it("builds nft rules", () => { + expect( + buildNftRuleCommand("add", { + protocol: "tcp", + port: 22, + target: "ACCEPT", + }), + ).toContain("add rule inet filter input tcp dport 22 accept"); + }); +}); + +describe("users", () => { + it("parses passwd for human users only", () => { + const out = + "root:x:0:0:root:/root:/bin/bash\n" + + "deploy:x:1000:1000:Deploy:/home/deploy:/bin/bash\n" + + "nobody:x:65534:65534:nobody:/:/usr/sbin/nologin"; + const users = parsePasswd(out); + expect(users).toHaveLength(1); + expect(users[0].name).toBe("deploy"); + }); + it("parses sudoers membership", () => { + const out = "sudo:x:27:deploy,alice\nwheel:x:10:bob"; + expect(parseSudoers(out).sort()).toEqual(["alice", "bob", "deploy"]); + }); +}); + +describe("health checks", () => { + it("builds tcp and http commands", () => { + const tcp = buildHealthCheckCommand({ + id: "1", + name: "ssh", + type: "tcp", + target: "localhost", + port: 22, + }); + expect(tcp).toContain("/dev/tcp/"); + const http = buildHealthCheckCommand({ + id: "2", + name: "web", + type: "http", + target: "example.com", + path: "/health", + }); + expect(http).toContain("curl -s -o /dev/null"); + expect(http).toContain("http://example.com/health"); + }); + it("parses tcp and http results", () => { + const tcp = parseHealthResult( + { id: "1", name: "ssh", type: "tcp", target: "h", port: 22 }, + "ok 12", + ); + expect(tcp).toMatchObject({ ok: true, latencyMs: 12 }); + const http = parseHealthResult( + { id: "2", name: "web", type: "http", target: "h" }, + "200 0.045", + ); + expect(http).toMatchObject({ ok: true, latencyMs: 45 }); + const bad = parseHealthResult( + { id: "3", name: "web", type: "http", target: "h" }, + "500 0.01", + ); + expect(bad.ok).toBe(false); + }); +}); + +describe("logs", () => { + it("clamps line counts", () => { + expect(clampLines(50)).toBe(50); + expect(clampLines(99999)).toBe(2000); + expect(clampLines("abc")).toBe(200); + expect(clampLines(0)).toBe(1); + }); + it("builds a quoted tail command without suppressing stderr", () => { + expect(buildTailCommand("/var/log/syslog", 100)).toBe( + "tail -n 100 '/var/log/syslog'", + ); + }); +}); diff --git a/src/backend/ssh/managers/packages.ts b/src/backend/ssh/managers/packages.ts new file mode 100644 index 00000000..6c5c96d5 --- /dev/null +++ b/src/backend/ssh/managers/packages.ts @@ -0,0 +1,148 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { isValidPackageName } from "./validation.js"; +import { detectPlatform, type PackageManager } from "./platform.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface UpgradablePackage { + name: string; + currentVersion?: string; + newVersion?: string; +} + +export function buildListUpgradableCommand(pkg: PackageManager): string | null { + switch (pkg) { + case "apt": + return "apt list --upgradable 2>/dev/null | tail -n +2"; + case "dnf": + return "dnf -q check-update 2>/dev/null || true"; + case "yum": + return "yum -q check-update 2>/dev/null || true"; + case "pacman": + return "pacman -Qu 2>/dev/null || true"; + default: + return null; + } +} + +export function parseUpgradable( + pkg: PackageManager, + output: string, +): UpgradablePackage[] { + const out: UpgradablePackage[] = []; + const lines = output + .split("\n") + .map((l) => l.trim()) + .filter(Boolean); + if (pkg === "apt") { + for (const line of lines) { + // name/suite newver arch [upgradable from: oldver] + const m = line.match( + /^([^/\s]+)\/\S+\s+(\S+)\s+\S+(?:\s+\[upgradable from:\s+(\S+)\])?/, + ); + if (m) out.push({ name: m[1], newVersion: m[2], currentVersion: m[3] }); + } + } else if (pkg === "dnf" || pkg === "yum") { + for (const line of lines) { + if (/^(Last metadata|Obsoleting|Security:)/i.test(line)) continue; + const m = line.match(/^(\S+)\s+(\S+)\s+\S+$/); + if (m && m[1].includes(".")) out.push({ name: m[1], newVersion: m[2] }); + } + } else if (pkg === "pacman") { + for (const line of lines) { + const m = line.match(/^(\S+)\s+(\S+)\s+->\s+(\S+)$/); + if (m) out.push({ name: m[1], currentVersion: m[2], newVersion: m[3] }); + } + } + return out; +} + +export type PackageAction = "upgrade-all" | "install" | "upgrade"; + +export function buildPackageActionCommand( + pkg: PackageManager, + action: PackageAction, + name?: string, +): string | null { + const target = name ? ` ${name}` : ""; + switch (pkg) { + case "apt": + if (action === "upgrade-all") + return "DEBIAN_FRONTEND=noninteractive apt-get -y upgrade"; + return `DEBIAN_FRONTEND=noninteractive apt-get -y install${target}`; + case "dnf": + return action === "upgrade-all" + ? "dnf -y upgrade" + : `dnf -y install${target}`; + case "yum": + return action === "upgrade-all" + ? "yum -y update" + : `yum -y install${target}`; + case "pacman": + return action === "upgrade-all" + ? "pacman -Syu --noconfirm" + : `pacman -S --noconfirm${target}`; + default: + return null; + } +} + +export function registerPackageRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/packages/:id", + validateHostId, + managerHandler(runOnHost, "read", "packages_list", async (client) => { + const platform = await detectPlatform(client); + const cmd = buildListUpgradableCommand(platform.pkg); + if (!cmd) return { pkg: platform.pkg, upgradable: [] }; + const { stdout } = await execCommand(client, cmd, 60000); + return { + pkg: platform.pkg, + upgradable: parseUpgradable(platform.pkg, stdout), + }; + }), + ); + + app.post( + "/host-metrics/managers/packages/:id/action", + validateHostId, + managerHandler( + runOnHost, + "execute", + "packages_action", + async (client, host, req) => { + const { action, pkg: name } = req.body as { + action?: PackageAction; + pkg?: string; + }; + if ( + action !== "upgrade-all" && + action !== "install" && + action !== "upgrade" + ) { + throw new ManagerInputError("Invalid action"); + } + if (action !== "upgrade-all" && !isValidPackageName(name)) { + throw new ManagerInputError("Invalid package name"); + } + const platform = await detectPlatform(client); + const cmd = buildPackageActionCommand(platform.pkg, action, name); + if (!cmd) throw new ManagerInputError("No supported package manager"); + // Package operations can be slow; allow up to 10 minutes. + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + timeoutMs: 600000, + }); + return { + success: result.code === 0, + output: (result.stdout || result.stderr).slice(-8000), + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/platform.ts b/src/backend/ssh/managers/platform.ts new file mode 100644 index 00000000..e19716f8 --- /dev/null +++ b/src/backend/ssh/managers/platform.ts @@ -0,0 +1,61 @@ +import type { Client } from "ssh2"; +import { execCommand } from "../widgets/common-utils.js"; + +export type PackageManager = "apt" | "dnf" | "yum" | "pacman" | null; + +export interface PlatformInfo { + hasSystemd: boolean; + pkg: PackageManager; + hasCertbot: boolean; + hasAcmeSh: boolean; + hasDocker: boolean; + osPrettyName: string | null; +} + +/** + * Single probe that reports which tooling is available. Each line is + * "key=value" so the parser is trivial and order-independent. + */ +export const PLATFORM_PROBE_COMMAND = [ + "echo systemd=$(command -v systemctl >/dev/null 2>&1 && echo 1 || echo 0)", + "echo apt=$(command -v apt-get >/dev/null 2>&1 && echo 1 || echo 0)", + "echo dnf=$(command -v dnf >/dev/null 2>&1 && echo 1 || echo 0)", + "echo yum=$(command -v yum >/dev/null 2>&1 && echo 1 || echo 0)", + "echo pacman=$(command -v pacman >/dev/null 2>&1 && echo 1 || echo 0)", + "echo certbot=$(command -v certbot >/dev/null 2>&1 && echo 1 || echo 0)", + 'echo acmesh=$( { command -v acme.sh >/dev/null 2>&1 || [ -x "$HOME/.acme.sh/acme.sh" ]; } && echo 1 || echo 0)', + "echo docker=$(command -v docker >/dev/null 2>&1 && echo 1 || echo 0)", + 'echo os=$(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME")', +].join("; "); + +export function parsePlatformProbe(output: string): PlatformInfo { + const map = new Map(); + for (const line of output.split("\n")) { + const idx = line.indexOf("="); + if (idx === -1) continue; + map.set(line.slice(0, idx).trim(), line.slice(idx + 1).trim()); + } + const on = (k: string) => map.get(k) === "1"; + + // Prefer dnf over yum when both exist (dnf is the modern front-end). + let pkg: PackageManager = null; + if (on("apt")) pkg = "apt"; + else if (on("dnf")) pkg = "dnf"; + else if (on("yum")) pkg = "yum"; + else if (on("pacman")) pkg = "pacman"; + + const os = map.get("os"); + return { + hasSystemd: on("systemd"), + pkg, + hasCertbot: on("certbot"), + hasAcmeSh: on("acmesh"), + hasDocker: on("docker"), + osPrettyName: os && os.length > 0 ? os : null, + }; +} + +export async function detectPlatform(client: Client): Promise { + const { stdout } = await execCommand(client, PLATFORM_PROBE_COMMAND, 15000); + return parsePlatformProbe(stdout); +} diff --git a/src/backend/ssh/managers/processes.ts b/src/backend/ssh/managers/processes.ts new file mode 100644 index 00000000..180dc69e --- /dev/null +++ b/src/backend/ssh/managers/processes.ts @@ -0,0 +1,130 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { isValidPid, isValidSignal, type Signal } from "./validation.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface ProcessRow { + pid: number; + ppid: number; + user: string; + cpu: number; + mem: number; + rss: number; + stat: string; + command: string; + args: string; +} + +const LIST_PROCESSES_CMD = + "ps -eo pid,ppid,user:20,pcpu,pmem,rss,stat,comm,args --sort=-pcpu --no-headers 2>/dev/null | head -n 300"; + +/** Parse `ps -eo pid,ppid,user,pcpu,pmem,rss,stat,comm,args` output. */ +export function parseProcessList(output: string): ProcessRow[] { + const rows: ProcessRow[] = []; + for (const raw of output.split("\n")) { + const line = raw.trim(); + if (!line) continue; + const m = line.match( + /^(\d+)\s+(\d+)\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/, + ); + if (!m) continue; + rows.push({ + pid: Number(m[1]), + ppid: Number(m[2]), + user: m[3], + cpu: Number(m[4]), + mem: Number(m[5]), + rss: Number(m[6]), + stat: m[7], + command: m[8], + args: m[9], + }); + } + return rows; +} + +export function buildKillCommand(pid: number, signal: Signal): string { + return `kill -${signal} ${pid}`; +} + +export function registerProcessRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + /** + * @openapi + * /host-metrics/managers/processes/{id}: + * get: + * summary: List processes (rich, sortable, filterable client-side) + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: Process list. } + */ + app.get( + "/host-metrics/managers/processes/:id", + validateHostId, + managerHandler(runOnHost, "read", "processes_list", async (client) => { + const { stdout } = await execCommand(client, LIST_PROCESSES_CMD, 20000); + return { processes: parseProcessList(stdout) }; + }), + ); + + /** + * @openapi + * /host-metrics/managers/processes/{id}/signal: + * post: + * summary: Send a signal to a process (TERM/KILL/HUP/INT) + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * pid: { type: integer } + * signal: { type: string } + * responses: + * 200: { description: Signal result. } + * 400: { description: Invalid pid or signal. } + * 403: { description: Elevation required or denied. } + */ + app.post( + "/host-metrics/managers/processes/:id/signal", + validateHostId, + managerHandler( + runOnHost, + "execute", + "processes_signal", + async (client, host, req) => { + const { pid, signal } = req.body as { + pid?: number; + signal?: string; + }; + if (!isValidPid(pid)) throw new ManagerInputError("Invalid pid"); + if (!isValidSignal(signal)) + throw new ManagerInputError("Invalid signal"); + const cmd = buildKillCommand(Number(pid), signal); + // Try unprivileged first; elevate only if the process isn't owned. + const result = await execElevated(client, cmd, host.sudoPassword); + return { + success: result.code === 0, + output: result.stdout || result.stderr, + usedSudo: result.usedSudo, + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/route-helpers.ts b/src/backend/ssh/managers/route-helpers.ts new file mode 100644 index 00000000..5dfb6301 --- /dev/null +++ b/src/backend/ssh/managers/route-helpers.ts @@ -0,0 +1,60 @@ +import type { Request, Response } from "express"; +import type { Client } from "ssh2"; +import type { AuthenticatedRequest } from "../../../types/index.js"; +import { statsLogger } from "../../utils/logger.js"; +import { ElevationError } from "./exec-elevated.js"; +import type { ManagerHost, RunOnHost } from "./types.js"; + +export class AccessDeniedError extends Error { + constructor(message = "No access to this host") { + super(message); + this.name = "AccessDeniedError"; + } +} + +export class ManagerInputError extends Error { + constructor(message: string) { + super(message); + this.name = "ManagerInputError"; + } +} + +/** + * Wrap a manager handler: parses hostId, runs `fn` on a pooled connection at the + * given access level, and maps known errors to clean HTTP responses. + */ +export function managerHandler( + runOnHost: RunOnHost, + level: "read" | "execute", + operation: string, + fn: (client: Client, host: ManagerHost, req: Request) => Promise, +) { + return async (req: Request, res: Response) => { + const userId = (req as AuthenticatedRequest).userId; + const hostId = parseInt(String(req.params.id), 10); + try { + const result = await runOnHost(hostId, userId, level, (client, host) => + fn(client, host, req), + ); + return res.json(result); + } catch (error) { + if (error instanceof ManagerInputError) { + return res.status(400).json({ error: error.message }); + } + if (error instanceof AccessDeniedError) { + return res.status(403).json({ error: error.message }); + } + if (error instanceof ElevationError) { + return res.status(403).json({ error: error.message, code: error.code }); + } + statsLogger.error(`Manager operation failed: ${operation}`, { + operation, + hostId, + error: error instanceof Error ? error.message : String(error), + }); + return res.status(500).json({ + error: error instanceof Error ? error.message : "Operation failed", + }); + } + }; +} diff --git a/src/backend/ssh/managers/services.ts b/src/backend/ssh/managers/services.ts new file mode 100644 index 00000000..63164833 --- /dev/null +++ b/src/backend/ssh/managers/services.ts @@ -0,0 +1,135 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { + isValidSystemdUnit, + isValidServiceAction, + type ServiceAction, +} from "./validation.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface SystemdService { + unit: string; + load: string; + active: string; + sub: string; + description: string; +} + +const LIST_SERVICES_CMD = + "systemctl list-units --type=service --all --no-legend --no-pager --plain 2>/dev/null"; + +/** Parse `systemctl list-units --plain` output into structured rows. */ +export function parseServiceList(output: string): SystemdService[] { + const services: SystemdService[] = []; + for (const raw of output.split("\n")) { + const line = raw.trim(); + if (!line) continue; + // Columns: UNIT LOAD ACTIVE SUB DESCRIPTION (description may contain spaces) + const parts = line.split(/\s+/); + if (parts.length < 4) continue; + const [unit, load, active, sub, ...rest] = parts; + if (!unit.endsWith(".service")) continue; + services.push({ + unit, + load, + active, + sub, + description: rest.join(" "), + }); + } + return services; +} + +export function buildServiceActionCommand( + unit: string, + action: ServiceAction, +): string { + return `systemctl ${action} ${unit}`; +} + +export function registerServiceRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + /** + * @openapi + * /host-metrics/managers/services/{id}: + * get: + * summary: List systemd services + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * responses: + * 200: { description: List of services. } + */ + app.get( + "/host-metrics/managers/services/:id", + validateHostId, + managerHandler(runOnHost, "read", "services_list", async (client) => { + const { stdout } = await execCommand(client, LIST_SERVICES_CMD, 20000); + return { services: parseServiceList(stdout) }; + }), + ); + + /** + * @openapi + * /host-metrics/managers/services/{id}/action: + * post: + * summary: Start/stop/restart/enable/disable a systemd service + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * unit: { type: string } + * action: { type: string } + * responses: + * 200: { description: Action result. } + * 400: { description: Invalid unit or action. } + * 403: { description: Elevation required or denied. } + */ + app.post( + "/host-metrics/managers/services/:id/action", + validateHostId, + managerHandler( + runOnHost, + "execute", + "services_action", + async (client, host, req) => { + const { unit, action } = req.body as { + unit?: string; + action?: string; + }; + if (!isValidSystemdUnit(unit)) { + throw new ManagerInputError("Invalid unit name"); + } + if (!isValidServiceAction(action)) { + throw new ManagerInputError("Invalid action"); + } + const result = await execElevated( + client, + buildServiceActionCommand(unit, action), + host.sudoPassword, + { forceSudo: true, timeoutMs: 30000 }, + ); + return { + success: result.code === 0, + output: result.stdout || result.stderr, + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/simple-reads.ts b/src/backend/ssh/managers/simple-reads.ts new file mode 100644 index 00000000..189f36e3 --- /dev/null +++ b/src/backend/ssh/managers/simple-reads.ts @@ -0,0 +1,138 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { managerHandler } from "./route-helpers.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +// ─── Top by memory ────────────────────────────────────────────────────────── + +export interface MemProcessRow { + pid: number; + user: string; + mem: number; + rss: number; + command: string; +} + +const TOP_MEM_CMD = + "ps -eo pid,user:20,pmem,rss,comm --sort=-pmem --no-headers 2>/dev/null | head -n 20"; + +export function parseTopMemory(output: string): MemProcessRow[] { + const rows: MemProcessRow[] = []; + for (const raw of output.split("\n")) { + const line = raw.trim(); + if (!line) continue; + const m = line.match(/^(\d+)\s+(\S+)\s+([\d.]+)\s+(\d+)\s+(.*)$/); + if (!m) continue; + rows.push({ + pid: Number(m[1]), + user: m[2], + mem: Number(m[3]), + rss: Number(m[4]), + command: m[5], + }); + } + return rows; +} + +// ─── Systemd timers ───────────────────────────────────────────────────────── + +export interface TimerRow { + next: string; + left: string; + last: string; + unit: string; + activates: string; +} + +const TIMERS_CMD = + "systemctl list-timers --all --no-legend --no-pager 2>/dev/null"; + +export function parseTimers(output: string): TimerRow[] { + const rows: TimerRow[] = []; + for (const raw of output.split("\n")) { + const line = raw.trim(); + if (!line || line.startsWith("NEXT")) continue; + // NEXT(3) LEFT(2) LAST(3) PASSED(2) UNIT ACTIVATES -> columns vary; grab + // the trailing UNIT + ACTIVATES which always end the line. + const parts = line.split(/\s+/); + if (parts.length < 2) continue; + const activates = parts[parts.length - 1]; + const unit = parts[parts.length - 2]; + if (!unit.endsWith(".timer")) continue; + rows.push({ + next: parts.slice(0, 3).join(" "), + left: "", + last: "", + unit, + activates, + }); + } + return rows; +} + +// ─── Disk breakdown (per-mount) ───────────────────────────────────────────── + +export interface MountUsage { + filesystem: string; + sizeKb: number; + usedKb: number; + availKb: number; + usePct: number; + mount: string; +} + +const DF_CMD = "df -Pk 2>/dev/null | tail -n +2"; + +export function parseDfMounts(output: string): MountUsage[] { + const mounts: MountUsage[] = []; + for (const raw of output.split("\n")) { + const line = raw.trim(); + if (!line) continue; + const m = line.match(/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%\s+(.+)$/); + if (!m) continue; + const fs = m[1]; + // Skip pseudo/virtual filesystems that clutter the view. + if (/^(tmpfs|devtmpfs|overlay|udev|none|shm)$/.test(fs)) continue; + mounts.push({ + filesystem: fs, + sizeKb: Number(m[2]), + usedKb: Number(m[3]), + availKb: Number(m[4]), + usePct: Number(m[5]), + mount: m[6], + }); + } + return mounts; +} + +export function registerSimpleReadRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/top-memory/:id", + validateHostId, + managerHandler(runOnHost, "read", "top_memory", async (client) => { + const { stdout } = await execCommand(client, TOP_MEM_CMD, 15000); + return { processes: parseTopMemory(stdout) }; + }), + ); + + app.get( + "/host-metrics/managers/timers/:id", + validateHostId, + managerHandler(runOnHost, "read", "systemd_timers", async (client) => { + const { stdout } = await execCommand(client, TIMERS_CMD, 15000); + return { timers: parseTimers(stdout) }; + }), + ); + + app.get( + "/host-metrics/managers/disk-breakdown/:id", + validateHostId, + managerHandler(runOnHost, "read", "disk_breakdown", async (client) => { + const { stdout } = await execCommand(client, DF_CMD, 15000); + return { mounts: parseDfMounts(stdout) }; + }), + ); +} diff --git a/src/backend/ssh/managers/ssl.ts b/src/backend/ssh/managers/ssl.ts new file mode 100644 index 00000000..9a17c953 --- /dev/null +++ b/src/backend/ssh/managers/ssl.ts @@ -0,0 +1,317 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated, shellSingleQuote } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { + isValidDomain, + isValidDnsProvider, + isAllowedPath, +} from "./validation.js"; +import { detectPlatform } from "./platform.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export type AcmeClient = "certbot" | "acme.sh"; +export type ChallengeType = "http-standalone" | "http-webroot" | "dns"; + +export interface CertInfo { + client: AcmeClient | "other"; + name: string; + domains: string[]; + expiry: string | null; + path?: string; +} + +const CERTBOT_LIST_CMD = "certbot certificates 2>/dev/null"; +const ACMESH_BIN = '"$(command -v acme.sh || echo "$HOME/.acme.sh/acme.sh")"'; +const ACMESH_LIST_CMD = `${ACMESH_BIN} --list 2>/dev/null`; + +/** Parse `certbot certificates` output. */ +export function parseCertbotCertificates(output: string): CertInfo[] { + const certs: CertInfo[] = []; + let current: CertInfo | null = null; + for (const raw of output.split("\n")) { + const line = raw.trim(); + const nameM = line.match(/^Certificate Name:\s+(.+)$/); + if (nameM) { + if (current) certs.push(current); + current = { + client: "certbot", + name: nameM[1], + domains: [], + expiry: null, + }; + continue; + } + if (!current) continue; + const domM = line.match(/^Domains:\s+(.+)$/); + if (domM) current.domains = domM[1].split(/\s+/).filter(Boolean); + const expM = line.match(/^Expiry Date:\s+(\S+\s+\S+)/); + if (expM) current.expiry = expM[1]; + const pathM = line.match(/^Certificate Path:\s+(.+)$/); + if (pathM) current.path = pathM[1]; + } + if (current) certs.push(current); + return certs; +} + +/** Parse `acme.sh --list` (tab/space separated columns with a header). */ +export function parseAcmeShList(output: string): CertInfo[] { + const certs: CertInfo[] = []; + const lines = output.split("\n").filter((l) => l.trim()); + if (lines.length < 2) return certs; + for (const line of lines.slice(1)) { + const cols = line + .split(/\s{2,}|\t/) + .map((c) => c.trim()) + .filter(Boolean); + if (cols.length < 1) continue; + certs.push({ + client: "acme.sh", + name: cols[0], + domains: [cols[0]], + expiry: cols[cols.length - 1] || null, + }); + } + return certs; +} + +/** + * A certbot cert name or acme.sh primary domain. Allows letters, digits, dots, + * hyphens, underscores and the wildcard `*` (acme.sh), but no shell metachars. + */ +const CERT_NAME_RE = /^[A-Za-z0-9._*-]+$/; +export function isValidCertName(name: unknown): name is string { + return typeof name === "string" && name.length > 0 && CERT_NAME_RE.test(name); +} + +export interface IssueRequest { + client: AcmeClient; + domains: string[]; + challenge: ChallengeType; + webroot?: string; + dnsProvider?: string; +} + +/** + * Build the certificate issuance command for the chosen client/challenge. + * DNS-01 provider credentials are expected to already be present in the + * environment / provider config file; we never put secrets in argv. + */ +export function buildIssueCommand(req: IssueRequest): string { + const domains = req.domains; + if (req.client === "certbot") { + const dFlags = domains.map((d) => `-d ${shellSingleQuote(d)}`).join(" "); + if (req.challenge === "dns") { + return `certbot certonly --non-interactive --agree-tos --dns-${req.dnsProvider} ${dFlags}`; + } + if (req.challenge === "http-webroot") { + return `certbot certonly --non-interactive --agree-tos --webroot -w ${shellSingleQuote( + req.webroot as string, + )} ${dFlags}`; + } + return `certbot certonly --non-interactive --agree-tos --standalone ${dFlags}`; + } + // acme.sh + const dFlags = domains.map((d) => `-d ${shellSingleQuote(d)}`).join(" "); + if (req.challenge === "dns") { + return `${ACMESH_BIN} --issue --dns dns_${req.dnsProvider} ${dFlags}`; + } + if (req.challenge === "http-webroot") { + return `${ACMESH_BIN} --issue -w ${shellSingleQuote( + req.webroot as string, + )} ${dFlags}`; + } + return `${ACMESH_BIN} --issue --standalone ${dFlags}`; +} + +export function buildRenewCommand(client: AcmeClient, dryRun: boolean): string { + if (client === "certbot") { + return `certbot renew${dryRun ? " --dry-run" : ""}`; + } + return `${ACMESH_BIN} --renew-all${dryRun ? " --staging" : ""}`; +} + +/** + * Revoke (and remove) a certificate. certbot revokes by its certificate name and + * deletes the lineage afterwards; acme.sh revokes by primary domain then removes + * it from management. `name` is the certbot cert name or the acme.sh domain. + */ +export function buildRevokeCommand(client: AcmeClient, name: string): string { + if (client === "certbot") { + return `certbot revoke --non-interactive --cert-name ${shellSingleQuote( + name, + )} --delete-after-revoke`; + } + const d = shellSingleQuote(name); + return `${ACMESH_BIN} --revoke -d ${d} && ${ACMESH_BIN} --remove -d ${d}`; +} + +export function registerSslRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/ssl/:id", + validateHostId, + managerHandler(runOnHost, "read", "ssl_list", async (client, host) => { + const platform = await detectPlatform(client); + const certs: CertInfo[] = []; + if (platform.hasCertbot) { + const r = await execElevated( + client, + CERTBOT_LIST_CMD, + host.sudoPassword, + ).catch(() => null); + if (r) certs.push(...parseCertbotCertificates(r.stdout)); + } + if (platform.hasAcmeSh) { + const { stdout } = await execCommand( + client, + ACMESH_LIST_CMD, + 15000, + ).catch(() => ({ stdout: "" }) as { stdout: string }); + certs.push(...parseAcmeShList(stdout)); + } + return { + clients: { + certbot: platform.hasCertbot, + acmeSh: platform.hasAcmeSh, + }, + certs, + }; + }), + ); + + app.post( + "/host-metrics/managers/ssl/:id/issue", + validateHostId, + managerHandler( + runOnHost, + "execute", + "ssl_issue", + async (client, host, req) => { + const body = req.body as Partial; + if (body.client !== "certbot" && body.client !== "acme.sh") { + throw new ManagerInputError("Invalid ACME client"); + } + if (!Array.isArray(body.domains) || body.domains.length === 0) { + throw new ManagerInputError("At least one domain is required"); + } + for (const d of body.domains) { + if (!isValidDomain(d)) + throw new ManagerInputError(`Invalid domain: ${d}`); + } + const challenge = body.challenge; + if ( + challenge !== "http-standalone" && + challenge !== "http-webroot" && + challenge !== "dns" + ) { + throw new ManagerInputError("Invalid challenge type"); + } + if (challenge === "dns" && !isValidDnsProvider(body.dnsProvider)) { + throw new ManagerInputError("Invalid DNS provider"); + } + if ( + challenge === "http-webroot" && + !isAllowedPath(body.webroot, ["/var/www", "/srv", "/usr/share/nginx"]) + ) { + throw new ManagerInputError("Invalid or disallowed webroot path"); + } + const cmd = buildIssueCommand(body as IssueRequest); + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + timeoutMs: 300000, + }); + return { + success: result.code === 0, + output: (result.stdout || result.stderr).slice(-8000), + }; + }, + ), + ); + + app.post( + "/host-metrics/managers/ssl/:id/renew", + validateHostId, + managerHandler( + runOnHost, + "execute", + "ssl_renew", + async (client, host, req) => { + const { client: acmeClient, dryRun } = req.body as { + client?: AcmeClient; + dryRun?: boolean; + }; + if (acmeClient !== "certbot" && acmeClient !== "acme.sh") { + throw new ManagerInputError("Invalid ACME client"); + } + const cmd = buildRenewCommand(acmeClient, !!dryRun); + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + timeoutMs: 300000, + }); + return { + success: result.code === 0, + output: (result.stdout || result.stderr).slice(-8000), + }; + }, + ), + ); + + /** + * @openapi + * /host-metrics/managers/ssl/{id}/revoke: + * post: + * summary: Revoke and remove an issued certificate (certbot or acme.sh) + * tags: [Host Metrics] + * parameters: + * - in: path + * name: id + * required: true + * schema: { type: integer } + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * client: { type: string, enum: [certbot, acme.sh] } + * name: { type: string, description: certbot cert name or acme.sh domain } + * responses: + * 200: { description: Revoke result. } + * 400: { description: Invalid client or certificate name. } + * 403: { description: Elevation required or denied. } + */ + app.post( + "/host-metrics/managers/ssl/:id/revoke", + validateHostId, + managerHandler( + runOnHost, + "execute", + "ssl_revoke", + async (client, host, req) => { + const { client: acmeClient, name } = req.body as { + client?: AcmeClient; + name?: string; + }; + if (acmeClient !== "certbot" && acmeClient !== "acme.sh") { + throw new ManagerInputError("Invalid ACME client"); + } + if (!isValidCertName(name)) { + throw new ManagerInputError("Invalid certificate name"); + } + const cmd = buildRevokeCommand(acmeClient, name); + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + timeoutMs: 120000, + }); + return { + success: result.code === 0, + output: (result.stdout || result.stderr).slice(-8000), + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/types.ts b/src/backend/ssh/managers/types.ts new file mode 100644 index 00000000..05ff6096 --- /dev/null +++ b/src/backend/ssh/managers/types.ts @@ -0,0 +1,27 @@ +import type { Client } from "ssh2"; +import type { RequestHandler } from "express"; + +/** Minimal host shape managers need (includes the decrypted sudo password). */ +export interface ManagerHost { + id: number; + userId: string; + sudoPassword?: string; + enableDocker?: boolean; +} + +/** + * Runs `fn` against a pooled SSH connection for the host, after verifying the + * user has at least `level` access. Resolves the host (with sudoPassword) so + * managers can elevate. Rejects with an access error if not permitted. + */ +export type RunOnHost = ( + hostId: number, + userId: string, + level: "read" | "execute", + fn: (client: Client, host: ManagerHost) => Promise, +) => Promise; + +export interface ManagerRoutesDeps { + validateHostId: RequestHandler; + runOnHost: RunOnHost; +} diff --git a/src/backend/ssh/managers/users.ts b/src/backend/ssh/managers/users.ts new file mode 100644 index 00000000..4df2278c --- /dev/null +++ b/src/backend/ssh/managers/users.ts @@ -0,0 +1,151 @@ +import type { Express } from "express"; +import { execCommand } from "../widgets/common-utils.js"; +import { execElevated } from "./exec-elevated.js"; +import { managerHandler, ManagerInputError } from "./route-helpers.js"; +import { isValidUsername, isValidGroupName } from "./validation.js"; +import type { ManagerRoutesDeps } from "./types.js"; + +export interface SystemUser { + name: string; + uid: number; + gid: number; + home: string; + shell: string; +} + +export interface SystemGroup { + name: string; + gid: number; + members: string[]; +} + +// Human users only (uid >= 1000, excluding nobody at 65534). +const READ_USERS_CMD = "getent passwd 2>/dev/null"; +const READ_GROUPS_CMD = "getent group 2>/dev/null"; +const READ_SUDOERS_CMD = "getent group sudo wheel 2>/dev/null"; + +export function parsePasswd(output: string): SystemUser[] { + const users: SystemUser[] = []; + for (const line of output.split("\n")) { + const parts = line.split(":"); + if (parts.length < 7) continue; + const uid = Number(parts[2]); + if (!Number.isFinite(uid)) continue; + if (uid < 1000 || uid === 65534) continue; + users.push({ + name: parts[0], + uid, + gid: Number(parts[3]), + home: parts[5], + shell: parts[6], + }); + } + return users; +} + +export function parseGroups(output: string): SystemGroup[] { + const groups: SystemGroup[] = []; + for (const line of output.split("\n")) { + const parts = line.split(":"); + if (parts.length < 4) continue; + groups.push({ + name: parts[0], + gid: Number(parts[2]), + members: parts[3].split(",").filter(Boolean), + }); + } + return groups; +} + +export function parseSudoers(output: string): string[] { + const members = new Set(); + for (const line of output.split("\n")) { + const parts = line.split(":"); + if (parts.length < 4) continue; + parts[3] + .split(",") + .filter(Boolean) + .forEach((m) => members.add(m)); + } + return [...members]; +} + +export type UserAction = "create" | "delete" | "addToGroup" | "removeFromGroup"; + +export function registerUserRoutes( + app: Express, + { validateHostId, runOnHost }: ManagerRoutesDeps, +): void { + app.get( + "/host-metrics/managers/users/:id", + validateHostId, + managerHandler(runOnHost, "read", "users_list", async (client) => { + const [passwd, groups, sudoers] = await Promise.all([ + execCommand(client, READ_USERS_CMD, 15000), + execCommand(client, READ_GROUPS_CMD, 15000), + execCommand(client, READ_SUDOERS_CMD, 15000), + ]); + return { + users: parsePasswd(passwd.stdout), + groups: parseGroups(groups.stdout), + sudoers: parseSudoers(sudoers.stdout), + }; + }), + ); + + app.post( + "/host-metrics/managers/users/:id/action", + validateHostId, + managerHandler( + runOnHost, + "execute", + "users_action", + async (client, host, req) => { + const { action, username, group } = req.body as { + action?: UserAction; + username?: string; + group?: string; + }; + if (!isValidUsername(username)) + throw new ManagerInputError("Invalid username"); + + // Never modify/delete the user we're connected as, or root. + const who = (await execCommand(client, "id -un", 8000)).stdout.trim(); + if (username === who || username === "root") { + throw new ManagerInputError( + "Refusing to modify the connected user or root", + ); + } + + let cmd: string; + switch (action) { + case "create": + cmd = `useradd -m ${username}`; + break; + case "delete": + cmd = `userdel -r ${username}`; + break; + case "addToGroup": + case "removeFromGroup": + if (!isValidGroupName(group)) + throw new ManagerInputError("Invalid group"); + cmd = + action === "addToGroup" + ? `usermod -aG ${group} ${username}` + : `gpasswd -d ${username} ${group}`; + break; + default: + throw new ManagerInputError("Invalid action"); + } + + const result = await execElevated(client, cmd, host.sudoPassword, { + forceSudo: true, + }); + return { + success: result.code === 0, + output: result.stdout || result.stderr, + }; + }, + ), + ); +} diff --git a/src/backend/ssh/managers/validation.ts b/src/backend/ssh/managers/validation.ts new file mode 100644 index 00000000..2104d6bd --- /dev/null +++ b/src/backend/ssh/managers/validation.ts @@ -0,0 +1,115 @@ +/** + * Strict allowlist validators for every dynamic value that reaches a shell + * command. Managers MUST validate inputs through these before interpolation; + * never pass raw user text to the shell. + */ + +const SYSTEMD_UNIT_RE = + /^[A-Za-z0-9@._:\\-]+\.(service|socket|timer|target|path|mount)$/; +const PACKAGE_RE = /^[A-Za-z0-9][A-Za-z0-9.+_-]*$/; +const USERNAME_RE = /^[a-z_][a-z0-9_-]*\$?$/; +const GROUP_RE = /^[a-z_][a-z0-9_-]*$/; +const DOMAIN_RE = + /^(\*\.)?(?!-)[A-Za-z0-9-]{1,63}(? 0 && n < 2 ** 31; +} + +export function isValidPort(port: unknown): port is number { + const n = typeof port === "string" ? Number(port) : port; + return typeof n === "number" && Number.isInteger(n) && n >= 1 && n <= 65535; +} + +export function isValidPackageName(pkg: unknown): pkg is string { + return typeof pkg === "string" && pkg.length <= 128 && PACKAGE_RE.test(pkg); +} + +export function isValidUsername(name: unknown): name is string { + return ( + typeof name === "string" && name.length <= 32 && USERNAME_RE.test(name) + ); +} + +export function isValidGroupName(name: unknown): name is string { + return typeof name === "string" && name.length <= 32 && GROUP_RE.test(name); +} + +export function isValidDomain(domain: unknown): domain is string { + return ( + typeof domain === "string" && domain.length <= 253 && DOMAIN_RE.test(domain) + ); +} + +export function isValidDnsProvider(provider: unknown): provider is string { + return ( + typeof provider === "string" && + provider.length <= 64 && + PROVIDER_RE.test(provider) + ); +} + +export type Signal = "TERM" | "KILL" | "HUP" | "INT"; +const SIGNALS: Signal[] = ["TERM", "KILL", "HUP", "INT"]; +export function isValidSignal(sig: unknown): sig is Signal { + return typeof sig === "string" && (SIGNALS as string[]).includes(sig); +} + +export type ServiceAction = + | "start" + | "stop" + | "restart" + | "reload" + | "enable" + | "disable"; +const SERVICE_ACTIONS: ServiceAction[] = [ + "start", + "stop", + "restart", + "reload", + "enable", + "disable", +]; +export function isValidServiceAction(a: unknown): a is ServiceAction { + return typeof a === "string" && (SERVICE_ACTIONS as string[]).includes(a); +} + +export type IpProtocol = "tcp" | "udp"; +export function isValidIpProtocol(p: unknown): p is IpProtocol { + return p === "tcp" || p === "udp"; +} + +export type FirewallTarget = "ACCEPT" | "DROP" | "REJECT"; +const FW_TARGETS: FirewallTarget[] = ["ACCEPT", "DROP", "REJECT"]; +export function isValidFirewallTarget(t: unknown): t is FirewallTarget { + return typeof t === "string" && (FW_TARGETS as string[]).includes(t); +} + +/** + * Validate an absolute file path against an allowlist of permitted prefixes and + * reject traversal. Used by the log viewer (e.g. only under /var/log). + */ +export function isAllowedPath( + path: unknown, + allowedPrefixes: string[], +): path is string { + if (typeof path !== "string" || path.length === 0 || path.length > 4096) { + return false; + } + if (!path.startsWith("/")) return false; + if (path.includes("\0")) return false; + if (path.split("/").some((seg) => seg === "..")) return false; + return allowedPrefixes.some( + (prefix) => + path === prefix || + path.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`), + ); +} diff --git a/src/backend/ssh/opkssh-auth.ts b/src/backend/ssh/opkssh-auth.ts index 03a0c3c4..b2890dc0 100644 --- a/src/backend/ssh/opkssh-auth.ts +++ b/src/backend/ssh/opkssh-auth.ts @@ -69,7 +69,7 @@ async function createTemplateConfig(): Promise { const template = ` # OPKSSH Configuration # OPKSSH Documentation: https://github.com/openpubkey/opkssh/blob/main/docs/config.md -# Termix Documentation: https://docs.termix.site/opkssh +# Termix Documentation: https://docs.termix.site/features/authentication/opkssh `; try { @@ -227,7 +227,7 @@ function validateRedirectUrisAreLocalhost( `you do not put it here. Register the PUBLIC Termix URL with your OAuth provider instead ` + `(e.g. https://your-domain${OPKSSH_CALLBACK_PATH}).\n\n` + `Fix: remove the non-localhost entries above, or delete the whole 'redirect_uris' block to use defaults.\n\n` + - `Docs: https://docs.termix.site/opkssh`, + `Docs: https://docs.termix.site/features/authentication/opkssh`, }; } @@ -410,7 +410,8 @@ export async function startOPKSSHAuth( `Remove any non-localhost entries from redirect_uris (or delete the whole block to use OPKSSH's ` + `defaults of :3000, :10001, :11110). Register the public Termix callback URL with your OAuth ` + `provider instead, Termix passes it to OPKSSH automatically via --remote-redirect-uri.`, - instructions: "See documentation: https://docs.termix.site/opkssh", + instructions: + "See documentation: https://docs.termix.site/features/authentication/opkssh", }), ); await cleanup(); @@ -448,7 +449,8 @@ export async function startOPKSSHAuth( `Register '${remoteRedirectUri}' as an authorized redirect URI with your OAuth provider ` + `(e.g. in Google Cloud Console → OAuth client). ` + `Also confirm any 'redirect_uris' in your OPKSSH config contain ONLY localhost URLs.`, - instructions: "See documentation: https://docs.termix.site/opkssh", + instructions: + "See documentation: https://docs.termix.site/features/authentication/opkssh", }), ); await cleanup(); diff --git a/src/backend/ssh/terminal-session-manager.test.ts b/src/backend/ssh/terminal-session-manager.test.ts new file mode 100644 index 00000000..8b48136a --- /dev/null +++ b/src/backend/ssh/terminal-session-manager.test.ts @@ -0,0 +1,150 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +// Stub all external imports before loading the module under test +const mockInsertValues = vi.fn().mockResolvedValue(undefined); +const mockInsert = vi.fn().mockReturnValue({ values: mockInsertValues }); + +vi.mock("../database/db/index.js", () => ({ + getDb: () => ({ + $client: { + prepare: () => ({ get: () => undefined }), + }, + insert: mockInsert, + }), +})); + +vi.mock("../database/db/schema.js", () => ({ + sessionRecordings: {}, +})); + +vi.mock("../utils/logger.js", () => ({ + sshLogger: { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + }, +})); + +// Mock individual fs.promises methods via a stub object +const mockMkdir = vi.fn().mockResolvedValue(undefined); +const mockWriteFile = vi.fn().mockResolvedValue(undefined); + +vi.mock("fs", () => ({ + default: { + promises: { + mkdir: mockMkdir, + writeFile: mockWriteFile, + readFile: vi.fn(), + unlink: vi.fn(), + }, + }, + promises: { + mkdir: mockMkdir, + writeFile: mockWriteFile, + readFile: vi.fn(), + unlink: vi.fn(), + }, +})); + +const { sessionManager } = await import("./terminal-session-manager.js"); + +describe("TerminalSessionManager - session logging", () => { + beforeEach(() => { + vi.clearAllMocks(); + // Re-apply resolved values after clearAllMocks + mockMkdir.mockResolvedValue(undefined); + mockWriteFile.mockResolvedValue(undefined); + mockInsertValues.mockResolvedValue(undefined); + mockInsert.mockReturnValue({ values: mockInsertValues }); + }); + + it("createSession stores sessionLoggingEnabled=true by default", () => { + const id = sessionManager.createSession("u1", 1, "host", 80, 24); + const session = sessionManager.getSession(id); + expect(session?.sessionLoggingEnabled).toBe(true); + sessionManager.destroySession(id); + }); + + it("createSession stores sessionLoggingEnabled=false when passed", () => { + const id = sessionManager.createSession( + "u1", + 1, + "host", + 80, + 24, + undefined, + false, + ); + const session = sessionManager.getSession(id); + expect(session?.sessionLoggingEnabled).toBe(false); + sessionManager.destroySession(id); + }); + + it("does not write log file when sessionLoggingEnabled=false", async () => { + const id = sessionManager.createSession( + "u1", + 1, + "host", + 80, + 24, + undefined, + false, + ); + sessionManager.bufferOutput(id, "some output"); + sessionManager.destroySession(id); + await new Promise((r) => setTimeout(r, 20)); + expect(mockWriteFile).not.toHaveBeenCalled(); + }); + + it("writes log file and inserts DB row when sessionLoggingEnabled=true", async () => { + const id = sessionManager.createSession( + "u1", + 1, + "host", + 80, + 24, + undefined, + true, + ); + sessionManager.bufferOutput(id, "terminal output data"); + sessionManager.destroySession(id); + await new Promise((r) => setTimeout(r, 20)); + expect(mockWriteFile).toHaveBeenCalledOnce(); + expect(mockInsert).toHaveBeenCalledOnce(); + expect(mockInsertValues).toHaveBeenCalledOnce(); + }); + + it("does not write log file when buffer is empty", async () => { + const id = sessionManager.createSession( + "u1", + 1, + "host", + 80, + 24, + undefined, + true, + ); + sessionManager.destroySession(id); + await new Promise((r) => setTimeout(r, 20)); + expect(mockWriteFile).not.toHaveBeenCalled(); + }); + + it("bufferOutput trims old data when exceeding 512KB", () => { + const id = sessionManager.createSession( + "u1", + 1, + "host", + 80, + 24, + undefined, + false, + ); + const chunk = "x".repeat(300 * 1024); + sessionManager.bufferOutput(id, chunk); + sessionManager.bufferOutput(id, chunk); + const session = sessionManager.getSession(id); + expect(session!.outputBufferBytes).toBeLessThanOrEqual(512 * 1024); + sessionManager.destroySession(id); + }); +}); diff --git a/src/backend/ssh/terminal-session-manager.ts b/src/backend/ssh/terminal-session-manager.ts index 3d06348f..01d19186 100644 --- a/src/backend/ssh/terminal-session-manager.ts +++ b/src/backend/ssh/terminal-session-manager.ts @@ -1,9 +1,14 @@ import { type Client, type ClientChannel } from "ssh2"; import { WebSocket } from "ws"; +import fs from "fs"; +import path from "path"; import { sshLogger } from "../utils/logger.js"; import { getDb } from "../database/db/index.js"; +import { sessionRecordings } from "../database/db/schema.js"; const MAX_BUFFER_BYTES = 512 * 1024; +const DATA_DIR = process.env.DATA_DIR ?? "./db/data"; +const SESSION_LOGS_DIR = path.join(DATA_DIR, "session_logs"); const DEFAULT_TIMEOUT_MINUTES = 30; const HEALTH_CHECK_INTERVAL_MS = 60_000; const MAX_SESSIONS_PER_USER = 10; @@ -32,6 +37,9 @@ export interface TerminalSession { outputBuffer: string[]; outputBufferBytes: number; tmuxSessionName: string | null; + sessionLoggingEnabled: boolean; + sessionStartedAt: number; + lastPersistedBytes: number; } class TerminalSessionManager { @@ -60,6 +68,7 @@ class TerminalSessionManager { cols: number, rows: number, tabInstanceId?: string, + sessionLoggingEnabled = true, ): string { const userSessions = this.getUserSessions(userId); if (userSessions.length >= MAX_SESSIONS_PER_USER) { @@ -107,6 +116,7 @@ class TerminalSessionManager { } const id = crypto.randomUUID(); + const now = Date.now(); const session: TerminalSession = { id, userId, @@ -119,13 +129,16 @@ class TerminalSessionManager { cols, rows, isConnected: false, - createdAt: Date.now(), + createdAt: now, attachedWs: null, lastDetachedAt: null, detachTimeout: null, outputBuffer: [], outputBufferBytes: 0, tmuxSessionName: null, + sessionLoggingEnabled, + sessionStartedAt: now, + lastPersistedBytes: 0, }; this.sessions.set(id, session); @@ -288,6 +301,10 @@ class TerminalSessionManager { session.attachedWs = null; session.lastDetachedAt = Date.now(); + // Persist log immediately when the user detaches so it appears right away, + // regardless of whether the session is later reattached or times out. + this.maybePersistLog(session); + const timeoutMs = this.getTimeoutMs(); session.detachTimeout = setTimeout(() => { @@ -316,6 +333,8 @@ class TerminalSessionManager { session.detachTimeout = null; } + this.maybePersistLog(session, true); + if (session.sshStream) { try { session.sshStream.end(); @@ -357,6 +376,83 @@ class TerminalSessionManager { }); } + private stripAnsi(raw: string): string { + return ( + raw + // ESC sequences: CSI, OSC, DCS, PM, APC, SOS + .replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "") + .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "") + .replace(/\x1b[PX^_][^\x1b]*\x1b\\/g, "") + // Single-char ESC sequences (e.g. ESC M, ESC =) + .replace(/\x1b[^[\]PX^_]/g, "") + // Other C0/C1 control chars except newline, carriage return, tab + .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "") + // Collapse carriage returns used for line overwrites + .replace(/[^\n]*\r(?!\n)/g, "") + ); + } + + private maybePersistLog(session: TerminalSession, force = false): void { + if (!session.sessionLoggingEnabled) return; + if (session.outputBufferBytes === 0) return; + // Only save if new output arrived since last persist (unless forced) + if (!force && session.outputBufferBytes === session.lastPersistedBytes) + return; + const snapshot = session.outputBuffer.join(""); + session.lastPersistedBytes = session.outputBufferBytes; + this.persistSessionLog(session, snapshot).catch((err) => { + sshLogger.warn("Failed to persist session log", { + operation: "session_log_persist_error", + sessionId: session.id, + error: err instanceof Error ? err.message : String(err), + }); + }); + } + + private async persistSessionLog( + session: TerminalSession, + logContent: string, + ): Promise { + const cleaned = this.stripAnsi(logContent); + if (!cleaned.trim()) return; + + const userLogDir = path.join(SESSION_LOGS_DIR, session.userId); + await fs.promises.mkdir(userLogDir, { recursive: true }); + + const logFile = path.join(userLogDir, `${session.id}.log`); + await fs.promises.writeFile(logFile, cleaned, "utf-8"); + + const endedAt = Date.now(); + const duration = Math.floor((endedAt - session.sessionStartedAt) / 1000); + + try { + const db = getDb(); + await db.insert(sessionRecordings).values({ + hostId: session.hostId, + userId: session.userId, + startedAt: new Date(session.sessionStartedAt).toISOString(), + endedAt: new Date(endedAt).toISOString(), + duration, + recordingPath: logFile, + }); + } catch (err) { + sshLogger.warn("Failed to insert session recording row", { + operation: "session_recording_insert_error", + sessionId: session.id, + error: err instanceof Error ? err.message : String(err), + }); + } + + sshLogger.info("Session log persisted", { + operation: "session_log_persisted", + sessionId: session.id, + userId: session.userId, + hostId: session.hostId, + duration, + bytes: cleaned.length, + }); + } + getUserSessions(userId: string): TerminalSession[] { const result: TerminalSession[] = []; for (const session of this.sessions.values()) { diff --git a/src/backend/ssh/terminal.ts b/src/backend/ssh/terminal.ts index b53d7690..9345725b 100644 --- a/src/backend/ssh/terminal.ts +++ b/src/backend/ssh/terminal.ts @@ -11,6 +11,7 @@ import { getDb } from "../database/db/index.js"; import { hosts } from "../database/db/schema.js"; import { eq, and } from "drizzle-orm"; import { sshLogger, authLogger } from "../utils/logger.js"; +import { logAudit } from "../utils/audit-logger.js"; import { SimpleDBOps } from "../utils/simple-db-ops.js"; import { AuthManager } from "../utils/auth-manager.js"; import { UserCrypto } from "../utils/user-crypto.js"; @@ -64,9 +65,13 @@ interface ConnectToHostData { keepaliveCountMax?: number; [key: string]: unknown; }; + enableSessionLogging?: boolean; }; initialPath?: string; executeCommand?: string; + /** Attach straight to this tmux session once the shell is ready + * (tmux monitor opens its panes through a real PTY this way). */ + tmuxAttachSession?: string; } interface ResizeData { @@ -822,7 +827,7 @@ wss.on("connection", async (ws: WebSocket, req) => { }); async function handleConnectToHost(data: ConnectToHostData) { - const { hostConfig, initialPath, executeCommand } = data; + const { hostConfig, initialPath, executeCommand, tmuxAttachSession } = data; const { id, ip: rawIp, @@ -959,6 +964,7 @@ wss.on("connection", async (ws: WebSocket, req) => { socks5Password?: string; socks5ProxyChain?: unknown; terminalConfig?: ConnectToHostData["hostConfig"]["terminalConfig"]; + enableSessionLogging?: boolean; }) | null = null; @@ -1081,6 +1087,16 @@ wss.on("connection", async (ws: WebSocket, req) => { hostId: id, ip, }); + + logAudit({ + userId, + username: userId, + action: "ssh_connect", + resourceType: "host", + resourceId: String(id), + resourceName: `${username}@${ip}:${port}`, + success: true, + }); if (totpPromptSent) { authLogger.success("TOTP verification successful for SSH session", { operation: "terminal_totp_success", @@ -1095,6 +1111,10 @@ wss.on("connection", async (ws: WebSocket, req) => { const hostDisplayName = `${username}@${ip}:${port}`; const tabInstanceId = hostConfig.instanceId; + const sessionLoggingEnabled = + resolvedHostData?.enableSessionLogging ?? + hostConfig.enableSessionLogging ?? + true; currentSessionId = sessionManager.createSession( userId, id, @@ -1102,6 +1122,7 @@ wss.on("connection", async (ws: WebSocket, req) => { data.cols, data.rows, tabInstanceId, + sessionLoggingEnabled, ); // If createSession returned an existing live session (duplicate tabInstanceId), @@ -1465,7 +1486,27 @@ wss.on("connection", async (ws: WebSocket, req) => { }, delay); }; - if (autoTmux && conn) { + if (tmuxAttachSession && conn) { + // Direct attach (tmux monitor): the session is known to exist, so + // skip detection and reuse the same path as the manual + // "tmux_attach" websocket message. + attachOrCreateTmuxSession(stream, tmuxAttachSession); + { + const session = sessionManager.getSession(boundSessionId); + if (session) session.tmuxSessionName = tmuxAttachSession; + } + sshLogger.info("Attached to requested tmux session", { + operation: "tmux_direct_attach", + sessionName: tmuxAttachSession, + hostId: id, + }); + ws.send( + JSON.stringify({ + type: "tmux_session_attached", + sessionName: tmuxAttachSession, + }), + ); + } else if (autoTmux && conn) { (async () => { try { const detection = await detectTmux(conn); @@ -1699,6 +1740,31 @@ wss.on("connection", async (ws: WebSocket, req) => { return; } + if ( + resolvedCredentials.authType === "tailscale" && + (authMethodNotAvailable || + err.message.includes("All configured authentication methods failed")) + ) { + sendLog( + "auth", + "error", + "Tailscale SSH authentication failed. Ensure Tailscale is running on the server, SSH is advertised (tailscale set --ssh), and your ACL policy permits this connection.", + ); + if (currentSessionId) { + sessionManager.destroySession(currentSessionId); + currentSessionId = null; + } + cleanupAuthState(connectionTimeout); + ws.send( + JSON.stringify({ + type: "error", + message: + "Tailscale SSH authentication failed. Ensure Tailscale is running on the server, SSH is advertised (tailscale set --ssh), and your ACL policy permits this connection.", + }), + ); + return; + } + if ( authMethodNotAvailable && resolvedCredentials.authType === "none" && @@ -1946,7 +2012,9 @@ wss.on("connection", async (ws: WebSocket, req) => { host: ip, port, username, - tryKeyboard: resolvedCredentials.authType !== "none", + tryKeyboard: + resolvedCredentials.authType !== "none" && + resolvedCredentials.authType !== "tailscale", keepaliveInterval: typeof hostKeepaliveInterval === "number" ? hostKeepaliveInterval * 1000 @@ -2017,8 +2085,11 @@ wss.on("connection", async (ws: WebSocket, req) => { }, }; - if (resolvedCredentials.authType === "none") { - // no credentials needed + if ( + resolvedCredentials.authType === "none" || + resolvedCredentials.authType === "tailscale" + ) { + // Tailscale SSH and "none" auth: daemon handles authorization, no credentials needed } else if (resolvedCredentials.authType === "password") { if (!resolvedCredentials.password) { sshLogger.error( diff --git a/src/backend/ssh/tmux-monitor-helpers.test.ts b/src/backend/ssh/tmux-monitor-helpers.test.ts new file mode 100644 index 00000000..9e47eec9 --- /dev/null +++ b/src/backend/ssh/tmux-monitor-helpers.test.ts @@ -0,0 +1,225 @@ +import { describe, it, expect } from "vitest"; +import { + SEP, + parseSessions, + parseWindows, + parsePanes, + parsePsOutput, + parseGpuOutput, + buildPaneMetrics, + attachPanesToWindows, + shellEscape, +} from "./tmux-monitor-helpers.js"; + +function join(...fields: (string | number)[]): string { + return fields.join(SEP); +} + +describe("parseSessions", () => { + it("parses tmux list-sessions output", () => { + const output = [ + join("training", 1760000000, 1760001000, 1), + join("lab|with|pipes", 1760000500, 1760002000, 0), + ].join("\n"); + + const sessions = parseSessions(output); + expect(sessions).toHaveLength(2); + expect(sessions[0]).toEqual({ + name: "training", + created: 1760000000, + lastActivity: 1760001000, + attachedClients: 1, + }); + // Session names containing "|" survive because SEP is a multi-char token + expect(sessions[1].name).toBe("lab|with|pipes"); + expect(sessions[1].attachedClients).toBe(0); + }); + + it("returns empty array for empty output", () => { + expect(parseSessions("")).toEqual([]); + }); +}); + +describe("parseWindows", () => { + it("groups windows by session", () => { + const output = [ + join("training", 0, 1, "vim"), + join("training", 1, 0, "logs"), + join("api", 0, 1, "server"), + ].join("\n"); + + const windows = parseWindows(output); + expect(windows.get("training")).toHaveLength(2); + expect(windows.get("training")![0]).toMatchObject({ + index: 0, + name: "vim", + active: true, + }); + expect(windows.get("api")![0].name).toBe("server"); + }); +}); + +describe("parsePanes", () => { + it("parses full pane lines including free-text fields", () => { + const output = join( + "training", + 0, + "%3", + 1, + 12345, + 1, + 120, + 40, + "python", + "/home/user/my|dir", + "gpu01: train.py", + ); + + const panes = parsePanes(output); + expect(panes).toHaveLength(1); + expect(panes[0]).toEqual({ + sessionName: "training", + windowIndex: 0, + id: "%3", + index: 1, + pid: 12345, + active: true, + width: 120, + height: 40, + command: "python", + path: "/home/user/my|dir", + title: "gpu01: train.py", + }); + }); +}); + +describe("parsePsOutput", () => { + it("parses ps -eo pid,ppid,pcpu,pmem,rss,comm output", () => { + const output = [ + " 1 0 0.0 0.1 1234 systemd", + "12345 1 2.5 1.0 50000 bash", + "12400 12345 95.3 12.5 800000 python3", + "garbage line", + ].join("\n"); + + const procs = parsePsOutput(output); + expect(procs).toHaveLength(3); + expect(procs[2]).toEqual({ + pid: 12400, + ppid: 12345, + cpu: 95.3, + mem: 12.5, + rss: 800000, + comm: "python3", + }); + }); +}); + +describe("parseGpuOutput", () => { + it("parses nvidia-smi csv output and sums per pid", () => { + const output = ["12400, 8000", "12400, 2000", "99999, 512"].join("\n"); + const gpu = parseGpuOutput(output); + expect(gpu.get(12400)).toBe(10000); + expect(gpu.get(99999)).toBe(512); + }); + + it("handles empty output (no GPU)", () => { + expect(parseGpuOutput("").size).toBe(0); + }); +}); + +describe("buildPaneMetrics", () => { + const panes = parsePanes( + [ + join("training", 0, "%1", 0, 100, 1, 80, 24, "bash", "/", "t"), + join("idle", 0, "%2", 0, 200, 1, 80, 24, "bash", "/", "t"), + ].join("\n"), + ); + + const processes = parsePsOutput( + [ + // pane %1: bash(100) -> python3(110) -> worker(111) + " 100 1 0.1 0.1 4000 bash", + " 110 100 90.0 10.0 700000 python3", + " 111 110 9.5 2.0 100000 dataloader", + // pane %2: bash(200) only + " 200 1 0.0 0.1 4000 bash", + // unrelated process + " 300 1 50.0 5.0 200000 chrome", + ].join("\n"), + ); + + it("aggregates descendant trees per pane", () => { + const metrics = buildPaneMetrics(panes, processes, new Map()); + const m1 = metrics.find((m) => m.paneId === "%1")!; + expect(m1.processCount).toBe(3); + expect(m1.cpuPercent).toBeCloseTo(99.6, 1); + expect(m1.memRssKb).toBe(804000); + expect(m1.topCommand).toBe("python3"); + + const m2 = metrics.find((m) => m.paneId === "%2")!; + expect(m2.processCount).toBe(1); + expect(m2.cpuPercent).toBe(0); + // Unrelated process is never attributed + expect(m2.memRssKb).toBe(4000); + }); + + it("attributes GPU memory through the process tree", () => { + const gpu = new Map([ + [110, 8000], + [300, 4000], + ]); + const metrics = buildPaneMetrics(panes, processes, gpu); + expect(metrics.find((m) => m.paneId === "%1")!.gpuMemMb).toBe(8000); + expect(metrics.find((m) => m.paneId === "%2")!.gpuMemMb).toBe(0); + }); + + it("handles a pane whose pid is missing from ps output", () => { + const orphan = parsePanes( + join("gone", 0, "%9", 0, 99999, 0, 80, 24, "bash", "/", "t"), + ); + const metrics = buildPaneMetrics(orphan, processes, new Map()); + expect(metrics[0].processCount).toBe(0); + expect(metrics[0].cpuPercent).toBe(0); + expect(metrics[0].topCommand).toBeNull(); + }); + + it("does not loop on cyclic ppid data", () => { + const cyclic = parsePsOutput( + [" 100 101 1.0 0.1 1000 a", " 101 100 1.0 0.1 1000 b"].join("\n"), + ); + const pane = parsePanes( + join("s", 0, "%1", 0, 100, 1, 80, 24, "a", "/", "t"), + ); + const metrics = buildPaneMetrics(pane, cyclic, new Map()); + expect(metrics[0].processCount).toBe(2); + }); +}); + +describe("attachPanesToWindows", () => { + it("places panes into their windows", () => { + const windows = parseWindows( + [join("s1", 0, 1, "main"), join("s1", 1, 0, "logs")].join("\n"), + ); + const panes = parsePanes( + [ + join("s1", 0, "%1", 0, 100, 1, 80, 24, "bash", "/", "t"), + join("s1", 1, "%2", 0, 200, 0, 80, 24, "tail", "/", "t"), + join("unknown", 5, "%3", 0, 300, 0, 80, 24, "bash", "/", "t"), + ].join("\n"), + ); + + attachPanesToWindows(windows, panes); + expect(windows.get("s1")![0].panes).toHaveLength(1); + expect(windows.get("s1")![0].panes[0].id).toBe("%1"); + expect(windows.get("s1")![1].panes[0].id).toBe("%2"); + }); +}); + +describe("shellEscape", () => { + it("wraps in single quotes and escapes embedded quotes", () => { + expect(shellEscape("simple")).toBe("'simple'"); + expect(shellEscape("it's")).toBe("'it'\\''s'"); + expect(shellEscape("$(rm -rf /)")).toBe("'$(rm -rf /)'"); + }); +}); diff --git a/src/backend/ssh/tmux-monitor-helpers.ts b/src/backend/ssh/tmux-monitor-helpers.ts new file mode 100644 index 00000000..b1b12e9b --- /dev/null +++ b/src/backend/ssh/tmux-monitor-helpers.ts @@ -0,0 +1,240 @@ +// Pure parsing/aggregation helpers for the tmux monitor module. +// Kept free of SSH/Express dependencies so they can be unit-tested. + +// Field separator used in tmux -F format strings. Session names, pane titles +// and paths may contain "|", and tmux sanitizes control characters (and, under +// non-UTF-8 locales, multibyte characters) in format output to "_", so a +// printable ASCII token is the only separator that survives everywhere. +export const SEP = "<>"; + +export interface TmuxPane { + id: string; + index: number; + pid: number; + active: boolean; + width: number; + height: number; + command: string; + path: string; + title: string; +} + +export interface TmuxWindow { + index: number; + name: string; + active: boolean; + panes: TmuxPane[]; +} + +export interface TmuxSessionSummary { + name: string; + created: number; + lastActivity: number; + attachedClients: number; +} + +export interface RawPane extends TmuxPane { + sessionName: string; + windowIndex: number; +} + +export interface ProcessInfo { + pid: number; + ppid: number; + cpu: number; + mem: number; + rss: number; + comm: string; +} + +export interface PaneMetrics { + paneId: string; + sessionName: string; + pid: number; + processCount: number; + cpuPercent: number; + memRssKb: number; + gpuMemMb: number; + topCommand: string | null; +} + +export function parseSessions(output: string): TmuxSessionSummary[] { + return output + .split("\n") + .filter(Boolean) + .map((line) => { + const [name, created, activity, attached] = line.split(SEP); + return { + name, + created: parseInt(created, 10) || 0, + lastActivity: parseInt(activity, 10) || 0, + attachedClients: parseInt(attached, 10) || 0, + }; + }); +} + +export function parseWindows(output: string): Map { + const bySession = new Map(); + for (const line of output.split("\n").filter(Boolean)) { + const [session, index, active, name] = line.split(SEP); + if (!bySession.has(session)) bySession.set(session, []); + bySession.get(session)!.push({ + index: parseInt(index, 10) || 0, + name: name || "", + active: active === "1", + panes: [], + }); + } + return bySession; +} + +export function parsePanes(output: string): RawPane[] { + return output + .split("\n") + .filter(Boolean) + .map((line) => { + const [ + sessionName, + windowIndex, + id, + index, + pid, + active, + width, + height, + command, + path, + title, + ] = line.split(SEP); + return { + sessionName, + windowIndex: parseInt(windowIndex, 10) || 0, + id, + index: parseInt(index, 10) || 0, + pid: parseInt(pid, 10) || 0, + active: active === "1", + width: parseInt(width, 10) || 0, + height: parseInt(height, 10) || 0, + command: command || "", + path: path || "", + title: title || "", + }; + }); +} + +export function parsePsOutput(output: string): ProcessInfo[] { + const processes: ProcessInfo[] = []; + for (const line of output.split("\n")) { + const parts = line.trim().split(/\s+/); + if (parts.length < 6) continue; + const pid = parseInt(parts[0], 10); + const ppid = parseInt(parts[1], 10); + if (isNaN(pid) || isNaN(ppid)) continue; + processes.push({ + pid, + ppid, + cpu: parseFloat(parts[2]) || 0, + mem: parseFloat(parts[3]) || 0, + rss: parseInt(parts[4], 10) || 0, + comm: parts.slice(5).join(" "), + }); + } + return processes; +} + +export function parseGpuOutput(output: string): Map { + const gpuByPid = new Map(); + for (const line of output.split("\n").filter(Boolean)) { + const [pid, mem] = line.split(",").map((s) => s.trim()); + const pidNum = parseInt(pid, 10); + const memNum = parseInt(mem, 10); + if (!isNaN(pidNum) && !isNaN(memNum)) { + gpuByPid.set(pidNum, (gpuByPid.get(pidNum) || 0) + memNum); + } + } + return gpuByPid; +} + +/** + * Map each pane's shell pid to its descendant process tree and aggregate + * CPU/RAM/GPU usage per pane. + */ +export function buildPaneMetrics( + panes: RawPane[], + processes: ProcessInfo[], + gpuByPid: Map, +): PaneMetrics[] { + const byPid = new Map(); + const childrenOf = new Map(); + for (const p of processes) { + byPid.set(p.pid, p); + if (!childrenOf.has(p.ppid)) childrenOf.set(p.ppid, []); + childrenOf.get(p.ppid)!.push(p.pid); + } + + return panes.map((pane) => { + // Walk the descendant tree starting at (and including) the pane's shell + const treePids: number[] = []; + const queue = [pane.pid]; + const seen = new Set(); + while (queue.length > 0) { + const pid = queue.shift()!; + if (seen.has(pid)) continue; + seen.add(pid); + if (byPid.has(pid)) treePids.push(pid); + for (const child of childrenOf.get(pid) || []) queue.push(child); + } + + let cpuPercent = 0; + let memRssKb = 0; + let gpuMemMb = 0; + let topCommand: string | null = null; + let topCpu = -1; + for (const pid of treePids) { + const p = byPid.get(pid)!; + cpuPercent += p.cpu; + memRssKb += p.rss; + gpuMemMb += gpuByPid.get(pid) || 0; + // The pane shell itself is rarely the interesting process + if (p.cpu > topCpu && pid !== pane.pid) { + topCpu = p.cpu; + topCommand = p.comm; + } + } + if (topCommand === null && treePids.length > 0) { + topCommand = byPid.get(treePids[0])!.comm; + } + + return { + paneId: pane.id, + sessionName: pane.sessionName, + pid: pane.pid, + processCount: treePids.length, + cpuPercent: Math.round(cpuPercent * 10) / 10, + memRssKb, + gpuMemMb, + topCommand, + }; + }); +} + +/** + * Group panes into their windows (mutates the window objects' pane arrays). + */ +export function attachPanesToWindows( + windows: Map, + panes: RawPane[], +): void { + for (const pane of panes) { + const sessionWindows = windows.get(pane.sessionName) || []; + const window = sessionWindows.find((w) => w.index === pane.windowIndex); + if (window) { + const { sessionName: _s, windowIndex: _w, ...paneFields } = pane; + window.panes.push(paneFields); + } + } +} + +export function shellEscape(s: string): string { + return "'" + s.replace(/'/g, "'\\''") + "'"; +} diff --git a/src/backend/ssh/tmux-monitor.ts b/src/backend/ssh/tmux-monitor.ts new file mode 100644 index 00000000..5d576282 --- /dev/null +++ b/src/backend/ssh/tmux-monitor.ts @@ -0,0 +1,928 @@ +import express from "express"; +import cookieParser from "cookie-parser"; +import { Client, type ConnectConfig } from "ssh2"; +import { eq, and } from "drizzle-orm"; +import { createCorsMiddleware } from "../utils/cors-config.js"; +import { AuthManager } from "../utils/auth-manager.js"; +import { SimpleDBOps } from "../utils/simple-db-ops.js"; +import { getDb, DatabaseSaveTrigger } from "../database/db/index.js"; +import { tmuxSessionTags, users } from "../database/db/schema.js"; +import { logAudit, getRequestMeta } from "../utils/audit-logger.js"; +import { sshLogger } from "../utils/logger.js"; +import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js"; +import { SSHHostKeyVerifier } from "./host-key-verifier.js"; +import { resolveHostById, checkHostAccess } from "./host-resolver.js"; +import { createJumpHostChain } from "./jump-host-chain.js"; +import { + createSocks5Connection, + type SOCKS5Config, +} from "../utils/socks5-helper.js"; +import { withConnection } from "./ssh-connection-pool.js"; +import { execCommand } from "./tmux-helper.js"; +import { + SEP, + parseSessions, + parseWindows, + parsePanes, + parsePsOutput, + parseGpuOutput, + buildPaneMetrics, + attachPanesToWindows, + shellEscape, + type RawPane, + type TmuxSessionSummary, + type TmuxWindow, + type PaneMetrics, +} from "./tmux-monitor-helpers.js"; +import type { SSHHost, AuthenticatedRequest } from "../../types/index.js"; + +const PANE_ID_RE = /^%\d+$/; +// tmux session names cannot contain ":" or "."; keep to a conservative +// printable subset so the name is safe as a tmux target everywhere. +const SESSION_NAME_RE = /^[A-Za-z0-9_@%+=-]{1,64}$/; +const MAX_SEARCH_PANES = 100; +const MAX_MATCHES_PER_PANE = 50; +const SEARCH_HISTORY_LINES = 2000; +const SEARCH_CONCURRENCY = 4; + +interface TmuxSessionOverview extends TmuxSessionSummary { + windows: TmuxWindow[]; + tags: string[]; +} + +// --------------------------------------------------------------------------- +// SSH connection (lean variant of the per-module pattern used by server-stats +// and docker; jump hosts and SOCKS5 reuse the shared helpers) +// --------------------------------------------------------------------------- + +async function buildSshConfig(host: SSHHost): Promise { + const base: ConnectConfig = { + host: (host.ip || "").replace(/^\[|\]$/g, ""), + port: host.port, + username: host.username, + tryKeyboard: true, + keepaliveInterval: 30000, + keepaliveCountMax: 3, + readyTimeout: 60000, + hostVerifier: await SSHHostKeyVerifier.createHostVerifier( + host.id, + host.ip, + host.port, + null, + host.userId || "", + false, + ), + algorithms: SSH_ALGORITHMS, + } as ConnectConfig; + + if (host.authType === "password") { + if (!host.password) { + throw new Error(`No password available for host ${host.ip}`); + } + base.password = host.password; + } else if (host.authType === "key") { + if (!host.key || !host.key.includes("-----BEGIN")) { + throw new Error(`No valid SSH key available for host ${host.ip}`); + } + const cleanKey = host.key + .trim() + .replace(/\r\n/g, "\n") + .replace(/\r/g, "\n"); + (base as Record).privateKey = Buffer.from( + cleanKey, + "utf8", + ); + if (host.keyPassword) { + (base as Record).passphrase = host.keyPassword; + } + } else if (host.authType === "none") { + // no credentials needed + } else { + // opkssh and other interactive flows are not supported by this module + throw new Error( + `Authentication type '${host.authType}' is not supported by the tmux monitor. Open a terminal connection instead.`, + ); + } + + return base; +} + +export function connectToHost(host: SSHHost): () => Promise { + return async () => { + const config = await buildSshConfig(host); + const client = new Client(); + + const proxyConfig: SOCKS5Config | null = + host.useSocks5 && + (host.socks5Host || + (host.socks5ProxyChain && host.socks5ProxyChain.length > 0)) + ? { + useSocks5: host.useSocks5, + socks5Host: host.socks5Host, + socks5Port: host.socks5Port, + socks5Username: host.socks5Username, + socks5Password: host.socks5Password, + socks5ProxyChain: host.socks5ProxyChain, + } + : null; + + let jumpClient: Client | null = null; + if (host.jumpHosts && host.jumpHosts.length > 0 && host.userId) { + jumpClient = await createJumpHostChain( + host.jumpHosts, + host.userId, + proxyConfig, + ); + if (!jumpClient) { + throw new Error("Failed to establish jump host chain"); + } + } else if (proxyConfig) { + const proxySocket = await createSocks5Connection( + host.ip, + host.port, + proxyConfig, + ); + if (proxySocket) { + config.sock = proxySocket; + } + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + client.end(); + jumpClient?.end(); + reject(new Error("SSH connection timeout")); + }, 30000); + + client.on("ready", () => { + clearTimeout(timeout); + resolve(client); + }); + client.on("error", (err) => { + clearTimeout(timeout); + jumpClient?.end(); + reject(err); + }); + client.on( + "keyboard-interactive", + (_name, _instructions, _lang, prompts, finish) => { + finish( + prompts.map((p) => + /password/i.test(p.prompt) ? host.password || "" : "", + ), + ); + }, + ); + + if (jumpClient) { + jumpClient.forwardOut( + "127.0.0.1", + 0, + host.ip, + host.port, + (err, stream) => { + if (err) { + clearTimeout(timeout); + jumpClient!.end(); + reject( + new Error( + "Failed to forward through jump host: " + err.message, + ), + ); + return; + } + config.sock = stream; + client.connect(config); + }, + ); + } else { + client.connect(config); + } + }); + }; +} + +function getPoolKey(host: SSHHost): string { + const socks5Key = host.useSocks5 + ? `:socks5:${host.socks5Host}:${host.socks5Port}` + : ""; + return `tmux-monitor:${host.userId}:${host.ip}:${host.port}:${host.username}${socks5Key}`; +} + +async function withHostConnection( + host: SSHHost, + fn: (client: Client) => Promise, +): Promise { + return withConnection(getPoolKey(host), connectToHost(host), fn); +} + +// --------------------------------------------------------------------------- +// tmux queries +// --------------------------------------------------------------------------- + +async function tmuxAvailable(conn: Client): Promise { + try { + await execCommand(conn, "command -v tmux"); + return true; + } catch { + return false; + } +} + +async function runTmuxList(conn: Client, command: string): Promise { + try { + return await execCommand(conn, command); + } catch { + return ""; // tmux server not running -- no sessions + } +} + +function listSessionsCmd(): string { + return `tmux list-sessions -F "#{session_name}${SEP}#{session_created}${SEP}#{session_activity}${SEP}#{session_attached}" 2>/dev/null`; +} + +function listWindowsCmd(): string { + return `tmux list-windows -a -F "#{session_name}${SEP}#{window_index}${SEP}#{window_active}${SEP}#{window_name}" 2>/dev/null`; +} + +function listPanesCmd(): string { + return `tmux list-panes -a -F "#{session_name}${SEP}#{window_index}${SEP}#{pane_id}${SEP}#{pane_index}${SEP}#{pane_pid}${SEP}#{pane_active}${SEP}#{pane_width}${SEP}#{pane_height}${SEP}#{pane_current_command}${SEP}#{pane_current_path}${SEP}#{pane_title}" 2>/dev/null`; +} + +async function listPanesRaw(conn: Client): Promise { + return parsePanes(await runTmuxList(conn, listPanesCmd())); +} + +async function fetchSessionTags( + userId: string, + hostId: number, +): Promise> { + const rows = await getDb() + .select() + .from(tmuxSessionTags) + .where( + and( + eq(tmuxSessionTags.userId, userId), + eq(tmuxSessionTags.hostId, hostId), + ), + ); + const bySession = new Map(); + for (const row of rows) { + if (!bySession.has(row.sessionName)) bySession.set(row.sessionName, []); + bySession.get(row.sessionName)!.push(row.tag); + } + return bySession; +} + +async function collectPaneMetrics( + conn: Client, + panes: RawPane[], +): Promise { + let psOutput = ""; + try { + psOutput = await execCommand( + conn, + "ps -eo pid=,ppid=,pcpu=,pmem=,rss=,comm= 2>/dev/null", + ); + } catch { + return []; + } + + // GPU memory per pid (best effort; nvidia-smi may not exist) + let gpuOutput = ""; + try { + gpuOutput = await execCommand( + conn, + "command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi --query-compute-apps=pid,used_gpu_memory --format=csv,noheader,nounits 2>/dev/null || true", + ); + } catch { + // no GPU on host + } + + return buildPaneMetrics( + panes, + parsePsOutput(psOutput), + parseGpuOutput(gpuOutput), + ); +} + +// --------------------------------------------------------------------------- +// Express app +// --------------------------------------------------------------------------- + +const app = express(); +const authManager = AuthManager.getInstance(); + +app.use(createCorsMiddleware(["GET", "POST", "PUT", "DELETE", "OPTIONS"])); +app.use(cookieParser()); +app.use(express.json({ limit: "1mb" })); +app.use((_req, res, next) => { + res.setHeader("Cache-Control", "no-store"); + next(); +}); +app.use(authManager.createAuthMiddleware()); + +/** + * Resolve the host for a request and verify the user can access it. + * Sends the error response and returns null when access is denied. + */ +async function requireHost( + req: express.Request, + res: express.Response, + permission: "read" | "execute" = "read", +): Promise { + const userId = (req as unknown as AuthenticatedRequest).userId; + const hostId = parseInt(String(req.params.hostId), 10); + if (isNaN(hostId)) { + res.status(400).json({ error: "Invalid host ID" }); + return null; + } + if (!SimpleDBOps.isUserDataUnlocked(userId)) { + res.status(401).json({ error: "User data is locked" }); + return null; + } + + let host: SSHHost | null = null; + try { + host = await resolveHostById(hostId, userId); + } catch (err) { + sshLogger.error(`Failed to resolve host ${hostId} for tmux monitor`, err); + } + if (!host) { + res.status(404).json({ error: "Host not found" }); + return null; + } + + const hasAccess = await checkHostAccess( + hostId, + userId, + host.userId || userId, + permission, + ); + if (!hasAccess) { + res.status(403).json({ error: "Access denied" }); + return null; + } + + // The monitor is opt-in per host (same pattern as enableDocker in + // docker.ts): hiding the UI is not enough, the API must refuse too. + if (!host.enableTmuxMonitor) { + res + .status(403) + .json({ error: "Tmux Monitor is not enabled for this host" }); + return null; + } + return host; +} + +function toErrorMessage(err: unknown): string { + return err instanceof Error ? err.message : "Unknown error"; +} + +// Destructive tmux actions terminate processes on the remote host, so they +// land in the audit log like other host-level mutations (see host.ts). +async function auditTmuxAction( + req: express.Request, + host: SSHHost, + action: string, + resourceName: string, + details?: Record, +): Promise { + const userId = (req as unknown as AuthenticatedRequest).userId; + const { ipAddress, userAgent } = getRequestMeta(req); + let username = userId; + try { + const actor = await getDb() + .select({ username: users.username }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + username = actor[0]?.username ?? userId; + } catch { + // fall back to the raw user id + } + await logAudit({ + userId, + username, + action, + resourceType: "host", + resourceId: String(host.id), + resourceName, + details: details ? JSON.stringify(details) : undefined, + ipAddress, + userAgent, + success: true, + }); +} + +// Typed error codes so the frontend can render a helpful state instead of a +// raw 500 (same pattern as SESSION_EXPIRED handling in main-axios). +type TmuxErrorCode = + | "TMUX_NOT_INSTALLED" + | "TMUX_NO_SERVER" + | "HOST_UNREACHABLE" + | "TMUX_ERROR"; + +function classifyTmuxError(err: unknown): TmuxErrorCode { + const msg = err instanceof Error ? err.message : ""; + if (/command not found|exited with code 127/i.test(msg)) + return "TMUX_NOT_INSTALLED"; + if (/no server running|lost server/i.test(msg)) return "TMUX_NO_SERVER"; + if ( + /timeout|timed out|econnrefused|ehostunreach|enotfound|enetunreach|econnreset|authentication|handshake|keepalive/i.test( + msg, + ) + ) + return "HOST_UNREACHABLE"; + return "TMUX_ERROR"; +} + +function sendTmuxError( + res: express.Response, + err: unknown, + context: string, + hostId: number, +): void { + const code = classifyTmuxError(err); + const status = code === "TMUX_ERROR" ? 500 : 503; + const error = + code === "TMUX_NOT_INSTALLED" + ? "tmux is not installed on this host" + : code === "TMUX_NO_SERVER" + ? "No tmux server is running on this host" + : code === "HOST_UNREACHABLE" + ? "Could not connect to the host" + : toErrorMessage(err); + sshLogger.error(`tmux ${context} failed for host ${hostId}`, err); + res.status(status).json({ error, code }); +} + +app.get("/tmux_monitor/:hostId/overview", async (req, res) => { + const userId = (req as unknown as AuthenticatedRequest).userId; + const host = await requireHost(req, res); + if (!host) return; + + try { + const result = await withHostConnection(host, async (conn) => { + if (!(await tmuxAvailable(conn))) { + return { available: false, sessions: [] as TmuxSessionOverview[] }; + } + const [sessionsOut, windowsOut, panesOut] = await Promise.all([ + runTmuxList(conn, listSessionsCmd()), + runTmuxList(conn, listWindowsCmd()), + runTmuxList(conn, listPanesCmd()), + ]); + const sessions = parseSessions(sessionsOut); + const windows = parseWindows(windowsOut); + attachPanesToWindows(windows, parsePanes(panesOut)); + + const tags = await fetchSessionTags(userId, host.id); + const full: TmuxSessionOverview[] = sessions.map((s) => ({ + ...s, + windows: windows.get(s.name) || [], + tags: tags.get(s.name) || [], + })); + return { available: true, sessions: full }; + }); + res.json(result); + } catch (err) { + sendTmuxError(res, err, "overview", host.id); + } +}); + +// Focus a pane: select its window and pane on the server so every attached +// client (including the monitor's embedded terminal) switches to it. +app.post("/tmux_monitor/:hostId/focus", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const paneId = String((req.body as { paneId?: string })?.paneId || ""); + if (!PANE_ID_RE.test(paneId)) { + return res.status(400).json({ error: "Invalid pane ID" }); + } + + try { + await withHostConnection(host, (conn) => + // A pane id is a valid window target: tmux resolves it to the window + // containing the pane. + execCommand( + conn, + `tmux select-window -t ${shellEscape(paneId)} \\; select-pane -t ${shellEscape(paneId)}`, + ), + ); + res.json({ ok: true }); + } catch (err) { + sendTmuxError(res, err, "focus", host.id); + } +}); + +// Create a detached session. Starts the tmux server if none is running. +app.post("/tmux_monitor/:hostId/sessions", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const name = String((req.body as { name?: string })?.name || "").trim(); + if (!SESSION_NAME_RE.test(name)) { + return res.status(400).json({ error: "Invalid session name" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand(conn, `tmux new-session -d -s ${shellEscape(name)}`), + ); + sshLogger.info("tmux session created", { + operation: "tmux_session_create", + hostId: host.id, + sessionName: name, + }); + res.json({ ok: true, name }); + } catch (err) { + if (/duplicate session/i.test(toErrorMessage(err))) { + return res + .status(409) + .json({ error: "A session with this name already exists" }); + } + sendTmuxError(res, err, "create session", host.id); + } +}); + +// Create a window in an existing session. The session name comes from tmux's +// own listing, so it is only checked for characters that would change the +// target's meaning (":" and "." are window/pane separators in tmux targets); +// "=" prefixes the target for an exact-name match. +app.post("/tmux_monitor/:hostId/windows", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const sessionName = String( + (req.body as { sessionName?: string })?.sessionName || "", + ).trim(); + if (!sessionName || /[:.\n]/.test(sessionName)) { + return res.status(400).json({ error: "Invalid session name" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand(conn, `tmux new-window -t ${shellEscape(`=${sessionName}`)}`), + ); + sshLogger.info("tmux window created", { + operation: "tmux_window_create", + hostId: host.id, + sessionName, + }); + res.json({ ok: true }); + } catch (err) { + if (/can't find session|no such session/i.test(toErrorMessage(err))) { + return res.status(404).json({ error: "Session not found" }); + } + sendTmuxError(res, err, "create window", host.id); + } +}); + +// Rename a session. Saved tags follow the session to its new name (for every +// user — the session itself is shared on the host). +app.post("/tmux_monitor/:hostId/rename", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const body = req.body as { sessionName?: string; newName?: string }; + const sessionName = String(body?.sessionName || "").trim(); + const newName = String(body?.newName || "").trim(); + if (!sessionName || /[:.\n]/.test(sessionName)) { + return res.status(400).json({ error: "Invalid session name" }); + } + if (!SESSION_NAME_RE.test(newName)) { + return res.status(400).json({ error: "Invalid new session name" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand( + conn, + `tmux rename-session -t ${shellEscape(`=${sessionName}`)} ${shellEscape(newName)}`, + ), + ); + await getDb() + .update(tmuxSessionTags) + .set({ sessionName: newName }) + .where( + and( + eq(tmuxSessionTags.hostId, host.id), + eq(tmuxSessionTags.sessionName, sessionName), + ), + ); + await DatabaseSaveTrigger.triggerSave("tmux_session_tags_updated"); + sshLogger.info("tmux session renamed", { + operation: "tmux_session_rename", + hostId: host.id, + sessionName, + newName, + }); + await auditTmuxAction(req, host, "tmux_session_rename", sessionName, { + newName, + }); + res.json({ ok: true, name: newName }); + } catch (err) { + if (/can't find session|no such session/i.test(toErrorMessage(err))) { + return res.status(404).json({ error: "Session not found" }); + } + if (/duplicate session/i.test(toErrorMessage(err))) { + return res + .status(409) + .json({ error: "A session with this name already exists" }); + } + sendTmuxError(res, err, "rename session", host.id); + } +}); + +// Kill a session and drop its saved tags. +app.post("/tmux_monitor/:hostId/kill", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const sessionName = String( + (req.body as { sessionName?: string })?.sessionName || "", + ).trim(); + if (!sessionName || /[:.\n]/.test(sessionName)) { + return res.status(400).json({ error: "Invalid session name" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand( + conn, + `tmux kill-session -t ${shellEscape(`=${sessionName}`)}`, + ), + ); + await getDb() + .delete(tmuxSessionTags) + .where( + and( + eq(tmuxSessionTags.hostId, host.id), + eq(tmuxSessionTags.sessionName, sessionName), + ), + ); + await DatabaseSaveTrigger.triggerSave("tmux_session_tags_updated"); + sshLogger.info("tmux session killed", { + operation: "tmux_session_kill", + hostId: host.id, + sessionName, + }); + await auditTmuxAction(req, host, "tmux_session_kill", sessionName); + res.json({ ok: true }); + } catch (err) { + if (/can't find session|no such session/i.test(toErrorMessage(err))) { + return res.status(404).json({ error: "Session not found" }); + } + sendTmuxError(res, err, "kill session", host.id); + } +}); + +// Kill a window (and every pane in it). Killing the last window of a session +// ends the session — tmux semantics. +app.post("/tmux_monitor/:hostId/kill-window", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const body = req.body as { sessionName?: string; windowIndex?: number }; + const sessionName = String(body?.sessionName || "").trim(); + const windowIndex = Number(body?.windowIndex); + if (!sessionName || /[:.\n]/.test(sessionName)) { + return res.status(400).json({ error: "Invalid session name" }); + } + if (!Number.isInteger(windowIndex) || windowIndex < 0) { + return res.status(400).json({ error: "Invalid window index" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand( + conn, + `tmux kill-window -t ${shellEscape(`=${sessionName}:${windowIndex}`)}`, + ), + ); + sshLogger.info("tmux window killed", { + operation: "tmux_window_kill", + hostId: host.id, + sessionName, + windowIndex, + }); + await auditTmuxAction(req, host, "tmux_window_kill", sessionName, { + windowIndex, + }); + res.json({ ok: true }); + } catch (err) { + if ( + /can't find window|no such window|can't find session/i.test( + toErrorMessage(err), + ) + ) { + return res.status(404).json({ error: "Window not found" }); + } + sendTmuxError(res, err, "kill window", host.id); + } +}); + +// Kill a single pane. Killing the last pane of a window closes the window, +// and the last window of a session ends the session — tmux semantics. +app.post("/tmux_monitor/:hostId/kill-pane", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const paneId = String((req.body as { paneId?: string })?.paneId || ""); + if (!PANE_ID_RE.test(paneId)) { + return res.status(400).json({ error: "Invalid pane ID" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand(conn, `tmux kill-pane -t ${shellEscape(paneId)}`), + ); + sshLogger.info("tmux pane killed", { + operation: "tmux_pane_kill", + hostId: host.id, + paneId, + }); + await auditTmuxAction(req, host, "tmux_pane_kill", paneId); + res.json({ ok: true }); + } catch (err) { + if (/can't find pane|no such pane/i.test(toErrorMessage(err))) { + return res.status(404).json({ error: "Pane not found" }); + } + sendTmuxError(res, err, "kill pane", host.id); + } +}); + +// Split the window containing a pane. "h" places the new pane to the right, +// "v" below — matching tmux's own -h/-v semantics. The new pane starts in the +// source pane's working directory. +app.post("/tmux_monitor/:hostId/split", async (req, res) => { + const host = await requireHost(req, res, "execute"); + if (!host) return; + + const body = req.body as { paneId?: string; direction?: string }; + const paneId = String(body?.paneId || ""); + const direction = body?.direction === "v" ? "-v" : "-h"; + if (!PANE_ID_RE.test(paneId)) { + return res.status(400).json({ error: "Invalid pane ID" }); + } + if (body?.direction !== "h" && body?.direction !== "v") { + return res.status(400).json({ error: "Invalid split direction" }); + } + + try { + await withHostConnection(host, (conn) => + execCommand( + conn, + `tmux split-window ${direction} -t ${shellEscape(paneId)} -c ${shellEscape("#{pane_current_path}")}`, + ), + ); + sshLogger.info("tmux pane split", { + operation: "tmux_pane_split", + hostId: host.id, + paneId, + direction: body.direction, + }); + res.json({ ok: true }); + } catch (err) { + sendTmuxError(res, err, "split", host.id); + } +}); + +app.get("/tmux_monitor/:hostId/search", async (req, res) => { + const host = await requireHost(req, res); + if (!host) return; + + const query = String(req.query.q || "").trim(); + if (!query) { + return res.status(400).json({ error: "Missing search query" }); + } + + try { + const results = await withHostConnection(host, async (conn) => { + const allPanes = await listPanesRaw(conn); + const panes = allPanes.slice(0, MAX_SEARCH_PANES); + // Flips to true whenever a limit was hit, so the UI can tell the user + // the results are partial instead of silently truncating. + let truncated = allPanes.length > MAX_SEARCH_PANES; + const matches: Array<{ + paneId: string; + sessionName: string; + windowIndex: number; + line: number; + text: string; + }> = []; + + // Bounded concurrency; each search runs capture+grep remotely so only + // matching lines travel back over the wire. + for (let i = 0; i < panes.length; i += SEARCH_CONCURRENCY) { + const batch = panes.slice(i, i + SEARCH_CONCURRENCY); + await Promise.all( + batch.map(async (pane) => { + try { + const output = await execCommand( + conn, + `tmux capture-pane -p -J -t ${shellEscape(pane.id)} -S -${SEARCH_HISTORY_LINES} 2>/dev/null | grep -n -i -F -- ${shellEscape(query)} | head -${MAX_MATCHES_PER_PANE}`, + ); + const lines = output.split("\n").filter(Boolean); + if (lines.length >= MAX_MATCHES_PER_PANE) truncated = true; + for (const line of lines) { + const sep = line.indexOf(":"); + if (sep === -1) continue; + matches.push({ + paneId: pane.id, + sessionName: pane.sessionName, + windowIndex: pane.windowIndex, + line: parseInt(line.slice(0, sep), 10) || 0, + text: line.slice(sep + 1).slice(0, 500), + }); + } + } catch { + // grep exits non-zero when there are no matches -- not an error + } + }), + ); + } + return { matches, truncated }; + }); + res.json({ + query, + matches: results.matches, + truncated: results.truncated, + searchedLines: SEARCH_HISTORY_LINES, + maxPanes: MAX_SEARCH_PANES, + }); + } catch (err) { + sendTmuxError(res, err, "search", host.id); + } +}); + +app.get("/tmux_monitor/:hostId/metrics", async (req, res) => { + const host = await requireHost(req, res); + if (!host) return; + + try { + const metrics = await withHostConnection(host, async (conn) => { + const panes = await listPanesRaw(conn); + if (panes.length === 0) return []; + return collectPaneMetrics(conn, panes); + }); + res.json({ panes: metrics }); + } catch (err) { + sendTmuxError(res, err, "metrics", host.id); + } +}); + +app.put("/tmux_monitor/:hostId/tags", async (req, res) => { + const userId = (req as unknown as AuthenticatedRequest).userId; + const host = await requireHost(req, res); + if (!host) return; + + const { sessionName, tags } = req.body as { + sessionName?: string; + tags?: string[]; + }; + if (!sessionName || typeof sessionName !== "string") { + return res.status(400).json({ error: "Missing session name" }); + } + if (!Array.isArray(tags) || tags.some((t) => typeof t !== "string")) { + return res.status(400).json({ error: "Tags must be an array of strings" }); + } + const cleanTags = [ + ...new Set(tags.map((t) => t.trim().slice(0, 64)).filter(Boolean)), + ].slice(0, 20); + + try { + const db = getDb(); + await db + .delete(tmuxSessionTags) + .where( + and( + eq(tmuxSessionTags.userId, userId), + eq(tmuxSessionTags.hostId, host.id), + eq(tmuxSessionTags.sessionName, sessionName), + ), + ); + if (cleanTags.length > 0) { + await db.insert(tmuxSessionTags).values( + cleanTags.map((tag) => ({ + userId, + hostId: host.id, + sessionName, + tag, + })), + ); + } + await DatabaseSaveTrigger.triggerSave("tmux_session_tags_updated"); + res.json({ sessionName, tags: cleanTags }); + } catch (err) { + sshLogger.error( + `Failed to save tmux session tags for host ${host.id}`, + err, + ); + res.status(500).json({ error: toErrorMessage(err) }); + } +}); + +const PORT = 30010; +app.listen(PORT, () => {}); diff --git a/src/backend/ssh/tunnel.ts b/src/backend/ssh/tunnel.ts index a6ae3219..a5f95933 100644 --- a/src/backend/ssh/tunnel.ts +++ b/src/backend/ssh/tunnel.ts @@ -24,6 +24,7 @@ import type { } from "../../types/index.js"; import { CONNECTION_STATES } from "../../types/index.js"; import { tunnelLogger } from "../utils/logger.js"; +import { logAudit } from "../utils/audit-logger.js"; import { SystemCrypto } from "../utils/system-crypto.js"; import { SimpleDBOps } from "../utils/simple-db-ops.js"; import { DataCrypto } from "../utils/data-crypto.js"; @@ -1186,6 +1187,20 @@ async function connectSSHTunnel( endpointPort: tunnelConfig.endpointPort, }); + logAudit({ + userId: tunnelConfig.sourceUserId, + username: tunnelConfig.sourceUserId, + action: "tunnel_connect", + resourceType: "tunnel", + resourceId: String(tunnelConfig.sourceHostId), + resourceName: tunnelName, + details: JSON.stringify({ + mode: getTunnelMode(tunnelConfig), + sourcePort: tunnelConfig.sourcePort, + }), + success: true, + }); + broadcastTunnelStatus(tunnelName, { connected: true, status: CONNECTION_STATES.CONNECTED, diff --git a/src/backend/starter.ts b/src/backend/starter.ts index a6d8aed8..d2b9e553 100644 --- a/src/backend/starter.ts +++ b/src/backend/starter.ts @@ -104,7 +104,7 @@ import { await AutoSSLSetup.initialize(); systemLogger.success("SSL setup completed", { operation: "backend_init_ssl", - sslEnabled: process.env.SSL_ENABLED === "true", + sslEnabled: process.env.ENABLE_SSL === "true", }); const dbModule = await import("./database/db/index.js"); @@ -142,9 +142,10 @@ import { await import("./ssh/terminal.js"); await import("./ssh/tunnel.js"); await import("./ssh/file-manager.js"); - await import("./ssh/server-stats.js"); + await import("./ssh/host-metrics.js"); await import("./ssh/docker.js"); await import("./ssh/docker-console.js"); + await import("./ssh/tmux-monitor.js"); // --- tmux-monitor --- await import("./dashboard.js"); // Initialize log level from database settings @@ -191,7 +192,7 @@ import { systemLogger.success("Termix backend started successfully", { operation: "backend_init_complete", port: process.env.PORT || 4090, - ssl: process.env.SSL_ENABLED === "true", + ssl: process.env.ENABLE_SSL === "true", duration: Date.now() - initStartTime, }); diff --git a/src/backend/swagger.ts b/src/backend/swagger.ts index feb9221f..f9e7c932 100644 --- a/src/backend/swagger.ts +++ b/src/backend/swagger.ts @@ -108,8 +108,8 @@ const swaggerOptions: SwaggerJSDocOptions = { description: "SSH tunnel connection management", }, { - name: "Server Stats", - description: "Server status monitoring and metrics collection", + name: "Host Metrics", + description: "Host status monitoring, metrics collection, and managers", }, { name: "File Manager", diff --git a/src/backend/utils/audit-logger.test.ts b/src/backend/utils/audit-logger.test.ts new file mode 100644 index 00000000..e88862ce --- /dev/null +++ b/src/backend/utils/audit-logger.test.ts @@ -0,0 +1,132 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("../database/db/index.js", () => ({ + db: { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockResolvedValue(undefined), + }), + $client: { + prepare: vi.fn().mockReturnValue({ + get: vi.fn().mockReturnValue({ count: 0 }), + run: vi.fn(), + }), + }, + }, +})); + +import { logAudit, getRequestMeta } from "./audit-logger.js"; +import { db } from "../database/db/index.js"; + +const mockDb = db as { + insert: ReturnType; + $client: { prepare: ReturnType }; +}; + +describe("logAudit", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockDb.insert.mockReturnValue({ + values: vi.fn().mockResolvedValue(undefined), + }); + mockDb.$client.prepare.mockReturnValue({ + get: vi.fn().mockReturnValue({ count: 0 }), + run: vi.fn(), + }); + }); + + it("inserts an audit log entry with all required fields", async () => { + const params = { + userId: "user-1", + username: "alice", + action: "create_host", + resourceType: "host", + resourceId: "42", + resourceName: "my-server", + ipAddress: "1.2.3.4", + userAgent: "Mozilla/5.0", + success: true, + }; + + await logAudit(params); + + expect(mockDb.insert).toHaveBeenCalledOnce(); + const valuesFn = mockDb.insert.mock.results[0].value.values; + expect(valuesFn).toHaveBeenCalledWith( + expect.objectContaining({ + userId: "user-1", + username: "alice", + action: "create_host", + resourceType: "host", + resourceId: "42", + resourceName: "my-server", + success: true, + }), + ); + }); + + it("does not throw when insert fails", async () => { + mockDb.insert.mockReturnValue({ + values: vi.fn().mockRejectedValue(new Error("db error")), + }); + + await expect( + logAudit({ + userId: "u", + username: "u", + action: "x", + resourceType: "y", + success: false, + }), + ).resolves.toBeUndefined(); + }); + + it("triggers pruning when row count exceeds threshold", async () => { + const prepareMock = vi.fn(); + const runMock = vi.fn(); + prepareMock.mockImplementation((sql: string) => { + if (sql.includes("COUNT(*)")) { + return { get: () => ({ count: 10001 }) }; + } + return { run: runMock }; + }); + mockDb.$client.prepare = prepareMock; + + await logAudit({ + userId: "u", + username: "u", + action: "x", + resourceType: "y", + success: true, + }); + + const deleteCalled = prepareMock.mock.calls.some((args: unknown[]) => + String(args[0]).includes("DELETE"), + ); + expect(deleteCalled).toBe(true); + expect(runMock).toHaveBeenCalledWith(1001); + }); +}); + +describe("getRequestMeta", () => { + it("extracts ip from x-forwarded-for header", () => { + const req = { + headers: { + "x-forwarded-for": "10.0.0.1, 10.0.0.2", + "user-agent": "TestAgent/1.0", + }, + ip: "127.0.0.1", + }; + const meta = getRequestMeta(req as never); + expect(meta.ipAddress).toBe("10.0.0.1"); + expect(meta.userAgent).toBe("TestAgent/1.0"); + }); + + it("falls back to req.ip when no forwarded header", () => { + const req = { + headers: { "user-agent": "Bot/2" }, + ip: "192.168.1.1", + }; + const meta = getRequestMeta(req as never); + expect(meta.ipAddress).toBe("192.168.1.1"); + }); +}); diff --git a/src/backend/utils/audit-logger.ts b/src/backend/utils/audit-logger.ts new file mode 100644 index 00000000..9fe31acb --- /dev/null +++ b/src/backend/utils/audit-logger.ts @@ -0,0 +1,68 @@ +import type { Request } from "express"; +import { db } from "../database/db/index.js"; +import { auditLogs } from "../database/db/schema.js"; + +const PRUNE_MAX = 10000; +const PRUNE_TARGET = 9000; + +export interface AuditLogParams { + userId: string; + username: string; + action: string; + resourceType: string; + resourceId?: string; + resourceName?: string; + details?: string; + ipAddress?: string; + userAgent?: string; + success: boolean; + errorMessage?: string; +} + +export async function logAudit(params: AuditLogParams): Promise { + try { + await db.insert(auditLogs).values({ + userId: params.userId, + username: params.username, + action: params.action, + resourceType: params.resourceType, + resourceId: params.resourceId ?? null, + resourceName: params.resourceName ?? null, + details: params.details ?? null, + ipAddress: params.ipAddress ?? null, + userAgent: params.userAgent ?? null, + success: params.success, + errorMessage: params.errorMessage ?? null, + }); + + const countResult = db.$client + .prepare("SELECT COUNT(*) as count FROM audit_logs") + .get() as { count: number }; + + if (countResult.count >= PRUNE_MAX) { + const deleteCount = countResult.count - PRUNE_TARGET; + db.$client + .prepare( + `DELETE FROM audit_logs WHERE id IN ( + SELECT id FROM audit_logs ORDER BY timestamp ASC LIMIT ? + )`, + ) + .run(deleteCount); + } + } catch { + // audit logging must never throw and break the caller + } +} + +export function getRequestMeta(req: Request): { + ipAddress: string; + userAgent: string; +} { + const forwarded = req.headers["x-forwarded-for"]; + const ipAddress = + (Array.isArray(forwarded) ? forwarded[0] : forwarded?.split(",")[0]) || + req.ip || + ""; + const userAgent = (req.headers["user-agent"] as string) || ""; + return { ipAddress, userAgent }; +} diff --git a/src/main.tsx b/src/main.tsx index 3609930e..329eaa06 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -32,8 +32,8 @@ const FileManagerApp = lazy(() => const TunnelApp = lazy(() => import("@/features/tunnel/TunnelApp").then((m) => ({ default: m.default })), ); -const ServerStatsApp = lazy(() => - import("@/features/server-stats/ServerStatsApp").then((m) => ({ +const HostMetricsApp = lazy(() => + import("@/features/host-metrics/HostMetricsApp").then((m) => ({ default: m.default, })), ); @@ -45,6 +45,12 @@ const GuacamoleApp = lazy(() => default: m.default, })), ); +// --- tmux-monitor --- +const TmuxMonitorApp = lazy(() => + import("@/features/tmux-monitor/TmuxMonitorApp").then((m) => ({ + default: m.default, + })), +); const ElectronVersionCheck = lazy(() => import("@/user/ElectronVersionCheck").then((module) => ({ @@ -63,22 +69,37 @@ function FullscreenApp() { const searchParams = new URLSearchParams(window.location.search); const view = searchParams.get("view"); const hostId = searchParams.get("hostId"); + const tmuxSession = searchParams.get("tmuxSession"); switch (view) { case "terminal": - return ; + return ( + + ); case "file-manager": return ; case "tunnel": return ; + case "host-metrics": case "server-stats": - return ; + return ; case "docker": return ; case "rdp": case "vnc": case "telnet": - return ; + return ( + + ); + case "tmux-monitor": // --- tmux-monitor --- + case "tmux_monitor": // tab type spelling, so copied links also resolve + return ; default: return null; } diff --git a/src/types/host-metrics.ts b/src/types/host-metrics.ts new file mode 100644 index 00000000..d604387e --- /dev/null +++ b/src/types/host-metrics.ts @@ -0,0 +1,137 @@ +import type { WidgetType } from "./stats-widgets.js"; + +/** + * Host Metrics layout model. Shared by the frontend tab and the backend + * preferences endpoint. Metric card ids reuse the existing `WidgetType` values + * verbatim (so `enabledWidgets` stays valid for the mobile app); manager card + * ids are new and never enter `statsConfig.enabledWidgets`. + */ + +export type HostMetricCardId = WidgetType; + +export type HostMetricManagerId = + | "service_manager" + | "process_inspector" + | "log_viewer" + | "cron_manager" + | "package_manager" + | "ssl_manager" + | "firewall_manager" + | "user_manager" + | "health_check" + | "disk_breakdown" + | "systemd_timers" + | "top_memory"; + +export type HostMetricsCardId = HostMetricCardId | HostMetricManagerId; + +export type HostMetricsColSpan = 1 | 2 | 3; + +export interface HostMetricsSlot { + id: HostMetricsCardId; + order: number; + colSpan: HostMetricsColSpan; + height: number | null; +} + +export interface HostMetricsLayout { + slots: HostMetricsSlot[]; + columns: number; +} + +/** Canonical order of metric cards (also the canonical `enabledWidgets` order). */ +export const METRIC_CARD_IDS: HostMetricCardId[] = [ + "cpu", + "memory", + "disk", + "network", + "uptime", + "system", + "login_stats", + "processes", + "ports", + "firewall", +]; + +export const MANAGER_CARD_IDS: HostMetricManagerId[] = [ + "service_manager", + "process_inspector", + "log_viewer", + "cron_manager", + "package_manager", + "ssl_manager", + "firewall_manager", + "user_manager", + "health_check", + "disk_breakdown", + "systemd_timers", + "top_memory", +]; + +export function isMetricCardId(id: string): id is HostMetricCardId { + return (METRIC_CARD_IDS as string[]).includes(id); +} + +export function isManagerCardId(id: string): id is HostMetricManagerId { + return (MANAGER_CARD_IDS as string[]).includes(id); +} + +/** + * Derive the flat `enabledWidgets` array (canonical order, deduped, metric ids + * only) from a set of layout slots. This is the single source of truth keeping + * `statsConfig.enabledWidgets` valid for the mobile app. + */ +export function deriveEnabledWidgets( + slots: Array<{ id: string }>, +): WidgetType[] { + const present = new Set(slots.map((s) => s.id)); + return METRIC_CARD_IDS.filter((id) => present.has(id)); +} + +const DEFAULT_COLSPAN: Partial> = + { + cpu: 1, + memory: 1, + disk: 1, + uptime: 1, + system: 1, + network: 1, + processes: 2, + ports: 2, + firewall: 2, + login_stats: 2, + }; + +// All cards default to content/auto height (null); the masonry packs them +// tetris-style and scrollable cards cap their own body height. Users can still +// pin an explicit height by dragging a card's resize handle. +const DEFAULT_HEIGHT: Partial> = {}; + +/** + * Build a default layout from a list of enabled metric-card ids (e.g. the host's + * current `statsConfig.enabledWidgets`), preserving canonical order. Manager + * cards are not added by default. + */ +export function defaultLayoutFromWidgets( + enabledWidgets: string[], + columns = 3, +): HostMetricsLayout { + const enabled = new Set(enabledWidgets); + const ids = METRIC_CARD_IDS.filter((id) => enabled.has(id)); + const slots: HostMetricsSlot[] = ids.map((id, i) => ({ + id, + order: i, + colSpan: DEFAULT_COLSPAN[id] ?? 1, + height: DEFAULT_HEIGHT[id] ?? null, + })); + return { slots, columns }; +} + +export function defaultColSpanFor(id: HostMetricsCardId): HostMetricsColSpan { + return DEFAULT_COLSPAN[id] ?? (isManagerCardId(id) ? 2 : 1); +} + +export function defaultHeightFor(id: HostMetricsCardId): number | null { + if (id in DEFAULT_HEIGHT) return DEFAULT_HEIGHT[id] ?? null; + return null; +} diff --git a/src/types/index.ts b/src/types/index.ts index 313b02aa..a78a6af7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,19 +2,83 @@ import type { Client } from "ssh2"; import type { Request } from "express"; import type { RefObject } from "react"; +// ============================================================================ +// SSO / AUTHENTICATION PROVIDER TYPES +// ============================================================================ + +export type SSOProviderType = "oidc" | "ldap" | "github" | "google"; + +export interface SSOProviderPublic { + id: number; + name: string; + type: SSOProviderType; + displayOrder: number; +} + +export interface SSOProvider extends SSOProviderPublic { + enabled: boolean; + config: string | Record; + createdAt: string; + updatedAt: string; +} + +export interface OIDCProviderConfig { + client_id: string; + client_secret: string; + issuer_url: string; + authorization_url: string; + token_url: string; + userinfo_url?: string; + identifier_path: string; + name_path: string; + scopes: string; + allowed_users?: string; + admin_group?: string; + group_claim?: string; +} + +export interface LDAPProviderConfig { + host: string; + port: number; + useTLS: boolean; + bindDN: string; + bindPassword: string; + userSearchBase: string; + userSearchFilter: string; + usernameAttribute: string; + displayNameAttribute: string; + groupSearchBase?: string; + adminGroup?: string; + allowedUsers?: string; +} + // ============================================================================ // HOST TYPES (SSH, RDP, VNC, Telnet) // ============================================================================ export type ConnectionType = "ssh" | "rdp" | "vnc" | "telnet"; -export type SSHAuthType = "password" | "key" | "credential" | "none" | "opkssh"; +export type SSHAuthType = + | "password" + | "key" + | "credential" + | "none" + | "opkssh" + | "tailscale"; export type GuacamoleAuthType = "password" | "credential"; +export interface ProxmoxConfig { + defaultCredentialId: number | null; + windowsPatterns: string; + dockerPatterns: string; + preferredPrefixes: string; +} + export interface HostFeatureFlags { enableTerminal: boolean; // SSH, Telnet only enableTunnel: boolean; // SSH only enableFileManager: boolean; // SSH only enableDocker: boolean; // SSH only + enableTmuxMonitor: boolean; // SSH only enableRemoteDesktop: boolean; // RDP, VNC only } @@ -36,7 +100,7 @@ export interface Host { folder: string; tags: string[]; pin: boolean; - authType: "password" | "key" | "credential" | "none" | "opkssh"; + authType: "password" | "key" | "credential" | "none" | "opkssh" | "tailscale"; password?: string; key?: string; keyPassword?: string; @@ -52,9 +116,14 @@ export interface Host { overrideCredentialUsername?: boolean; userId?: string; enableTerminal: boolean; + enableSessionLogging: boolean; + enableCommandHistory: boolean; enableTunnel: boolean; enableFileManager: boolean; enableDocker: boolean; + enableProxmox: boolean; + enableTmuxMonitor: boolean; + proxmoxConfig?: ProxmoxConfig | null; showTerminalInSidebar: boolean; showFileManagerInSidebar: boolean; showTunnelInSidebar: boolean; @@ -141,7 +210,7 @@ export interface HostData { folder?: string; tags?: string[]; pin?: boolean; - authType: "password" | "key" | "credential" | "none" | "opkssh"; + authType: "password" | "key" | "credential" | "none" | "opkssh" | "tailscale"; password?: string; key?: File | null; keyPassword?: string; @@ -150,9 +219,14 @@ export interface HostData { credentialId?: number | null; overrideCredentialUsername?: boolean; enableTerminal?: boolean; + enableSessionLogging?: boolean; + enableCommandHistory?: boolean; enableTunnel?: boolean; enableFileManager?: boolean; enableDocker?: boolean; + enableProxmox?: boolean; + enableTmuxMonitor?: boolean; + proxmoxConfig?: ProxmoxConfig | Record | null; showTerminalInSidebar?: boolean; showFileManagerInSidebar?: boolean; showTunnelInSidebar?: boolean; @@ -514,6 +588,15 @@ export interface TerminalConfig { keepaliveInterval?: number; keepaliveCountMax?: number; autoTmux: boolean; + syntaxHighlighting: boolean; + syntaxHighlightingOptions?: { + logLevels: boolean; + paths: boolean; + timestamps: boolean; + ipAddresses: boolean; + urls: boolean; + numbers: boolean; + }; } // ============================================================================ @@ -533,6 +616,7 @@ export interface TabContextTab { | "user_profile" | "docker" | "network_graph" + | "tmux_monitor" // --- tmux-monitor --- | "rdp" | "vnc" | "telnet"; @@ -599,7 +683,13 @@ export type ErrorType = // AUTHENTICATION TYPES // ============================================================================ -export type AuthType = "password" | "key" | "credential" | "none" | "opkssh"; +export type AuthType = + | "password" + | "key" + | "credential" + | "none" + | "opkssh" + | "tailscale"; export type KeyType = "rsa" | "ecdsa" | "ed25519"; diff --git a/src/types/proxmox.ts b/src/types/proxmox.ts new file mode 100644 index 00000000..e2d3549b --- /dev/null +++ b/src/types/proxmox.ts @@ -0,0 +1,16 @@ +export interface ProxmoxGuest { + name: string; + vmid: number; + type: "qemu" | "lxc"; + node: string; + status: string; + ip: string | null; + connectionType: "ssh" | "rdp"; + enableDocker: boolean; +} + +export interface ProxmoxDiscoverResult { + guests: ProxmoxGuest[]; + credentialId: number | null; + defaultCredentialId: number | null; +} diff --git a/src/types/ui-types.ts b/src/types/ui-types.ts index 9baf393c..308d6dbe 100644 --- a/src/types/ui-types.ts +++ b/src/types/ui-types.ts @@ -10,7 +10,7 @@ export type Host = { ram: number | null; lastAccess: string; tags?: string[]; - authType: "password" | "key" | "credential" | "none" | "opkssh"; + authType: "password" | "key" | "credential" | "none" | "opkssh" | "tailscale"; credentialId?: string; overrideCredentialUsername?: boolean; password?: string; @@ -27,6 +27,7 @@ export type Host = { pin?: boolean; enableTerminal: boolean; + enableCommandHistory: boolean; terminalConfig?: { cursorBlink: boolean; cursorStyle: "block" | "underline" | "bar"; @@ -90,6 +91,14 @@ export type Host = { defaultPath?: string; enableDocker: boolean; + enableProxmox: boolean; + enableTmuxMonitor: boolean; + proxmoxConfig?: { + defaultCredentialId: number | null; + windowsPatterns: string; + dockerPatterns: string; + preferredPrefixes: string; + } | null; statsConfig?: { statusCheckEnabled: boolean; @@ -144,6 +153,9 @@ export type Credential = { export type HostFolder = { name: string; children: (Host | HostFolder)[]; + path?: string; + color?: string; + icon?: string; }; export type TabType = @@ -152,14 +164,15 @@ export type TabType = | "rdp" | "vnc" | "telnet" - | "stats" + | "host-metrics" | "files" | "host-manager" | "user-profile" | "admin-settings" | "docker" | "tunnel" - | "network_graph"; + | "network_graph" + | "tmux_monitor"; // --- tmux-monitor --- export type TunnelStatusValue = | "CONNECTED" @@ -258,12 +271,13 @@ export type UserProfileSection = | "api-keys"; export type AdminSection = | "general" - | "oidc" + | "sso" | "users" | "sessions" | "roles" | "database" - | "api-keys"; + | "api-keys" + | "audit-log"; export type AccentColorId = string; export type ThemeId = | "dark" diff --git a/src/ui/AppShell.tsx b/src/ui/AppShell.tsx index 83c48836..53c52f67 100644 --- a/src/ui/AppShell.tsx +++ b/src/ui/AppShell.tsx @@ -18,6 +18,7 @@ import { QuickConnectPanel } from "@/sidebar/QuickConnectPanel"; import { SshToolsPanel } from "@/sidebar/SshToolsPanel"; import { SnippetsPanel } from "@/sidebar/SnippetsPanel"; import { HistoryPanel } from "@/sidebar/HistoryPanel"; +import { SessionLogsPanel } from "@/sidebar/SessionLogsPanel"; import { SplitScreenPanel } from "@/sidebar/SplitScreenPanel"; import { UserProfilePanel } from "@/sidebar/UserProfilePanel"; import { AdminSettingsPanel } from "@/sidebar/AdminSettingsPanel"; @@ -38,6 +39,7 @@ import { applyAccentColor, applyFontSize, PANE_COUNTS } from "@/lib/theme"; import { useTheme } from "@/components/theme-provider"; import { getSSHHosts, + getSSHFolders, getUserInfo, getOpenTabs, addOpenTab, @@ -80,6 +82,9 @@ function sshHostToHost(h: SSHHostWithStatus): Host { enableTunnel: h.enableTunnel ?? false, enableFileManager: h.enableFileManager ?? false, enableDocker: h.enableDocker ?? false, + enableProxmox: h.enableProxmox ?? false, + enableTmuxMonitor: h.enableTmuxMonitor ?? false, // --- tmux-monitor --- + proxmoxConfig: (h.proxmoxConfig as Host["proxmoxConfig"]) ?? null, enableRdp: h.enableRdp ?? h.connectionType === "rdp", enableVnc: h.enableVnc ?? h.connectionType === "vnc", enableTelnet: h.enableTelnet ?? h.connectionType === "telnet", @@ -106,7 +111,10 @@ function sshHostToHost(h: SSHHostWithStatus): Host { }; } -function buildHostTree(hosts: SSHHostWithStatus[]): HostFolder { +function buildHostTree( + hosts: SSHHostWithStatus[], + folderMeta?: Map, +): HostFolder { const root: HostFolder = { name: "root", children: [] }; const folderMap = new Map(); const getOrCreateFolder = (path: string): HostFolder => { @@ -117,7 +125,14 @@ function buildHostTree(hosts: SSHHostWithStatus[]): HostFolder { for (const part of parts) { accumulated = accumulated ? `${accumulated} / ${part}` : part; if (!folderMap.has(accumulated)) { - const folder: HostFolder = { name: part, children: [] }; + const meta = folderMeta?.get(accumulated); + const folder: HostFolder = { + name: part, + path: accumulated, + color: meta?.color, + icon: meta?.icon, + children: [], + }; folderMap.set(accumulated, folder); current.children.push(folder); } @@ -125,6 +140,10 @@ function buildHostTree(hosts: SSHHostWithStatus[]): HostFolder { } return current; }; + // Surface empty folders (created but with no hosts yet) so they stay visible. + if (folderMeta) { + for (const path of folderMeta.keys()) getOrCreateFolder(path); + } for (const h of hosts) { const host = sshHostToHost(h); if (h.folder) { @@ -188,7 +207,7 @@ export function AppShell({ const [profileDropdownOpen, setProfileDropdownOpen] = useState(false); const [sidebarWidth, setSidebarWidth] = useState(() => { const saved = localStorage.getItem("termix_sidebarWidth"); - return saved ? parseInt(saved, 10) : 266; + return saved ? parseInt(saved, 10) : 291; }); const [sidebarDragging, setSidebarDragging] = useState(false); const [sidebarEditing, setSidebarEditing] = useState(false); @@ -274,6 +293,7 @@ export function AppShell({ "ssh-tools": "SSH Tools", snippets: "Snippets", history: "History", + "session-logs": t("nav.sessionLogs"), "split-screen": "Split Screen", connections: t("nav.connections"), "user-profile": "User Profile", @@ -371,15 +391,110 @@ export function AppShell({ getUserPreferences() .then((prefs) => { setUserPrefs(prefs); - if (prefs.theme) setTheme(prefs.theme as ThemeId); - if (prefs.fontSize) applyFontSize(prefs.fontSize as FontSizeId); - if (prefs.accentColor) { - localStorage.setItem("termix-accent", prefs.accentColor); - applyAccentColor(prefs.accentColor); - } - if (prefs.language && prefs.language !== i18n.language) { - localStorage.setItem("i18nextLng", prefs.language); - void i18n.changeLanguage(prefs.language); + if (prefs.storageMode === "cloud") { + // Persist the current browser values before overwriting, so any tab can restore them + if (!localStorage.getItem("termix-local-snapshot")) { + const SNAPSHOT_KEYS = [ + "termix-accent", + "termix-font-size", + "i18nextLng", + "commandAutocomplete", + "commandPaletteShortcutEnabled", + "showHostTags", + "hostTrayOnClick", + "pinAppRail", + "defaultSnippetFoldersCollapsed", + "confirmSnippetExecution", + "disableUpdateCheck", + "confirmTabClose", + "hiddenRailTabs", + ]; + const snap: Record = { + __theme: localStorage.getItem("termix-theme"), + }; + for (const key of SNAPSHOT_KEYS) + snap[key] = localStorage.getItem(key); + localStorage.setItem("termix-local-snapshot", JSON.stringify(snap)); + } + if (prefs.theme) setTheme(prefs.theme as ThemeId); + if (prefs.fontSize) applyFontSize(prefs.fontSize as FontSizeId); + if (prefs.accentColor) { + localStorage.setItem("termix-accent", prefs.accentColor); + applyAccentColor(prefs.accentColor); + } + if (prefs.language && prefs.language !== i18n.language) { + localStorage.setItem("i18nextLng", prefs.language); + void i18n.changeLanguage(prefs.language); + } + if ( + prefs.commandAutocomplete !== null && + prefs.commandAutocomplete !== undefined + ) + localStorage.setItem( + "commandAutocomplete", + String(prefs.commandAutocomplete), + ); + if ( + prefs.commandPaletteEnabled !== null && + prefs.commandPaletteEnabled !== undefined + ) + localStorage.setItem( + "commandPaletteShortcutEnabled", + String(prefs.commandPaletteEnabled), + ); + if (prefs.showHostTags !== null && prefs.showHostTags !== undefined) { + localStorage.setItem("showHostTags", String(prefs.showHostTags)); + window.dispatchEvent(new CustomEvent("showHostTagsChanged")); + } + if ( + prefs.hostTrayOnClick !== null && + prefs.hostTrayOnClick !== undefined + ) + localStorage.setItem( + "hostTrayOnClick", + String(prefs.hostTrayOnClick), + ); + if (prefs.pinAppRail !== null && prefs.pinAppRail !== undefined) + localStorage.setItem("pinAppRail", String(prefs.pinAppRail)); + if ( + prefs.foldersCollapsed !== null && + prefs.foldersCollapsed !== undefined + ) + localStorage.setItem( + "defaultSnippetFoldersCollapsed", + String(prefs.foldersCollapsed), + ); + if ( + prefs.confirmSnippetExecution !== null && + prefs.confirmSnippetExecution !== undefined + ) + localStorage.setItem( + "confirmSnippetExecution", + String(prefs.confirmSnippetExecution), + ); + if ( + prefs.disableUpdateCheck !== null && + prefs.disableUpdateCheck !== undefined + ) + localStorage.setItem( + "disableUpdateCheck", + String(prefs.disableUpdateCheck), + ); + if ( + prefs.confirmTabClose !== null && + prefs.confirmTabClose !== undefined + ) + localStorage.setItem( + "confirmTabClose", + String(prefs.confirmTabClose), + ); + if ( + prefs.hiddenRailTabs !== null && + prefs.hiddenRailTabs !== undefined + ) { + localStorage.setItem("hiddenRailTabs", prefs.hiddenRailTabs); + window.dispatchEvent(new CustomEvent("hiddenRailTabsChanged")); + } } }) .catch(() => {}) @@ -389,10 +504,20 @@ export function AppShell({ // Load real hosts from API const loadHosts = useCallback(async () => { try { - const raw = await getSSHHosts(); + const [raw, folders] = await Promise.all([ + getSSHHosts(), + getSSHFolders().catch(() => []), + ]); const converted = raw.map(sshHostToHost); setAllHosts(converted); - setRealHostTree(buildHostTree(raw)); + const folderMeta = new Map(); + for (const f of folders) { + folderMeta.set(f.name, { + color: f.color ?? undefined, + icon: f.icon ?? undefined, + }); + } + setRealHostTree(buildHostTree(raw, folderMeta)); } catch { // Keep empty state on error } finally { @@ -442,7 +567,7 @@ export function AppShell({ "telnet", "files", "docker", - "stats", + "host-metrics", "tunnel", ]; @@ -637,11 +762,22 @@ export function AppShell({ : host.enableTelnet ? "telnet" : "terminal"); + // --- tmux-monitor --- singleton tab, not a per-host tab + if (type === "tmux_monitor") { + openSingletonTab(type, undefined, host); + return; + } openTab(host, type); } const openSingletonTab = useCallback( - function openSingletonTab(type: TabType, pendingEvent?: string) { + // --- tmux-monitor --- (added optional `host` so tmux_monitor can open + // with a preselected host; existing callers are unaffected) + function openSingletonTab( + type: TabType, + pendingEvent?: string, + host?: Host, + ) { if (type === "host-manager") { if (pendingEvent === "host-manager:add-credential") { setSidebarOpen(true); @@ -677,9 +813,15 @@ export function AppShell({ docker: t("nav.docker"), tunnel: t("nav.tunnels"), network_graph: t("nav.networkGraph"), + tmux_monitor: t("nav.tmuxMonitor"), // --- tmux-monitor --- }; setTabs((prev) => { - if (prev.find((t) => t.id === id)) return prev; + const existing = prev.find((t) => t.id === id); + if (existing) { + // --- tmux-monitor --- refocusing with a host preselects it + if (!host) return prev; + return prev.map((t) => (t.id === id ? { ...t, host } : t)); + } return [ ...prev, { @@ -688,6 +830,7 @@ export function AppShell({ type, label: singletonLabels[type] ?? type, openedAt: Date.now(), + ...(host ? { host } : {}), // --- tmux-monitor --- }, ]; }); @@ -709,6 +852,9 @@ export function AppShell({ function doCloseTab(id: string) { const tabToClose = tabs.find((t) => t.id === id); + if (tabToClose?.terminalRef?.current?.disconnect) { + tabToClose.terminalRef.current.disconnect(); + } if ( tabToClose?.instanceId && PERSISTENT_TAB_TYPES.includes(tabToClose.type) @@ -1051,6 +1197,12 @@ export function AppShell({ )} + {railView === "session-logs" && ( +

+ +
+ )} + {railView === "user-profile" && (
setSidebarWidth(266)} + onClick={() => setSidebarWidth(291)} > @@ -1269,6 +1421,13 @@ export function AppShell({ ].includes(type) ) { openSingletonTab(type, pendingEvent); + } else if (type === "tmux_monitor") { + // --- tmux-monitor --- singleton tab, optionally preselecting a host + openSingletonTab( + type, + undefined, + label ? allHosts.find((h) => h.name === label) : undefined, + ); } else if (label) { const host = allHosts.find((h) => h.name === label); if (host) openTab(host, type); diff --git a/src/ui/api/audit-log-api.ts b/src/ui/api/audit-log-api.ts new file mode 100644 index 00000000..a9d3b88d --- /dev/null +++ b/src/ui/api/audit-log-api.ts @@ -0,0 +1,66 @@ +import { authApi, handleApiError } from "@/main-axios"; + +export interface AuditLog { + id: number; + userId: string; + username: string; + action: string; + resourceType: string; + resourceId: string | null; + resourceName: string | null; + details: string | null; + ipAddress: string | null; + userAgent: string | null; + success: boolean; + errorMessage: string | null; + timestamp: string; +} + +export interface AuditLogFilters { + page?: number; + limit?: number; + userId?: string; + action?: string; + resourceType?: string; + success?: boolean | ""; + startDate?: string; + endDate?: string; +} + +export interface AuditLogResponse { + logs: AuditLog[]; + total: number; + page: number; + totalPages: number; +} + +export async function getAuditLogs( + filters: AuditLogFilters = {}, +): Promise { + try { + const params = new URLSearchParams(); + if (filters.page) params.set("page", String(filters.page)); + if (filters.limit) params.set("limit", String(filters.limit)); + if (filters.userId) params.set("userId", filters.userId); + if (filters.action) params.set("action", filters.action); + if (filters.resourceType) params.set("resourceType", filters.resourceType); + if (filters.success !== undefined && filters.success !== "") + params.set("success", String(filters.success)); + if (filters.startDate) params.set("startDate", filters.startDate); + if (filters.endDate) params.set("endDate", filters.endDate); + + const response = await authApi.get(`/audit-logs?${params.toString()}`); + return response.data; + } catch (error) { + handleApiError(error, "fetch audit logs"); + } +} + +export async function getAuditLogActions(): Promise<{ actions: string[] }> { + try { + const response = await authApi.get("/audit-logs/actions"); + return response.data; + } catch (error) { + handleApiError(error, "fetch audit log actions"); + } +} diff --git a/src/ui/api/credentials-api.ts b/src/ui/api/credentials-api.ts index 90229f4b..02976480 100644 --- a/src/ui/api/credentials-api.ts +++ b/src/ui/api/credentials-api.ts @@ -2,7 +2,9 @@ import { authApi, handleApiError, sshHostApi } from "@/main-axios"; import type { SSHFolder } from "@/types/index"; import { sshLogger } from "@/lib/frontend-logger"; -export async function getCredentials(): Promise> { +export async function getCredentials(): Promise< + Record[] | Record +> { try { const response = await authApi.get("/credentials"); return response.data; diff --git a/src/ui/api/dashboard-preferences-api.ts b/src/ui/api/dashboard-preferences-api.ts deleted file mode 100644 index 40396dca..00000000 --- a/src/ui/api/dashboard-preferences-api.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { dashboardApi } from "@/main-axios"; - -export interface DashboardLayout { - cards: Array<{ - id: string; - enabled: boolean; - order: number; - panel?: "main" | "side"; - height?: number | null; - }>; - mainWidthPct?: number; -} - -export async function getDashboardPreferences(): Promise { - const response = await dashboardApi.get("/dashboard/preferences"); - return response.data; -} - -export async function saveDashboardPreferences( - layout: DashboardLayout, -): Promise<{ success: boolean }> { - const response = await dashboardApi.post("/dashboard/preferences", layout); - return response.data; -} - -// ============================================================================ diff --git a/src/ui/api/host-metrics-api.ts b/src/ui/api/host-metrics-api.ts new file mode 100644 index 00000000..51599f3e --- /dev/null +++ b/src/ui/api/host-metrics-api.ts @@ -0,0 +1,100 @@ +import { handleApiError, statsApi } from "@/main-axios"; +import type { HostMetricsLayout } from "@/types/host-metrics"; + +/** + * Host Metrics layout persistence (server-synced per user/host) + the manager + * card endpoints. All routes live under the `/host-metrics/*` prefix on the + * stats app (port 30005). + */ + +export async function getHostMetricsLayout( + hostId: number, +): Promise { + try { + const res = await statsApi.get(`/host-metrics/preferences/${hostId}`, { + validateStatus: (status) => status === 200 || status === 404, + }); + if (res.status === 404) return null; + return res.data?.layout ?? null; + } catch (error) { + handleApiError(error, "fetch host metrics layout"); + throw error; + } +} + +export async function saveHostMetricsLayout( + hostId: number, + layout: HostMetricsLayout, +): Promise { + try { + await statsApi.post(`/host-metrics/preferences/${hostId}`, layout); + } catch (error) { + handleApiError(error, "save host metrics layout"); + throw error; + } +} + +// ─── Managers ─────────────────────────────────────────────────────────────── + +export interface PlatformInfo { + hasSystemd: boolean; + pkg: "apt" | "dnf" | "yum" | "pacman" | null; + hasCertbot: boolean; + hasAcmeSh: boolean; + hasDocker: boolean; + osPrettyName: string | null; +} + +export async function getHostPlatform(hostId: number): Promise { + const res = await statsApi.get(`/host-metrics/platform/${hostId}`); + return res.data; +} + +/** GET a manager resource (read). */ +export async function managerGet( + hostId: number, + resource: string, + params?: Record, +): Promise { + const res = await statsApi.get( + `/host-metrics/managers/${resource}/${hostId}`, + { params }, + ); + return res.data as T; +} + +/** + * GET a manager sub-resource where the host id sits in the middle of the path, + * e.g. /managers/logs/{id}/files. + */ +export async function managerGetSub( + hostId: number, + resource: string, + sub: string, + params?: Record, +): Promise { + const res = await statsApi.get( + `/host-metrics/managers/${resource}/${hostId}/${sub}`, + { params }, + ); + return res.data as T; +} + +/** + * POST a manager action. `resource` is the manager name; `action` is the + * optional sub-path (e.g. "action", "signal", "renew"). The host id always + * sits between them: /managers/{resource}/{id}[/{action}]. + */ +export async function managerPost( + hostId: number, + resource: string, + body: unknown, + action?: string, +): Promise { + const suffix = action ? `/${action}` : ""; + const res = await statsApi.post( + `/host-metrics/managers/${resource}/${hostId}${suffix}`, + body, + ); + return res.data as T; +} diff --git a/src/ui/api/server-stats-api.ts b/src/ui/api/host-metrics-status-api.ts similarity index 100% rename from src/ui/api/server-stats-api.ts rename to src/ui/api/host-metrics-status-api.ts diff --git a/src/ui/api/open-tabs-api.ts b/src/ui/api/open-tabs-api.ts index 294642af..ad2f62db 100644 --- a/src/ui/api/open-tabs-api.ts +++ b/src/ui/api/open-tabs-api.ts @@ -83,6 +83,17 @@ export interface UserPreferences { fontSize?: string | null; accentColor?: string | null; language?: string | null; + storageMode?: string | null; + commandAutocomplete?: boolean | null; + commandPaletteEnabled?: boolean | null; + showHostTags?: boolean | null; + hostTrayOnClick?: boolean | null; + pinAppRail?: boolean | null; + foldersCollapsed?: boolean | null; + confirmSnippetExecution?: boolean | null; + disableUpdateCheck?: boolean | null; + confirmTabClose?: boolean | null; + hiddenRailTabs?: string | null; } export async function getUserPreferences(): Promise { diff --git a/src/ui/api/session-log-api.ts b/src/ui/api/session-log-api.ts new file mode 100644 index 00000000..c3ffa911 --- /dev/null +++ b/src/ui/api/session-log-api.ts @@ -0,0 +1,42 @@ +import { authApi, handleApiError } from "@/main-axios"; + +export type SessionLogRecord = { + id: number; + hostId: number; + userId: string; + startedAt: string; + endedAt: string | null; + duration: number | null; + recordingPath: string | null; + hostName: string | null; + hostIp: string | null; + sizeBytes: number | null; +}; + +export async function getSessionLogs(): Promise { + try { + const response = await authApi.get("/session_logs/"); + return response.data.logs; + } catch (error) { + throw handleApiError(error, "fetch session logs"); + } +} + +export async function getSessionLogContent(id: number): Promise { + try { + const response = await authApi.get(`/session_logs/${id}/content`, { + responseType: "text", + }); + return response.data; + } catch (error) { + throw handleApiError(error, "fetch session log content"); + } +} + +export async function deleteSessionLog(id: number): Promise { + try { + await authApi.delete(`/session_logs/${id}`); + } catch (error) { + throw handleApiError(error, "delete session log"); + } +} diff --git a/src/ui/api/settings-api.ts b/src/ui/api/settings-api.ts index e3cfea68..79fd12d3 100644 --- a/src/ui/api/settings-api.ts +++ b/src/ui/api/settings-api.ts @@ -70,6 +70,54 @@ export async function updateSessionTimeout( } } +// ============================================================================ +// TAILSCALE SETTINGS +// ============================================================================ + +export async function getTailscaleSettings(): Promise<{ + apiKey: string; + hasApiKey: boolean; +}> { + try { + const response = await authApi.get("/users/tailscale-settings"); + return response.data; + } catch (error) { + handleApiError(error, "fetch Tailscale settings"); + } +} + +export async function updateTailscaleSettings( + apiKey: string, +): Promise<{ hasApiKey: boolean }> { + try { + const response = await authApi.patch("/users/tailscale-settings", { + apiKey, + }); + return response.data; + } catch (error) { + handleApiError(error, "update Tailscale settings"); + } +} + +export async function getTailscaleDevices(): Promise<{ + devices: Array<{ + id: string; + name: string; + hostname: string; + addresses: string[]; + os: string; + lastSeen: string; + }>; + hasApiKey: boolean; +}> { + try { + const response = await authApi.get("/tailscale/devices"); + return response.data; + } catch (error) { + handleApiError(error, "fetch Tailscale devices"); + } +} + // ============================================================================ // GUACAMOLE SETTINGS // ============================================================================ diff --git a/src/ui/api/ssh-host-management-api.ts b/src/ui/api/ssh-host-management-api.ts index 2d70263a..59f9d9b6 100644 --- a/src/ui/api/ssh-host-management-api.ts +++ b/src/ui/api/ssh-host-management-api.ts @@ -1,7 +1,13 @@ import { AxiosError } from "axios"; -import { getAllServerStatuses, handleApiError, sshHostApi } from "@/main-axios"; +import { + authApi, + getAllServerStatuses, + handleApiError, + sshHostApi, +} from "@/main-axios"; import type { SSHHost, SSHHostData, ProxyNode } from "@/types/index"; import type { ServerStatus, SSHHostWithStatus } from "@/main-axios"; +import type { ProxmoxDiscoverResult } from "@/types/proxmox"; // SSH HOST MANAGEMENT // ============================================================================ @@ -101,6 +107,21 @@ export async function bulkImportSSHHosts( } } +export async function discoverProxmoxGuests( + hostId: number, +): Promise { + try { + const response = await authApi.post( + "/proxmox/discover", + { hostId }, + { timeout: 120000 }, + ); + return response.data; + } catch (error) { + handleApiError(error, "discover Proxmox guests"); + } +} + export async function bulkUpdateSSHHosts( hostIds: number[], updates: Record, diff --git a/src/ui/api/sso-provider-api.ts b/src/ui/api/sso-provider-api.ts new file mode 100644 index 00000000..137fe90e --- /dev/null +++ b/src/ui/api/sso-provider-api.ts @@ -0,0 +1,76 @@ +import { authApi, handleApiError } from "@/main-axios"; +import type { SSOProvider, SSOProviderPublic } from "@/types/index"; + +export async function getSSOProviders(): Promise { + try { + const response = await authApi.get("/users/sso-providers"); + return response.data; + } catch (error: unknown) { + console.warn("Failed to fetch SSO providers:", error); + return []; + } +} + +export async function getAdminSSOProviders(): Promise { + try { + const response = await authApi.get("/users/sso-providers/admin"); + return Array.isArray(response.data) ? response.data : []; + } catch (error) { + handleApiError(error, "fetch admin SSO providers"); + return []; + } +} + +export async function createSSOProvider( + data: Omit & { + config: Record; + }, +): Promise { + try { + const response = await authApi.post("/users/sso-providers", data); + return response.data; + } catch (error) { + handleApiError(error, "create SSO provider"); + } +} + +export async function updateSSOProvider( + id: number, + data: Partial> & { + config?: Record; + }, +): Promise { + try { + const response = await authApi.put(`/users/sso-providers/${id}`, data); + return response.data; + } catch (error) { + handleApiError(error, "update SSO provider"); + } +} + +export async function deleteSSOProvider(id: number): Promise { + try { + await authApi.delete(`/users/sso-providers/${id}`); + } catch (error) { + handleApiError(error, "delete SSO provider"); + } +} + +export async function ldapLogin( + providerId: number, + username: string, + password: string, + rememberMe: boolean, +): Promise<{ success: boolean; message: string }> { + try { + const response = await authApi.post("/users/ldap/login", { + providerId, + username, + password, + rememberMe, + }); + return response.data; + } catch (error) { + handleApiError(error, "LDAP login"); + } +} diff --git a/src/ui/api/tmux-monitor-api.ts b/src/ui/api/tmux-monitor-api.ts new file mode 100644 index 00000000..a8fb1568 --- /dev/null +++ b/src/ui/api/tmux-monitor-api.ts @@ -0,0 +1,173 @@ +import { tmuxMonitorApi } from "@/main-axios"; + +export interface TmuxPane { + id: string; + index: number; + pid: number; + active: boolean; + width: number; + height: number; + command: string; + path: string; + title: string; +} + +export interface TmuxWindow { + index: number; + name: string; + active: boolean; + panes: TmuxPane[]; +} + +export interface TmuxSessionOverview { + name: string; + created: number; + lastActivity: number; + attachedClients: number; + windows: TmuxWindow[]; + tags: string[]; +} + +export interface TmuxOverview { + available: boolean; + sessions: TmuxSessionOverview[]; +} + +export interface TmuxSearchMatch { + paneId: string; + sessionName: string; + windowIndex: number; + line: number; + text: string; +} + +export interface TmuxPaneMetrics { + paneId: string; + sessionName: string; + pid: number; + processCount: number; + cpuPercent: number; + memRssKb: number; + gpuMemMb: number; + topCommand: string | null; +} + +export async function getTmuxOverview(hostId: number): Promise { + const response = await tmuxMonitorApi.get(`/${hostId}/overview`); + return response.data; +} + +/** Select a pane's window+pane on the server so the attached terminal + * (and any other attached client) switches to it. */ +export async function focusTmuxPane( + hostId: number, + paneId: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/focus`, { paneId }); +} + +/** Create a new detached session on the host. Starts the tmux server if + * none is running yet. */ +export async function createTmuxSession( + hostId: number, + name: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/sessions`, { name }); +} + +/** Create a new window in an existing session. */ +export async function createTmuxWindow( + hostId: number, + sessionName: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/windows`, { sessionName }); +} + +/** Rename a session. Saved tags follow the session to its new name. */ +export async function renameTmuxSession( + hostId: number, + sessionName: string, + newName: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/rename`, { sessionName, newName }); +} + +/** Kill a session (terminates all of its windows and processes). */ +export async function killTmuxSession( + hostId: number, + sessionName: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/kill`, { sessionName }); +} + +/** Kill a window and every pane in it. */ +export async function killTmuxWindow( + hostId: number, + sessionName: string, + windowIndex: number, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/kill-window`, { + sessionName, + windowIndex, + }); +} + +/** Kill a single pane (last pane of a window closes the window). */ +export async function killTmuxPane( + hostId: number, + paneId: string, +): Promise { + await tmuxMonitorApi.post(`/${hostId}/kill-pane`, { paneId }); +} + +/** Split the window containing a pane: "h" adds a pane to the right, + * "v" below (tmux -h/-v semantics). */ +export async function splitTmuxPane( + hostId: number, + paneId: string, + direction: "h" | "v", +): Promise { + await tmuxMonitorApi.post(`/${hostId}/split`, { paneId, direction }); +} + +export interface TmuxSearchResult { + matches: TmuxSearchMatch[]; + /** True when a search limit was hit and the results are partial. */ + truncated: boolean; + searchedLines: number; + maxPanes: number; +} + +export async function searchTmux( + hostId: number, + query: string, +): Promise { + const response = await tmuxMonitorApi.get(`/${hostId}/search`, { + params: { q: query }, + }); + return { + matches: response.data.matches ?? [], + truncated: response.data.truncated ?? false, + searchedLines: response.data.searchedLines ?? 0, + maxPanes: response.data.maxPanes ?? 0, + }; +} + +export async function getTmuxMetrics( + hostId: number, +): Promise { + const response = await tmuxMonitorApi.get(`/${hostId}/metrics`); + return response.data.panes ?? []; +} + +export async function setTmuxSessionTags( + hostId: number, + sessionName: string, + tags: string[], +): Promise { + const response = await tmuxMonitorApi.put(`/${hostId}/tags`, { + sessionName, + tags, + }); + return response.data.tags ?? []; +} diff --git a/src/ui/api/user-management-api.ts b/src/ui/api/user-management-api.ts index 0ea07274..ef7d7c5e 100644 --- a/src/ui/api/user-management-api.ts +++ b/src/ui/api/user-management-api.ts @@ -231,6 +231,30 @@ export async function updatePasswordResetAllowed( } } +export async function getCommandHistoryEnabled(): Promise<{ + enabled: boolean; +}> { + try { + const response = await authApi.get("/users/command-history-enabled"); + return response.data; + } catch (error) { + handleApiError(error, "get command history enabled"); + } +} + +export async function updateCommandHistoryEnabled( + enabled: boolean, +): Promise<{ enabled: boolean }> { + try { + const response = await authApi.patch("/users/command-history-enabled", { + enabled, + }); + return response.data; + } catch (error) { + handleApiError(error, "update command history enabled"); + } +} + export async function updateOIDCConfig( config: Record, ): Promise> { diff --git a/src/ui/auth/Auth.tsx b/src/ui/auth/Auth.tsx index 3c1408a8..36233d4a 100644 --- a/src/ui/auth/Auth.tsx +++ b/src/ui/auth/Auth.tsx @@ -12,6 +12,8 @@ import { ArrowLeft, Shield, CheckCircle2, + ChevronDown, + ChevronUp, } from "lucide-react"; import { useTranslation } from "react-i18next"; import { @@ -21,7 +23,6 @@ import { getRegistrationAllowed, getPasswordLoginAllowed, getPasswordResetAllowed, - getOIDCConfig, getSetupRequired, initiatePasswordReset, verifyPasswordResetCode, @@ -34,6 +35,8 @@ import { getEmbeddedServerStatus, getCurrentToken, } from "@/main-axios"; +import { getSSOProviders, ldapLogin } from "@/api/sso-provider-api"; +import type { SSOProviderPublic } from "@/types/index"; import { ElectronServerConfig as ServerConfigComponent } from "@/auth/ElectronServerConfig"; import { ElectronLoginForm } from "@/auth/ElectronLoginForm"; import { Checkbox } from "@/components/checkbox"; @@ -209,7 +212,9 @@ export function Auth({ onLogin }: AuthProps) { const { t } = useTranslation(); const [view, setView] = useState("login"); const [loading, setLoading] = useState(false); - const [oidcLoading, setOidcLoading] = useState(false); + const [providerLoading, setProviderLoading] = useState< + Record + >({}); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); @@ -245,8 +250,11 @@ export function Auth({ onLogin }: AuthProps) { const [registrationAllowed, setRegistrationAllowed] = useState(true); const [passwordLoginAllowed, setPasswordLoginAllowed] = useState(true); const [passwordResetAllowed, setPasswordResetAllowed] = useState(true); - const [oidcConfigured, setOidcConfigured] = useState(false); - const [oidcConfigLoaded, setOidcConfigLoaded] = useState(false); + const [ssoProviders, setSsoProviders] = useState([]); + const [ssoProvidersLoaded, setSsoProvidersLoaded] = useState(false); + const [expandedLdapId, setExpandedLdapId] = useState(null); + const [ldapUsername, setLdapUsername] = useState(""); + const [ldapPassword, setLdapPassword] = useState(""); const silentSigninHandledRef = useRef(false); const [firstUser, setFirstUser] = useState(false); const [dbConnectionFailed, setDbConnectionFailed] = useState(false); @@ -276,10 +284,10 @@ export function Auth({ onLogin }: AuthProps) { getPasswordResetAllowed() .then((allowed) => setPasswordResetAllowed(allowed)) .catch(() => setPasswordResetAllowed(false)); - getOIDCConfig() - .then((res) => setOidcConfigured(!!res)) - .catch(() => setOidcConfigured(false)) - .finally(() => setOidcConfigLoaded(true)); + getSSOProviders() + .then((providers) => setSsoProviders(providers || [])) + .catch(() => setSsoProviders([])) + .finally(() => setSsoProvidersLoaded(true)); }, []); useEffect(() => { @@ -335,6 +343,18 @@ export function Auth({ onLogin }: AuthProps) { if (view === "totp" && totpInputRef.current) totpInputRef.current.focus(); }, [view]); + useEffect(() => { + setLdapUsername(""); + setLdapPassword(""); + }, [expandedLdapId]); + + useEffect(() => { + if (!ssoProvidersLoaded) return; + if (!passwordLoginAllowed && ssoProviders.length > 0 && view === "login") { + setView("external"); + } + }, [ssoProvidersLoaded, passwordLoginAllowed, ssoProviders.length, view]); + useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const success = urlParams.get("success"); @@ -707,65 +727,113 @@ export function Auth({ onLogin }: AuthProps) { } } - const handleOIDCLogin = useCallback(async () => { - setOidcLoading(true); - try { - if (isElectron()) { - const electronAPI = ( - window as unknown as { - electronAPI?: { - oidcSystemBrowserAuth?: ( - authUrl: string, - port: number, - ) => Promise<{ - success: boolean; - token?: string; - error?: string; - }>; - }; + const handleOIDCLogin = useCallback( + async (providerId?: number) => { + const loadingKey = providerId ?? -1; + setProviderLoading((prev) => ({ ...prev, [loadingKey]: true })); + try { + if (isElectron()) { + const electronAPI = ( + window as unknown as { + electronAPI?: { + oidcSystemBrowserAuth?: ( + authUrl: string, + port: number, + ) => Promise<{ + success: boolean; + token?: string; + error?: string; + }>; + }; + } + ).electronAPI; + if (electronAPI?.oidcSystemBrowserAuth) { + const callbackPort = 17832 + Math.floor(Math.random() * 100); + const authResponse = await getOIDCAuthorizeUrl( + rememberMe, + callbackPort, + providerId, + ); + const { auth_url: authUrl } = authResponse; + if (!authUrl) throw new Error(t("errors.invalidAuthUrl")); + const result = await electronAPI.oidcSystemBrowserAuth( + authUrl, + callbackPort, + ); + if (result.success && result.token) { + localStorage.setItem("jwt", result.token); + window.location.reload(); + return; + } + throw new Error(result.error || "Authentication failed"); } - ).electronAPI; - if (electronAPI?.oidcSystemBrowserAuth) { - const callbackPort = 17832 + Math.floor(Math.random() * 100); - const authResponse = await getOIDCAuthorizeUrl( - rememberMe, - callbackPort, - ); - const { auth_url: authUrl } = authResponse; - if (!authUrl) throw new Error(t("errors.invalidAuthUrl")); - const result = await electronAPI.oidcSystemBrowserAuth( - authUrl, - callbackPort, - ); - if (result.success && result.token) { - localStorage.setItem("jwt", result.token); - window.location.reload(); - return; - } - throw new Error(result.error || "Authentication failed"); } + const authResponse = await getOIDCAuthorizeUrl( + rememberMe, + undefined, + providerId, + ); + const { auth_url: authUrl } = authResponse; + if (!authUrl || authUrl === "undefined") + throw new Error(t("errors.invalidAuthUrl")); + window.location.replace(authUrl); + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; + toast.error( + error?.response?.data?.error || + error?.message || + t("errors.failedOidcLogin"), + ); + setProviderLoading((prev) => ({ ...prev, [loadingKey]: false })); } - const authResponse = await getOIDCAuthorizeUrl(rememberMe); - const { auth_url: authUrl } = authResponse; - if (!authUrl || authUrl === "undefined") - throw new Error(t("errors.invalidAuthUrl")); - window.location.replace(authUrl); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { data?: { error?: string } }; - }; - toast.error( - error?.response?.data?.error || - error?.message || - t("errors.failedOidcLogin"), - ); - setOidcLoading(false); - } - }, [rememberMe, t]); + }, + [rememberMe, t], + ); + + const handleLDAPLogin = useCallback( + async (providerId: number) => { + if (!ldapUsername.trim() || !ldapPassword) { + toast.error(t("errors.requiredField")); + return; + } + setProviderLoading((prev) => ({ ...prev, [providerId]: true })); + try { + await ldapLogin( + providerId, + ldapUsername.trim(), + ldapPassword, + rememberMe, + ); + const meRes = await getUserInfo(); + storeAuth(meRes.username || ""); + toast.success(t("messages.loginSuccess")); + onLogin( + meRes.username || "", + meRes.userId || undefined, + !!meRes.is_admin, + ); + } catch (err: unknown) { + const error = err as { + response?: { data?: { error?: string } }; + message?: string; + }; + toast.error( + error?.response?.data?.error || + error?.message || + t("auth.ldapLoginFailed"), + ); + } finally { + setProviderLoading((prev) => ({ ...prev, [providerId]: false })); + } + }, + [ldapUsername, ldapPassword, rememberMe, onLogin, t], + ); useEffect(() => { - if (!oidcConfigLoaded || silentSigninHandledRef.current) return; + if (!ssoProvidersLoaded || silentSigninHandledRef.current) return; if (!shouldTriggerSilentSignin(window.location.search)) return; const nextSearch = removeSilentSigninFromSearch(window.location.search); @@ -776,13 +844,17 @@ export function Auth({ onLogin }: AuthProps) { ); silentSigninHandledRef.current = true; - if (oidcConfigured && !isElectron()) { - handleOIDCLogin(); + + const oidcProvider = ssoProviders.find( + (p) => p.type === "oidc" || p.type === "github" || p.type === "google", + ); + if (oidcProvider && !isElectron()) { + handleOIDCLogin(oidcProvider.id); return; } toast.info(t("errors.silentSigninOidcUnavailable")); - }, [handleOIDCLogin, oidcConfigLoaded, oidcConfigured, t]); + }, [handleOIDCLogin, ssoProvidersLoaded, ssoProviders, t]); // Electron server config / webview auth success screens if (isElectron() && !isInElectronWebView()) { @@ -908,7 +980,11 @@ export function Auth({ onLogin }: AuthProps) { label: t("common.register"), show: (passwordLoginAllowed || firstUser) && registrationAllowed, }, - { id: "external", label: t("auth.external"), show: oidcConfigured }, + { + id: "external", + label: t("auth.external"), + show: ssoProviders.length > 0, + }, ]; return ( @@ -1182,36 +1258,115 @@ export function Auth({ onLogin }: AuthProps) { {view === "external" && (
- {isElectron() ? ( -

- {t("auth.externalNotSupportedInElectron")} -

- ) : ( - <> -
- setRememberMe(v === true)} - /> -
+ +
+ {ssoProviders.map((provider) => { + const isLoading = !!providerLoading[provider.id]; + + if (provider.type === "ldap") { + const isExpanded = expandedLdapId === provider.id; + return ( +
+ + {isExpanded && ( +
{ + e.preventDefault(); + handleLDAPLogin(provider.id); + }} + className="flex flex-col gap-3 p-3 border-t border-border" + > + + + setLdapUsername(e.target.value) + } + disabled={isLoading} + autoFocus + /> + + + + + +
+ )} +
+ ); + } + + return ( +
- - - )} + {isLoading + ? t("common.loading") + : t("auth.loginWithProvider", { + name: provider.name, + })} + + ); + })} +
)} diff --git a/src/ui/auth/LoginPage.tsx b/src/ui/auth/LoginPage.tsx index a36d1a0e..34e6faa6 100644 --- a/src/ui/auth/LoginPage.tsx +++ b/src/ui/auth/LoginPage.tsx @@ -18,7 +18,6 @@ import { getUserInfo, getRegistrationAllowed, getPasswordLoginAllowed, - getOIDCConfig, getSetupRequired, initiatePasswordReset, verifyPasswordResetCode, @@ -31,6 +30,8 @@ import { getEmbeddedServerStatus, getCurrentToken, } from "@/main-axios"; +import { getSSOProviders, ldapLogin } from "@/api/sso-provider-api"; +import type { SSOProviderPublic } from "@/types/index"; import { ElectronServerConfig as ServerConfigComponent } from "@/auth/ElectronServerConfig.tsx"; import { ElectronLoginForm } from "@/auth/ElectronLoginForm.tsx"; import { @@ -102,9 +103,7 @@ export function Auth({ return false; }, []); - const [tab, setTab] = useState<"login" | "signup" | "external" | "reset">( - "login", - ); + const [tab, setTab] = useState<"login" | "signup" | "reset">("login"); const [localUsername, setLocalUsername] = useState(""); const [password, setPassword] = useState(""); const [signupConfirmPassword, setSignupConfirmPassword] = useState(""); @@ -123,8 +122,12 @@ export function Auth({ const [firstUserToastShown, setFirstUserToastShown] = useState(false); const [registrationAllowed, setRegistrationAllowed] = useState(true); const [passwordLoginAllowed, setPasswordLoginAllowed] = useState(true); - const [oidcConfigured, setOidcConfigured] = useState(false); - const [oidcConfigLoaded, setOidcConfigLoaded] = useState(false); + const [ssoProviders, setSsoProviders] = useState([]); + const [ssoProvidersLoaded, setSsoProvidersLoaded] = useState(false); + const [ldapProviderId, setLdapProviderId] = useState(null); + const [ldapUsername, setLdapUsername] = useState(""); + const [ldapPassword, setLdapPassword] = useState(""); + const [ldapLoading, setLdapLoading] = useState(false); const silentSigninHandledRef = useRef(false); const [resetStep, setResetStep] = useState< @@ -243,23 +246,15 @@ export function Auth({ }, []); useEffect(() => { - getOIDCConfig() - .then((response) => { - if (response) { - setOidcConfigured(true); - } else { - setOidcConfigured(false); - } + getSSOProviders() + .then((providers) => { + setSsoProviders(providers || []); }) - .catch((error) => { - if (error.response?.status === 404) { - setOidcConfigured(false); - } else { - setOidcConfigured(false); - } + .catch(() => { + setSsoProviders([]); }) .finally(() => { - setOidcConfigLoaded(true); + setSsoProvidersLoaded(true); }); }, []); @@ -292,11 +287,8 @@ export function Auth({ }); }, [setDbError, firstUserToastShown, showServerConfig, t]); - useEffect(() => { - if (!passwordLoginAllowed && oidcConfigured && tab !== "external") { - setTab("external"); - } - }, [passwordLoginAllowed, oidcConfigured, tab]); + // When password login is disabled and SSO is available, stay on login tab + // (SSO buttons appear below the form regardless of tab) async function handleSubmit(e: React.FormEvent) { e.preventDefault(); @@ -650,33 +642,68 @@ export function Auth({ } } - const handleOIDCLogin = useCallback(async () => { - setOidcLoading(true); - try { - const authResponse = await getOIDCAuthorizeUrl(rememberMe); - const { auth_url: authUrl } = authResponse; + const handleOIDCLogin = useCallback( + async (providerId?: number) => { + setOidcLoading(true); + try { + const authResponse = await getOIDCAuthorizeUrl( + rememberMe, + undefined, + providerId, + ); + const { auth_url: authUrl } = authResponse; - if (!authUrl || authUrl === "undefined") { - throw new Error(t("errors.invalidAuthUrl")); + if (!authUrl || authUrl === "undefined") { + throw new Error(t("errors.invalidAuthUrl")); + } + + window.location.replace(authUrl); + } catch (err: unknown) { + const error = err as { + message?: string; + response?: { data?: { error?: string } }; + }; + const errorMessage = + error?.response?.data?.error || + error?.message || + t("errors.failedOidcLogin"); + toast.error(errorMessage); + setOidcLoading(false); } + }, + [rememberMe, t], + ); - window.location.replace(authUrl); - } catch (err: unknown) { - const error = err as { - message?: string; - response?: { data?: { error?: string } }; - }; - const errorMessage = - error?.response?.data?.error || - error?.message || - t("errors.failedOidcLogin"); - toast.error(errorMessage); - setOidcLoading(false); - } - }, [rememberMe, t]); + const handleLDAPLogin = useCallback( + async (providerId: number) => { + if (!ldapUsername.trim() || !ldapPassword) { + toast.error(t("errors.requiredField")); + return; + } + setLdapLoading(true); + try { + await ldapLogin(providerId, ldapUsername, ldapPassword, rememberMe); + const userInfo = await getUserInfo(); + onLogin(userInfo); + } catch (err: unknown) { + const error = err as { + response?: { data?: { error?: string } }; + message?: string; + }; + toast.error( + error?.response?.data?.error || + error?.message || + t("auth.ldapLoginFailed"), + ); + } finally { + setLdapLoading(false); + } + }, + [ldapUsername, ldapPassword, rememberMe, onLogin, t], + ); useEffect(() => { - if (!oidcConfigLoaded || silentSigninHandledRef.current) return; + if (!ssoProvidersLoaded || silentSigninHandledRef.current) return; if (!shouldTriggerSilentSignin(window.location.search)) return; const nextSearch = removeSilentSigninFromSearch(window.location.search); @@ -687,13 +714,22 @@ export function Auth({ ); silentSigninHandledRef.current = true; - if (oidcConfigured && !isElectron()) { - handleOIDCLogin(); + const oidcProvider = ssoProviders.find( + (p) => p.type === "oidc" || p.type === "github" || p.type === "google", + ); + if (oidcProvider && !isElectron()) { + handleOIDCLogin(oidcProvider.id); + return; + } + + if (ssoProviders.length > 0 && !isElectron()) { + const first = ssoProviders[0]; + if (first.type !== "ldap") handleOIDCLogin(first.id); return; } toast.info(t("errors.silentSigninOidcUnavailable")); - }, [handleOIDCLogin, oidcConfigLoaded, oidcConfigured, t]); + }, [handleOIDCLogin, ssoProvidersLoaded, ssoProviders, t]); useEffect(() => { const urlParams = new URLSearchParams(window.location.search); @@ -1233,8 +1269,8 @@ export function Auth({ const hasSignup = (passwordLoginAllowed || firstUser) && registrationAllowed; - const hasOIDC = oidcConfigured; - const hasAnyAuth = hasLogin || hasSignup || hasOIDC; + const hasSso = ssoProviders.length > 0; + const hasAnyAuth = hasLogin || hasSignup || hasSso; if (!hasAnyAuth) { return ( @@ -1253,12 +1289,8 @@ export function Auth({ <> { - const newTab = value as - | "login" - | "signup" - | "external" - | "reset"; + onValueChange={(v) => { + const newTab = v as "login" | "signup" | "reset"; setTab(newTab); if (tab === "reset") resetPasswordState(); if ( @@ -1290,15 +1322,6 @@ export function Auth({ {t("common.register")} )} - {oidcConfigured && ( - - {t("auth.external")} - - )} @@ -1308,241 +1331,182 @@ export function Auth({ ? t("auth.loginTitle") : tab === "signup" ? t("auth.registerTitle") - : tab === "external" - ? t("auth.loginWithExternal") - : t("auth.forgotPassword")} + : t("auth.forgotPassword")}

- {tab === "external" || tab === "reset" ? ( + {tab === "reset" ? (
- {tab === "external" && ( + {resetStep === "initiate" && ( <> + + {t("common.warning")} + + {t("auth.dataLossWarning")} + +
-

{t("auth.loginWithExternalDesc")}

+

{t("auth.resetCodeDesc")}

+
+
+
+ + + setLocalUsername(e.target.value) + } + disabled={resetLoading} + /> +
+
- {(() => { - if (isElectron()) { - return ( -
-

- {t( - "auth.externalNotSupportedInElectron", - )} -

-
- ); - } else { - return ( - <> -
- - setRememberMe(checked === true) - } - /> - -
- - - ); - } - })()} )} - {tab === "reset" && ( + + {resetStep === "verify" && ( <> - {resetStep === "initiate" && ( - <> - - - {t("common.warning")} - - - {t("auth.dataLossWarning")} - - -
-

{t("auth.resetCodeDesc")}

-
-
-
- - - setLocalUsername(e.target.value) - } - disabled={resetLoading} - /> -
- -
- - )} +
+

+ {t("auth.enterResetCode")}{" "} + {localUsername} +

+
+
+
+ + + setResetCode( + e.target.value.replace(/\D/g, ""), + ) + } + disabled={resetLoading} + placeholder="000000" + /> +
+ + +
+ + )} - {resetStep === "verify" && ( - <> -
-

- {t("auth.enterResetCode")}{" "} - {localUsername} -

-
-
-
- - - setResetCode( - e.target.value.replace(/\D/g, ""), - ) - } - disabled={resetLoading} - placeholder="000000" - /> -
- - -
- - )} - - {resetStep === "newPassword" && - !resetSuccess && ( - <> -
-

- {t("auth.enterNewPassword")}{" "} - {localUsername} -

-
-
-
- - - setNewPassword(e.target.value) - } - disabled={resetLoading} - autoComplete="new-password" - /> -
-
- - - setConfirmPassword(e.target.value) - } - disabled={resetLoading} - autoComplete="new-password" - /> -
- - -
- - )} + {resetStep === "newPassword" && !resetSuccess && ( + <> +
+

+ {t("auth.enterNewPassword")}{" "} + {localUsername} +

+
+
+
+ + + setNewPassword(e.target.value) + } + disabled={resetLoading} + autoComplete="new-password" + /> +
+
+ + + setConfirmPassword(e.target.value) + } + disabled={resetLoading} + autoComplete="new-password" + /> +
+ + +
)}
@@ -1641,6 +1605,113 @@ export function Auth({ {t("auth.resetPasswordButton")} )} + + {ssoProviders.length > 0 && !isElectron() && ( +
+
+
+ + {t("auth.orContinueWith")} + +
+
+ {ssoProviders.map((provider) => { + if (provider.type === "ldap") { + const isExpanded = + ldapProviderId === provider.id; + return ( +
+ + {isExpanded && ( +
+ + setLdapUsername(e.target.value) + } + className="h-9 text-sm" + disabled={ldapLoading} + /> + + setLdapPassword(e.target.value) + } + className="h-9 text-sm" + disabled={ldapLoading} + onKeyDown={(e) => { + if (e.key === "Enter") + handleLDAPLogin(provider.id); + }} + /> + +
+ )} +
+ ); + } + return ( + + ); + })} +
+ )} )} diff --git a/src/ui/auth/LoginScreen.tsx b/src/ui/auth/LoginScreen.tsx new file mode 100644 index 00000000..9fcf715c --- /dev/null +++ b/src/ui/auth/LoginScreen.tsx @@ -0,0 +1,37 @@ +import React, { useState } from "react"; +import { Auth } from "@/auth/LoginPage.tsx"; + +interface LoginScreenProps { + authLoading: boolean; + onAuthSuccess: (authData: { + isAdmin: boolean; + username: string | null; + userId: string | null; + }) => void; +} + +export function LoginScreen({ + authLoading, + onAuthSuccess, +}: LoginScreenProps): React.ReactElement { + const [loggedIn, setLoggedIn] = useState(false); + const [, setIsAdmin] = useState(false); + const [, setUsername] = useState(null); + const [, setUserId] = useState(null); + const [, setDbError] = useState(null); + + return ( +
+ +
+ ); +} diff --git a/src/ui/components/card-grid/CardGridCanvas.tsx b/src/ui/components/card-grid/CardGridCanvas.tsx new file mode 100644 index 00000000..8d7690b3 --- /dev/null +++ b/src/ui/components/card-grid/CardGridCanvas.tsx @@ -0,0 +1,503 @@ +import { + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, + type ReactNode, + type DragEvent, + type MouseEvent as ReactMouseEvent, +} from "react"; +import { useTranslation } from "react-i18next"; +import { useIsMobile } from "@/hooks/use-mobile"; +import { cn } from "@/lib/utils"; +import { + GripVertical, + Trash2, + Plus, + Columns2, + StretchHorizontal, +} from "lucide-react"; +import type { GridSlot, GridCardCatalogEntry } from "./types"; +import { + MAX_COLUMNS, + MIN_COLUMNS, + clampColumns, + nextColSpan, + sortSlots, + reorderSlots, + addSlot, + removeSlot, + setColSpan, + setHeight, + heightToRowSpan, +} from "./layout-utils"; + +type DragState = { id: string; order: number } | null; + +/** + * Masonry is implemented with the CSS-grid row-span trick: the grid has many + * tiny rows (`ROW_UNIT`px each) and every tile spans `ceil(height / ROW_UNIT)` + * rows. With `grid-auto-flow: row dense` the browser packs short tiles under + * tall ones (tetris) while still honouring multi-column spans natively. + */ +const ROW_UNIT = 8; +const ROW_GAP = 12; + +function DropZone({ + order, + active, + onDropAt, + onDragOver, +}: { + order: number; + active: boolean; + onDropAt: (order: number) => void; + onDragOver: (e: DragEvent) => void; +}) { + const [over, setOver] = useState(false); + if (!active) return null; + return ( +
{ + onDragOver(e); + setOver(true); + }} + onDragLeave={() => setOver(false)} + onDrop={() => { + setOver(false); + onDropAt(order); + }} + /> + ); +} + +function TileChrome({ + columns, + label, + onRemove, + onCycleWidth, + onResizeStart, +}: { + columns: number; + label: string; + onRemove: () => void; + onCycleWidth: () => void; + onResizeStart: (e: ReactMouseEvent) => void; +}) { + const { t } = useTranslation(); + return ( + <> +
+
+
+ +
+ {columns > 1 && ( + + )} + +
+
+
+
+ + ); +} + +function AddTray({ + catalog, + presentIds, + onAdd, +}: { + catalog: GridCardCatalogEntry[]; + presentIds: Set; + onAdd: (entry: GridCardCatalogEntry) => void; +}) { + const { t } = useTranslation(); + const available = catalog.filter((c) => !presentIds.has(c.id)); + if (available.length === 0) return null; + return ( +
+ + {t("cardGrid.addCard")} + + {available.map((card) => ( + + ))} +
+ ); +} + +/** + * One masonry tile. Measures its natural (or overridden) height and reports the + * number of grid rows it should span so the parent grid can pack densely. + */ +function MasonryTile({ + slot, + span, + rows, + editMode, + isDragging, + effectiveColumns, + label, + measureRef, + children, + onReportRows, + onDragStart, + onDragEnd, + onDragOver, + onDrop, + onRemove, + onCycleWidth, + onResizeStart, +}: { + slot: GridSlot; + span: number; + rows: number; + editMode: boolean; + isDragging: boolean; + effectiveColumns: number; + label: string; + measureRef: (el: HTMLDivElement | null) => void; + children: ReactNode; + onReportRows: (rows: number) => void; + onDragStart: () => void; + onDragEnd: () => void; + onDragOver: (e: DragEvent) => void; + onDrop: () => void; + onRemove: () => void; + onCycleWidth: () => void; + onResizeStart: (e: ReactMouseEvent) => void; +}) { + const innerRef = useRef(null); + const fixedHeight = slot.height ?? undefined; + + // Report the span (in row units) from the measured content height. When an + // explicit height override is set we derive rows from it directly. + useLayoutEffect(() => { + const el = innerRef.current; + if (!el) return; + const report = () => { + const h = fixedHeight ?? el.getBoundingClientRect().height; + onReportRows(heightToRowSpan(h, ROW_UNIT, ROW_GAP)); + }; + report(); + if (fixedHeight != null) return; + const ro = new ResizeObserver(report); + ro.observe(el); + return () => ro.disconnect(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fixedHeight, span, effectiveColumns]); + + return ( +
+
*]:h-full", + )} + style={fixedHeight != null ? { height: fixedHeight } : undefined} + > + {children} +
+ {editMode && ( + + )} +
+ ); +} + +interface CardGridCanvasProps { + slots: GridSlot[]; + columns: number; + editMode: boolean; + /** Render the card body for a given slot id. */ + renderCard: (id: string) => ReactNode; + /** Cards available to add in edit mode (already-present ones are filtered out). */ + cardCatalog: GridCardCatalogEntry[]; + onChange: (slots: GridSlot[], columns: number) => void; + /** Human label lookup for a card id (used by remove/add chrome). */ + cardLabel?: (id: string) => string; + className?: string; +} + +export function CardGridCanvas({ + slots, + columns, + editMode, + renderCard, + cardCatalog, + onChange, + cardLabel, + className, +}: CardGridCanvasProps) { + const { t } = useTranslation(); + const isMobile = useIsMobile(); + const [dragState, setDragState] = useState(null); + const [rowSpans, setRowSpans] = useState>({}); + const tileRefs = useRef>(new Map()); + + const sorted = sortSlots(slots); + const presentIds = new Set(slots.map((s) => s.id)); + const labelFor = useCallback( + (id: string) => cardLabel?.(id) ?? id, + [cardLabel], + ); + + // Drop row-span entries for cards that are no longer present. + useEffect(() => { + setRowSpans((prev) => { + const next: Record = {}; + let changed = false; + for (const s of slots) { + if (prev[s.id] != null) next[s.id] = prev[s.id]; + else changed = true; + } + if (Object.keys(next).length !== Object.keys(prev).length) changed = true; + return changed ? next : prev; + }); + }, [slots]); + + const reportRows = useCallback((id: string, rows: number) => { + setRowSpans((prev) => (prev[id] === rows ? prev : { ...prev, [id]: rows })); + }, []); + + const commitSlots = useCallback( + (next: GridSlot[]) => onChange(next, columns), + [onChange, columns], + ); + + const handleDropAt = useCallback( + (targetOrder: number) => { + if (!dragState) return; + commitSlots(reorderSlots(slots, dragState.id, targetOrder)); + setDragState(null); + }, + [dragState, slots, commitSlots], + ); + + const handleRemove = useCallback( + (id: string) => commitSlots(removeSlot(slots, id)), + [slots, commitSlots], + ); + + const handleAdd = useCallback( + (entry: GridCardCatalogEntry) => + commitSlots(addSlot(slots, entry, columns)), + [slots, columns, commitSlots], + ); + + const handleCycleWidth = useCallback( + (id: string) => { + const slot = slots.find((s) => s.id === id); + if (!slot) return; + commitSlots(setColSpan(slots, id, nextColSpan(slot.colSpan, columns))); + }, + [slots, columns, commitSlots], + ); + + const handleResizeStart = useCallback( + (id: string, e: ReactMouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + const startY = e.clientY; + const startH = + tileRefs.current.get(id)?.getBoundingClientRect().height ?? 120; + const onMove = (ev: globalThis.MouseEvent) => { + onChange(setHeight(slots, id, startH + (ev.clientY - startY)), columns); + }; + const onUp = () => { + window.removeEventListener("mousemove", onMove); + window.removeEventListener("mouseup", onUp); + }; + window.addEventListener("mousemove", onMove); + window.addEventListener("mouseup", onUp); + }, + [slots, columns, onChange], + ); + + const handleDragOver = (e: DragEvent) => e.preventDefault(); + + // Mobile: simple single-column stack, no DnD / resize / edit chrome. + if (isMobile) { + return ( +
+ {sorted.map((slot) => ( +
+ {renderCard(slot.id)} +
+ ))} +
+ ); + } + + const effectiveColumns = clampColumns(columns); + + return ( +
+
+ {sorted.map((slot) => { + const span = Math.min(slot.colSpan, effectiveColumns); + const isDragging = dragState?.id === slot.id; + const rows = rowSpans[slot.id] ?? 24; + return ( + { + tileRefs.current.set(slot.id, el); + }} + onReportRows={(r) => reportRows(slot.id, r)} + onDragStart={() => + setDragState({ id: slot.id, order: slot.order }) + } + onDragEnd={() => setDragState(null)} + onDragOver={handleDragOver} + onDrop={() => handleDropAt(slot.order)} + onRemove={() => handleRemove(slot.id)} + onCycleWidth={() => handleCycleWidth(slot.id)} + onResizeStart={(e) => handleResizeStart(slot.id, e)} + > + {renderCard(slot.id)} + + ); + })} +
+ + {editMode && ( + <> + {sorted.length === 0 && ( +
+ {t("cardGrid.empty")} +
+ )} + + + )} + + {/* Drop target covering the end of the grid while dragging. */} + {editMode && ( + + )} +
+ ); +} + +/** Small stepper to change the masonry column count, for an edit toolbar. */ +export function ColumnCountStepper({ + columns, + onChange, +}: { + columns: number; + onChange: (columns: number) => void; +}) { + const { t } = useTranslation(); + return ( +
+ + + {t("cardGrid.columns")} + +
+ + + {columns} + + +
+
+ ); +} diff --git a/src/ui/components/card-grid/layout-utils.test.ts b/src/ui/components/card-grid/layout-utils.test.ts new file mode 100644 index 00000000..f00403b1 --- /dev/null +++ b/src/ui/components/card-grid/layout-utils.test.ts @@ -0,0 +1,131 @@ +import { describe, it, expect } from "vitest"; +import { + clampColumns, + nextColSpan, + sortSlots, + reorderSlots, + addSlot, + removeSlot, + setColSpan, + setHeight, + heightToRowSpan, + MIN_TILE_HEIGHT, +} from "./layout-utils"; +import type { GridSlot } from "./types"; + +function slot(id: string, order: number, colSpan: 1 | 2 | 3 = 1): GridSlot { + return { id, order, colSpan, height: null }; +} + +describe("clampColumns", () => { + it("clamps into 1..4 and rounds", () => { + expect(clampColumns(0)).toBe(1); + expect(clampColumns(1)).toBe(1); + expect(clampColumns(4)).toBe(4); + expect(clampColumns(9)).toBe(4); + expect(clampColumns(2.6)).toBe(3); + expect(clampColumns(NaN)).toBe(1); + }); +}); + +describe("nextColSpan", () => { + it("cycles 1->2->3->1 when 3+ columns", () => { + expect(nextColSpan(1, 3)).toBe(2); + expect(nextColSpan(2, 3)).toBe(3); + expect(nextColSpan(3, 3)).toBe(1); + }); + it("caps at the column count", () => { + expect(nextColSpan(1, 2)).toBe(2); + expect(nextColSpan(2, 2)).toBe(1); + expect(nextColSpan(1, 1)).toBe(1); + }); +}); + +describe("sortSlots", () => { + it("returns a stable copy ordered by order", () => { + const input = [slot("b", 2), slot("a", 1), slot("c", 3)]; + const out = sortSlots(input); + expect(out.map((s) => s.id)).toEqual(["a", "b", "c"]); + expect(out).not.toBe(input); + }); +}); + +describe("reorderSlots", () => { + const base = [slot("a", 0), slot("b", 1), slot("c", 2)]; + + it("moves a slot to the front and renumbers densely", () => { + const out = reorderSlots(base, "c", 0); + expect(out.map((s) => s.id)).toEqual(["c", "a", "b"]); + expect(out.map((s) => s.order)).toEqual([0, 1, 2]); + }); + + it("moves a slot to the end", () => { + const out = reorderSlots(base, "a", 99); + expect(out.map((s) => s.id)).toEqual(["b", "c", "a"]); + expect(out.map((s) => s.order)).toEqual([0, 1, 2]); + }); + + it("is a no-op-ish renumber for an unknown id", () => { + const out = reorderSlots(base, "zzz", 0); + expect(out.map((s) => s.id)).toEqual(["a", "b", "c"]); + expect(out.map((s) => s.order)).toEqual([0, 1, 2]); + }); +}); + +describe("addSlot", () => { + it("appends with clamped colSpan and next order", () => { + const out = addSlot( + [slot("a", 0)], + { id: "b", label: "B", defaultColSpan: 3 }, + 2, + ); + expect(out).toHaveLength(2); + const added = out.find((s) => s.id === "b")!; + expect(added.order).toBe(1); + expect(added.colSpan).toBe(2); // clamped to columns + }); + it("does not duplicate an existing id", () => { + const input = [slot("a", 0)]; + expect(addSlot(input, { id: "a", label: "A" }, 3)).toBe(input); + }); +}); + +describe("removeSlot", () => { + it("removes and renumbers", () => { + const out = removeSlot([slot("a", 0), slot("b", 1), slot("c", 2)], "b"); + expect(out.map((s) => s.id)).toEqual(["a", "c"]); + expect(out.map((s) => s.order)).toEqual([0, 1]); + }); +}); + +describe("setColSpan / setHeight", () => { + it("sets colSpan for the matching id only", () => { + const out = setColSpan([slot("a", 0, 1), slot("b", 1, 1)], "a", 2); + expect(out.find((s) => s.id === "a")!.colSpan).toBe(2); + expect(out.find((s) => s.id === "b")!.colSpan).toBe(1); + }); + it("enforces a minimum tile height", () => { + const out = setHeight([slot("a", 0)], "a", 10); + expect(out[0].height).toBe(MIN_TILE_HEIGHT); + const out2 = setHeight([slot("a", 0)], "a", 300); + expect(out2[0].height).toBe(300); + }); +}); + +describe("heightToRowSpan", () => { + const unit = 8; + const gap = 12; + it("returns at least one row for tiny tiles", () => { + expect(heightToRowSpan(0, unit, gap)).toBe(1); + expect(heightToRowSpan(5, unit, gap)).toBe(1); + }); + it("grows the row span with height (taller -> more rows)", () => { + const short = heightToRowSpan(200, unit, gap); + const tall = heightToRowSpan(400, unit, gap); + expect(tall).toBeGreaterThan(short); + }); + it("rounds up so content is never clipped", () => { + // 200px content: ceil((200 + 12) / (8 + 12)) = ceil(10.6) = 11 + expect(heightToRowSpan(200, unit, gap)).toBe(11); + }); +}); diff --git a/src/ui/components/card-grid/layout-utils.ts b/src/ui/components/card-grid/layout-utils.ts new file mode 100644 index 00000000..24e0328a --- /dev/null +++ b/src/ui/components/card-grid/layout-utils.ts @@ -0,0 +1,105 @@ +import type { GridSlot, GridColSpan, GridCardCatalogEntry } from "./types"; + +export const MIN_TILE_HEIGHT = 80; +export const MAX_COLUMNS = 4; +export const MIN_COLUMNS = 1; + +export function clampColumns(n: number): number { + if (!Number.isFinite(n)) return MIN_COLUMNS; + return Math.min(MAX_COLUMNS, Math.max(MIN_COLUMNS, Math.round(n))); +} + +/** Cycle a tile width 1 -> 2 -> 3 -> 1, capped at the available column count. */ +export function nextColSpan( + current: GridColSpan, + columns: number, +): GridColSpan { + const max = Math.min(3, Math.max(1, columns)) as GridColSpan; + return (((current % max) + 1) as GridColSpan) || 1; +} + +/** Sort slots by order ascending (stable copy). */ +export function sortSlots(slots: GridSlot[]): GridSlot[] { + return [...slots].sort((a, b) => a.order - b.order); +} + +/** + * Move the dragged slot so it lands at `targetOrder`, then renumber every slot's + * order to a dense 0..n-1 sequence (the masonry placement is derived from order). + */ +export function reorderSlots( + slots: GridSlot[], + draggedId: string, + targetOrder: number, +): GridSlot[] { + const moved = slots.find((s) => s.id === draggedId); + if (!moved) return sortSlots(slots).map((s, i) => ({ ...s, order: i })); + const without = sortSlots(slots).filter((s) => s.id !== draggedId); + const insertIdx = without.findIndex((s) => s.order >= targetOrder); + const at = insertIdx === -1 ? without.length : insertIdx; + return [...without.slice(0, at), moved, ...without.slice(at)].map((s, i) => ({ + ...s, + order: i, + })); +} + +/** Append a catalog card as a new slot, clamping its width to the column count. */ +export function addSlot( + slots: GridSlot[], + entry: GridCardCatalogEntry, + columns: number, +): GridSlot[] { + if (slots.some((s) => s.id === entry.id)) return slots; + const maxOrder = slots.reduce((m, s) => Math.max(m, s.order), -1); + const colSpan = Math.min( + entry.defaultColSpan ?? 1, + Math.min(3, Math.max(1, columns)), + ) as GridColSpan; + return [ + ...slots, + { + id: entry.id, + order: maxOrder + 1, + colSpan, + height: entry.defaultHeight ?? null, + }, + ]; +} + +export function removeSlot(slots: GridSlot[], id: string): GridSlot[] { + return slots + .filter((s) => s.id !== id) + .sort((a, b) => a.order - b.order) + .map((s, i) => ({ ...s, order: i })); +} + +export function setColSpan( + slots: GridSlot[], + id: string, + colSpan: GridColSpan, +): GridSlot[] { + return slots.map((s) => (s.id === id ? { ...s, colSpan } : s)); +} + +export function setHeight( + slots: GridSlot[], + id: string, + height: number, +): GridSlot[] { + const h = Math.max(MIN_TILE_HEIGHT, Math.round(height)); + return slots.map((s) => (s.id === id ? { ...s, height: h } : s)); +} + +/** + * Convert a tile pixel height into the number of grid rows it should span for + * the masonry (row-span) layout. The grid uses `rowUnit`px rows separated by + * `rowGap`px, so a tile of height H occupies ceil((H + gap) / (unit + gap)) + * rows. Kept pure for unit testing the packing math. + */ +export function heightToRowSpan( + height: number, + rowUnit: number, + rowGap: number, +): number { + return Math.max(1, Math.ceil((height + rowGap) / (rowUnit + rowGap))); +} diff --git a/src/ui/components/card-grid/types.ts b/src/ui/components/card-grid/types.ts new file mode 100644 index 00000000..98ca498f --- /dev/null +++ b/src/ui/components/card-grid/types.ts @@ -0,0 +1,33 @@ +/** + * Shared types for the CardGridCanvas: a content-agnostic, N-column masonry of + * movable, variable-width, resizable tiles. The dashboard and Host Metrics both + * describe their layout with these primitives. + */ + +export type GridColSpan = 1 | 2 | 3; + +export interface GridSlot { + /** Stable card identifier (the consumer's card id). */ + id: string; + /** Global order across the masonry; column placement is derived from order. */ + order: number; + /** Tile width in grid columns (the "tetris" piece width). */ + colSpan: GridColSpan; + /** Fixed pixel height, or null for content/auto height. */ + height: number | null; +} + +export interface GridLayout { + slots: GridSlot[]; + /** Number of masonry columns (1..4). */ + columns: number; +} + +/** Catalog entry describing an addable card, used by the edit-mode Add tray. */ +export interface GridCardCatalogEntry { + id: string; + label: string; + description?: string; + defaultColSpan?: GridColSpan; + defaultHeight?: number | null; +} diff --git a/src/ui/components/charts/BarSeries.tsx b/src/ui/components/charts/BarSeries.tsx new file mode 100644 index 00000000..92b356b1 --- /dev/null +++ b/src/ui/components/charts/BarSeries.tsx @@ -0,0 +1,55 @@ +import { cn } from "@/lib/utils"; + +export interface BarSeriesItem { + label: string; + /** 0..max value driving the bar width. */ + value: number; + /** Optional right-aligned value text (defaults to value). */ + valueLabel?: string; +} + +export interface BarSeriesProps { + items: BarSeriesItem[]; + /** Domain max; defaults to the largest value (min 1). */ + max?: number; + className?: string; + colorClassName?: string; +} + +/** Compact horizontal bar list with a label, a track, and a value. */ +export function BarSeries({ + items, + max, + className, + colorClassName = "bg-accent-brand", +}: BarSeriesProps) { + const domainMax = max ?? Math.max(1, ...items.map((i) => i.value)); + return ( +
+ {items.map((item, i) => { + const pct = Math.min(100, Math.max(0, (item.value / domainMax) * 100)); + return ( +
+
+ + {item.label} + + + {item.valueLabel ?? item.value} + +
+
+
+
+
+ ); + })} +
+ ); +} diff --git a/src/ui/components/charts/RadialGauge.tsx b/src/ui/components/charts/RadialGauge.tsx new file mode 100644 index 00000000..99af5b36 --- /dev/null +++ b/src/ui/components/charts/RadialGauge.tsx @@ -0,0 +1,94 @@ +import { cn } from "@/lib/utils"; +import { gaugeArc } from "./geometry"; + +export interface RadialGaugeProps { + /** 0..100 percentage, or null for "no data". */ + value: number | null; + /** Large centered text (defaults to the percentage). */ + label?: string; + /** Small caption under the value. */ + caption?: string; + size?: number; + strokeWidth?: number; + className?: string; + /** Tailwind text-color class for the value arc. */ + colorClassName?: string; +} + +/** + * A 270-degree radial gauge with a big centered readout. Auto-themes via + * currentColor; the track uses the muted token. + */ +export function RadialGauge({ + value, + label, + caption, + size = 120, + strokeWidth = 9, + className, + colorClassName = "text-accent-brand", +}: RadialGaugeProps) { + const pct = value === null ? 0 : Math.min(100, Math.max(0, value)); + const radius = (size - strokeWidth) / 2; + const cx = size / 2; + const cy = size / 2; + + // 270-degree sweep starting at the bottom-left (135deg), going clockwise. + const startAngle = 135; + const { trackLen, valueLen, circumference } = gaugeArc(value, radius, 270); + + const display = label ?? (value === null ? "N/A" : `${Math.round(pct)}%`); + + return ( +
+ +
+ + {display} + + {caption && ( + + {caption} + + )} +
+
+ ); +} diff --git a/src/ui/components/charts/Sparkline.tsx b/src/ui/components/charts/Sparkline.tsx new file mode 100644 index 00000000..c35b7a73 --- /dev/null +++ b/src/ui/components/charts/Sparkline.tsx @@ -0,0 +1,89 @@ +import { useId } from "react"; +import { cn } from "@/lib/utils"; +import { sparklineGeometry, SPARKLINE_VIEW_W } from "./geometry"; + +export interface SparklineProps { + /** Series of values, oldest first. */ + data: Array; + /** + * Fixed value domain [min, max]. When omitted the domain is derived from the + * data. Providing a stable domain (e.g. [0, 100] for percentages) avoids the + * jittery rescaling the old sparkline had. + */ + domain?: [number, number]; + className?: string; + /** Tailwind text-color class for the line/fill (uses currentColor). */ + colorClassName?: string; + /** Show a dot on the last point. */ + showLastDot?: boolean; + /** Internal coordinate height; the SVG scales to its container. */ + height?: number; +} + +/** + * Compact area + line chart. Uses a non-scaling stroke + a stable value domain + * so the line stays crisp and readable regardless of container size. + */ +export function Sparkline({ + data, + domain, + className, + colorClassName = "text-accent-brand", + showLastDot = true, + height = 56, +}: SparklineProps) { + const gradientId = useId(); + const { hasData, coords, linePath, areaPath } = sparklineGeometry( + data, + height, + domain, + ); + const last = coords[coords.length - 1]; + + return ( +
+ {hasData && ( + + )} +
+ ); +} diff --git a/src/ui/components/charts/StatRow.tsx b/src/ui/components/charts/StatRow.tsx new file mode 100644 index 00000000..3c678062 --- /dev/null +++ b/src/ui/components/charts/StatRow.tsx @@ -0,0 +1,58 @@ +import type { ReactNode } from "react"; +import { cn } from "@/lib/utils"; + +/** Dense key/value row used across metric and manager cards. */ +export function StatRow({ + label, + value, + mono = false, + className, +}: { + label: ReactNode; + value: ReactNode; + mono?: boolean; + className?: string; +}) { + return ( +
+ {label} + + {value} + +
+ ); +} + +/** A compact stacked stat: small uppercase caption over a bold value. */ +export function MiniStat({ + caption, + value, + valueClassName, + className, +}: { + caption: ReactNode; + value: ReactNode; + valueClassName?: string; + className?: string; +}) { + return ( +
+ + {caption} + + + {value} + +
+ ); +} diff --git a/src/ui/components/charts/geometry.test.ts b/src/ui/components/charts/geometry.test.ts new file mode 100644 index 00000000..5d403806 --- /dev/null +++ b/src/ui/components/charts/geometry.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect } from "vitest"; +import { sparklineGeometry, gaugeArc, SPARKLINE_VIEW_W } from "./geometry"; + +describe("sparklineGeometry", () => { + it("returns no data for fewer than 2 points", () => { + expect(sparklineGeometry([], 50).hasData).toBe(false); + expect(sparklineGeometry([5], 50).hasData).toBe(false); + }); + + it("maps endpoints to the full width", () => { + const g = sparklineGeometry([0, 100], 100, [0, 100]); + expect(g.coords[0][0]).toBe(0); + expect(g.coords[1][0]).toBe(SPARKLINE_VIEW_W); + }); + + it("inverts the y-axis (higher value = smaller y)", () => { + const g = sparklineGeometry([0, 100], 100, [0, 100]); + expect(g.coords[0][1]).toBe(100); // value 0 -> bottom + expect(g.coords[1][1]).toBe(0); // value 100 -> top + }); + + it("respects a fixed domain (no rescaling)", () => { + // With domain [0,100], a mid value of 50 sits at half height regardless + // of the other points. + const g = sparklineGeometry([50, 50, 50], 100, [0, 100]); + for (const [, y] of g.coords) expect(y).toBe(50); + }); + + it("clamps out-of-domain values", () => { + const g = sparklineGeometry([-20, 200], 100, [0, 100]); + expect(g.coords[0][1]).toBe(100); // clamped to lo + expect(g.coords[1][1]).toBe(0); // clamped to hi + }); + + it("treats null/NaN as 0", () => { + const g = sparklineGeometry([null, undefined, NaN], 100, [0, 100]); + expect(g.hasData).toBe(true); + for (const [, y] of g.coords) expect(y).toBe(100); + }); + + it("builds line and area paths", () => { + const g = sparklineGeometry([0, 100], 100, [0, 100]); + expect(g.linePath.startsWith("M ")).toBe(true); + expect(g.areaPath.startsWith("M 0,100")).toBe(true); + expect(g.areaPath.endsWith("Z")).toBe(true); + }); +}); + +describe("gaugeArc", () => { + it("zero value yields zero value-arc", () => { + const a = gaugeArc(0, 50); + expect(a.valueLen).toBe(0); + expect(a.trackLen).toBeGreaterThan(0); + }); + + it("null is treated as 0", () => { + expect(gaugeArc(null, 50).valueLen).toBe(0); + }); + + it("100% fills the whole track", () => { + const a = gaugeArc(100, 50); + expect(a.valueLen).toBeCloseTo(a.trackLen, 6); + }); + + it("track is the swept fraction of the circumference", () => { + const a = gaugeArc(50, 50, 270); + expect(a.trackLen).toBeCloseTo(a.circumference * 0.75, 6); + expect(a.valueLen).toBeCloseTo(a.trackLen * 0.5, 6); + }); + + it("clamps values above 100", () => { + const a = gaugeArc(150, 50); + expect(a.valueLen).toBeCloseTo(a.trackLen, 6); + }); +}); diff --git a/src/ui/components/charts/geometry.ts b/src/ui/components/charts/geometry.ts new file mode 100644 index 00000000..31f0b9dd --- /dev/null +++ b/src/ui/components/charts/geometry.ts @@ -0,0 +1,66 @@ +/** Pure geometry helpers for the chart primitives (unit-tested). */ + +export const SPARKLINE_VIEW_W = 300; + +export interface SparklineGeometry { + hasData: boolean; + coords: Array; + linePath: string; + areaPath: string; +} + +/** + * Map a value series to SVG coordinates within [0..VIEW_W] x [0..height]. + * A fixed `domain` keeps the y-axis stable (no per-tick rescaling). + */ +export function sparklineGeometry( + data: Array, + height: number, + domain?: [number, number], +): SparklineGeometry { + const points = data.map((v) => + typeof v === "number" && isFinite(v) ? v : 0, + ); + const hasData = points.length >= 2; + if (!hasData) + return { hasData: false, coords: [], linePath: "", areaPath: "" }; + + const lo = domain ? domain[0] : Math.min(...points); + const hiRaw = domain ? domain[1] : Math.max(...points); + const hi = hiRaw === lo ? lo + 1 : hiRaw; + const range = hi - lo; + + const coords = points.map((v, i) => { + const x = (i / (points.length - 1)) * SPARKLINE_VIEW_W; + const clamped = Math.min(hi, Math.max(lo, v)); + const y = height - ((clamped - lo) / range) * height; + return [x, y] as const; + }); + + const line = coords.map(([x, y]) => `${x},${y}`).join(" "); + return { + hasData: true, + coords, + linePath: `M ${line}`, + areaPath: `M 0,${height} L ${line} L ${SPARKLINE_VIEW_W},${height} Z`, + }; +} + +export interface GaugeArc { + trackLen: number; + valueLen: number; + circumference: number; +} + +/** Arc lengths for a radial gauge sweeping `sweepDeg` degrees. */ +export function gaugeArc( + value: number | null, + radius: number, + sweepDeg = 270, +): GaugeArc { + const pct = value === null ? 0 : Math.min(100, Math.max(0, value)); + const circumference = 2 * Math.PI * radius; + const trackLen = circumference * (sweepDeg / 360); + const valueLen = trackLen * (pct / 100); + return { trackLen, valueLen, circumference }; +} diff --git a/src/ui/components/charts/index.ts b/src/ui/components/charts/index.ts new file mode 100644 index 00000000..0e30d232 --- /dev/null +++ b/src/ui/components/charts/index.ts @@ -0,0 +1,8 @@ +export { Sparkline, type SparklineProps } from "./Sparkline"; +export { RadialGauge, type RadialGaugeProps } from "./RadialGauge"; +export { + BarSeries, + type BarSeriesItem, + type BarSeriesProps, +} from "./BarSeries"; +export { StatRow, MiniStat } from "./StatRow"; diff --git a/src/ui/components/folder-style.tsx b/src/ui/components/folder-style.tsx new file mode 100644 index 00000000..57853fba --- /dev/null +++ b/src/ui/components/folder-style.tsx @@ -0,0 +1,248 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + Folder, + FolderGit2, + FolderOpen, + Server, + ServerCog, + Cloud, + CloudCog, + Database, + HardDrive, + Box, + Boxes, + Container, + Network, + Router, + Wifi, + Globe, + Globe2, + Terminal, + Cpu, + MemoryStick, + Shield, + ShieldCheck, + Lock, + Key, + Rocket, + FlaskConical, + Wrench, + Hammer, + Gauge, + Activity, + Layers, + Building, + Building2, + Home, + Briefcase, + Code, + GitBranch, + Bug, + Settings, + Cog, + Star, + Flag, + Bookmark, + Tag, + Users, + User, + Zap, + Flame, + Leaf, + Mail, + Monitor, + Smartphone, + Search, + type LucideIcon, +} from "lucide-react"; +import { Input } from "@/components/input"; + +export const FOLDER_ICON_MAP: Record = { + folder: Folder, + "folder-open": FolderOpen, + "folder-git": FolderGit2, + server: Server, + "server-cog": ServerCog, + cloud: Cloud, + "cloud-cog": CloudCog, + database: Database, + "hard-drive": HardDrive, + box: Box, + boxes: Boxes, + container: Container, + network: Network, + router: Router, + wifi: Wifi, + globe: Globe, + "globe-2": Globe2, + terminal: Terminal, + cpu: Cpu, + memory: MemoryStick, + shield: Shield, + "shield-check": ShieldCheck, + lock: Lock, + key: Key, + rocket: Rocket, + flask: FlaskConical, + wrench: Wrench, + hammer: Hammer, + gauge: Gauge, + activity: Activity, + layers: Layers, + building: Building, + "building-2": Building2, + home: Home, + briefcase: Briefcase, + code: Code, + "git-branch": GitBranch, + bug: Bug, + settings: Settings, + cog: Cog, + star: Star, + flag: Flag, + bookmark: Bookmark, + tag: Tag, + users: Users, + user: User, + zap: Zap, + flame: Flame, + leaf: Leaf, + mail: Mail, + monitor: Monitor, + smartphone: Smartphone, +}; + +export const FOLDER_ICON_IDS = Object.keys(FOLDER_ICON_MAP); + +export const DEFAULT_FOLDER_ICON = "folder"; +export const DEFAULT_FOLDER_COLOR = "#f59145"; + +export const SUGGESTED_FOLDER_COLORS = [ + "#f59145", + "#ef4444", + "#eab308", + "#22c55e", + "#14b8a6", + "#3b82f6", + "#6366f1", + "#a855f7", + "#ec4899", + "#6b7280", +]; + +export function FolderIconEl({ + icon, + className, + style, +}: { + icon?: string | null; + className?: string; + style?: React.CSSProperties; +}) { + const Icon = FOLDER_ICON_MAP[icon ?? ""] ?? Folder; + return ; +} + +const HEX_RE = /^#([0-9a-fA-F]{6})$/; + +export function isValidHex(value: string): boolean { + return HEX_RE.test(value.trim()); +} + +export function HexColorPicker({ + value, + onChange, +}: { + value: string; + onChange: (hex: string) => void; +}) { + const safe = isValidHex(value) ? value : DEFAULT_FOLDER_COLOR; + return ( +
+
+ onChange(e.target.value)} + className="h-8 w-10 shrink-0 cursor-pointer rounded-none border border-input bg-transparent p-0.5" + /> + onChange(e.target.value)} + placeholder="#6b7280" + className="font-mono uppercase" + maxLength={7} + /> +
+
+ {SUGGESTED_FOLDER_COLORS.map((c) => ( +
+
+ ); +} + +export function IconPicker({ + value, + color, + onChange, +}: { + value: string; + color?: string; + onChange: (icon: string) => void; +}) { + const { t } = useTranslation(); + const [query, setQuery] = useState(""); + const filtered = query.trim() + ? FOLDER_ICON_IDS.filter((id) => + id.toLowerCase().includes(query.trim().toLowerCase()), + ) + : FOLDER_ICON_IDS; + + return ( +
+
+ + setQuery(e.target.value)} + placeholder={t("hosts.iconSearchPlaceholder")} + className="flex-1 text-xs bg-transparent outline-none placeholder:text-muted-foreground/50 text-foreground min-w-0" + /> +
+
+ {filtered.map((id) => ( + + ))} +
+
+ ); +} diff --git a/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx b/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx new file mode 100644 index 00000000..f302d70a --- /dev/null +++ b/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx @@ -0,0 +1,378 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Server, RefreshCw, CheckSquare, Square, Download } from "lucide-react"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/dialog"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/select"; +import { + discoverProxmoxGuests, + bulkImportSSHHosts, + getSSHHosts, +} from "@/main-axios"; +import type { SSHHostWithStatus } from "@/main-axios"; +import type { ProxmoxGuest } from "@/types/proxmox"; + +interface ProxmoxDiscoverDialogProps { + open: boolean; + onClose: () => void; + hosts: SSHHostWithStatus[]; + onHostsChanged: (hosts: SSHHostWithStatus[]) => void; + /** Pre-select a specific host and skip the host picker */ + preselectedHostId?: number; + /** Credential to use for imported hosts */ + defaultCredentialId?: number | null; + /** Username from the default credential */ + defaultUsername?: string; +} + +export function ProxmoxDiscoverDialog({ + open, + onClose, + hosts, + onHostsChanged, + preselectedHostId, + defaultCredentialId, + defaultUsername, +}: ProxmoxDiscoverDialogProps) { + const { t } = useTranslation(); + const [selectedHostId, setSelectedHostId] = useState( + preselectedHostId ? String(preselectedHostId) : "", + ); + const [discovering, setDiscovering] = useState(false); + const [guests, setGuests] = useState(null); + const [discoveredCredentialId, setDiscoveredCredentialId] = useState< + number | null + >(null); + const [selected, setSelected] = useState>(new Set()); + const [importing, setImporting] = useState(false); + + // When opened from the dropdown (no preselectedHostId), only show Proxmox-enabled hosts + const sshHosts = hosts.filter( + (h) => !("isFolder" in h) && h.enableProxmox === true, + ); + + // The Proxmox host the discovery runs against — imported guests are grouped + // into a folder named after it. Use the same id resolution as discovery so + // it also works when launched directly from a host action (preselectedHostId). + const effectiveHostId = + preselectedHostId != null ? String(preselectedHostId) : selectedHostId; + const sourceHost = hosts.find( + (h) => !("isFolder" in h) && String(h.id) === effectiveHostId, + ); + const importFolder = sourceHost?.name || "Proxmox"; + + function reset() { + if (!preselectedHostId) setSelectedHostId(""); + setGuests(null); + setDiscoveredCredentialId(null); + setSelected(new Set()); + setDiscovering(false); + setImporting(false); + } + + async function handleDiscover() { + const hostId = + preselectedHostId ?? (selectedHostId ? Number(selectedHostId) : null); + if (!hostId) return; + setDiscovering(true); + setGuests(null); + setDiscoveredCredentialId(null); + setSelected(new Set()); + try { + const result = await discoverProxmoxGuests(hostId); + setGuests(result.guests); + setDiscoveredCredentialId(result.credentialId ?? null); + setSelected( + new Set( + result.guests + .filter((g) => g.status === "running") + .map((g) => g.vmid), + ), + ); + } catch (err: any) { + toast.error(err?.message ?? t("hosts.proxmoxDiscoveryFailed")); + } finally { + setDiscovering(false); + } + } + + async function handleImport() { + if (!guests || selected.size === 0) return; + setImporting(true); + try { + // Prefer explicitly configured credential, then fall back to the host's own credential + const credId = defaultCredentialId ?? discoveredCredentialId; + + const toImport = guests + .filter((g) => selected.has(g.vmid)) + .map((g) => ({ + name: g.name, + ip: g.ip ?? "0.0.0.0", + port: g.connectionType === "rdp" ? 3389 : 22, + username: defaultUsername ?? "root", + folder: importFolder, + ...(credId != null + ? { + authType: "credential" as const, + credentialId: credId, + overrideCredentialUsername: true, + } + : { authType: "password" as const }), + enableTerminal: g.connectionType !== "rdp", + enableFileManager: g.connectionType !== "rdp", + enableTunnel: g.connectionType !== "rdp", + enableSsh: g.connectionType !== "rdp", + enableRdp: g.connectionType === "rdp", + enableDocker: g.enableDocker, + connectionType: g.connectionType, + tags: ["proxmox", g.type, g.node], + })); + + const result = await bulkImportSSHHosts(toImport, false); + const updated = await getSSHHosts(); + onHostsChanged(updated); + window.dispatchEvent(new CustomEvent("termix:hosts-changed")); + + const msg = [ + result.success + ? t("hosts.proxmoxResultImported", { count: result.success }) + : null, + result.updated + ? t("hosts.proxmoxResultUpdated", { count: result.updated }) + : null, + result.failed + ? t("hosts.proxmoxResultFailed", { count: result.failed }) + : null, + ] + .filter(Boolean) + .join(", "); + toast.success(t("hosts.proxmoxImportComplete", { summary: msg })); + + if (result.failed === 0) { + reset(); + onClose(); + } + } catch (err: any) { + toast.error(err?.message ?? t("hosts.proxmoxImportFailed")); + } finally { + setImporting(false); + } + } + + const nodeGroups = guests + ? guests.reduce( + (acc, g) => { + if (!acc[g.node]) acc[g.node] = []; + acc[g.node].push(g); + return acc; + }, + {} as Record, + ) + : {}; + + const canDiscover = preselectedHostId != null || !!selectedHostId; + + return ( + { + if (!v) { + reset(); + onClose(); + } + }} + > + + + + + {t("hosts.proxmoxImportTitle")} + +
+ {t("hosts.docsLink")} + + + +
+ {/* Host selector — hidden when launched from a specific host */} + {!preselectedHostId && ( +
+ + +
+ )} + + {/* When launched from a specific host, show Discover directly */} + {preselectedHostId && !guests && ( + + )} + + {/* Guest list */} + {guests !== null && ( +
+
+ + {t("hosts.proxmoxGuestsSelected", { + count: guests.length, + selected: selected.size, + })} + +
+ + · + +
+
+ + {guests.length === 0 ? ( +

+ {t("hosts.proxmoxNoGuests")} +

+ ) : ( +
+ {Object.entries(nodeGroups).map(([node, nodeGuests]) => ( +
+
+ {node} +
+ {nodeGuests.map((g) => ( + + ))} +
+ ))} +
+ )} +
+ )} +
+ + + + {guests !== null && selected.size > 0 && ( + + )} + + + + ); +} diff --git a/src/ui/components/section-card.tsx b/src/ui/components/section-card.tsx index f2b7c72e..b0e0d1e0 100644 --- a/src/ui/components/section-card.tsx +++ b/src/ui/components/section-card.tsx @@ -34,7 +34,7 @@ export function SettingRow({ }: { label: string; badge?: string; - description?: string; + description?: React.ReactNode; children: React.ReactNode; }) { return ( diff --git a/src/ui/dashboard/Dashboard.tsx b/src/ui/dashboard/Dashboard.tsx deleted file mode 100644 index af889d4c..00000000 --- a/src/ui/dashboard/Dashboard.tsx +++ /dev/null @@ -1,887 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState, useRef, useCallback } from "react"; -import { Auth } from "@/auth/LoginPage.tsx"; -import { AlertManager } from "@/dashboard/panels/alerts/AlertManager.tsx"; -import { Button } from "@/components/button.tsx"; -import { - getUserInfo, - getDatabaseHealth, - getUptime, - getVersionInfo, - getSSHHosts, - getCredentials, - getRecentActivity, - resetRecentActivity, - getAllServerStatuses, - getServerMetricsById, - registerMetricsViewer, - sendMetricsHeartbeat, - getGuacamoleDpi, - getGuacamoleTokenFromHost, - isCurrentAuthInvalidationError, - type RecentActivityItem, -} from "@/main-axios.ts"; -import { useSidebar } from "@/components/sidebar.tsx"; -import { Separator } from "@/components/separator.tsx"; -import { useTabs } from "@/shell/TabContext.tsx"; -import { Kbd } from "@/components/kbd"; -import { useTranslation } from "react-i18next"; -import { Settings as SettingsIcon } from "lucide-react"; -import { ServerOverviewCard } from "@/dashboard/cards/ServerOverviewCard"; -import { RecentActivityCard } from "@/dashboard/cards/RecentActivityCard"; -import { QuickActionsCard } from "@/dashboard/cards/QuickActionsCard"; -import { ServerStatsCard } from "@/dashboard/cards/ServerStatsCard"; -import { NetworkGraphCard } from "@/dashboard/cards/NetworkGraphCard"; -import { useDashboardPreferences } from "@/dashboard/hooks/useDashboardPreferences"; -import { DashboardSettingsDialog } from "@/dashboard/components/DashboardSettingsDialog"; -import { SimpleLoader } from "@/lib/SimpleLoader"; - -interface DashboardProps { - onSelectView: (view: string) => void; - isAuthenticated: boolean; - authLoading: boolean; - onAuthSuccess: (authData: { - isAdmin: boolean; - username: string | null; - userId: string | null; - }) => void; - isTopbarOpen: boolean; - rightSidebarOpen?: boolean; - rightSidebarWidth?: number; - initialDbError?: string | null; -} - -export function Dashboard({ - isAuthenticated, - authLoading, - onAuthSuccess, - isTopbarOpen, - rightSidebarOpen = false, - rightSidebarWidth = 400, - initialDbError = null, -}: DashboardProps): React.ReactElement { - const { t } = useTranslation(); - const [loggedIn, setLoggedIn] = useState(isAuthenticated); - const [isAdmin, setIsAdmin] = useState(false); - const [, setUsername] = useState(null); - const [userId, setUserId] = useState(null); - const [, setDbError] = useState(initialDbError); - - const [uptime, setUptime] = useState("0d 0h 0m"); - const [versionStatus, setVersionStatus] = useState< - "up_to_date" | "requires_update" | "beta" - >("up_to_date"); - const [versionText, setVersionText] = useState(""); - const [dbHealth, setDbHealth] = useState<"healthy" | "error">("healthy"); - const [totalServers, setTotalServers] = useState(0); - const [totalTunnels, setTotalTunnels] = useState(0); - const [totalCredentials, setTotalCredentials] = useState(0); - const [updateCheckDisabled, setUpdateCheckDisabled] = useState( - localStorage.getItem("disableUpdateCheck") === "true", - ); - const [recentActivity, setRecentActivity] = useState( - [], - ); - const [recentActivityLoading, setRecentActivityLoading] = - useState(true); - const [serverStats, setServerStats] = useState< - Array<{ id: number; name: string; cpu: number | null; ram: number | null }> - >([]); - const [serverStatsLoading, setServerStatsLoading] = useState(true); - const [settingsDialogOpen, setSettingsDialogOpen] = useState(false); - const [viewerSessions, setViewerSessions] = useState>( - new Map(), - ); - const [initialLoading, setInitialLoading] = useState(true); - - const { addTab, setCurrentTab, tabs: tabList } = useTabs(); - const { - layout, - loading: preferencesLoading, - updateLayout, - resetLayout, - } = useDashboardPreferences(loggedIn); - - const containerRef = useRef(null); - const mainWidthPct = layout?.mainWidthPct ?? 68; - - const handleDividerMouseDown = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - const startX = e.clientX; - const startPct = mainWidthPct; - const onMove = (ev: MouseEvent) => { - if (!containerRef.current) return; - const totalW = containerRef.current.getBoundingClientRect().width; - const delta = ev.clientX - startX; - const newPct = Math.min( - 85, - Math.max(30, startPct + (delta / totalW) * 100), - ); - if (layout) updateLayout({ ...layout, mainWidthPct: newPct }); - }; - const onUp = () => { - window.removeEventListener("mousemove", onMove); - window.removeEventListener("mouseup", onUp); - }; - window.addEventListener("mousemove", onMove); - window.addEventListener("mouseup", onUp); - }, - [mainWidthPct, layout, updateLayout], - ); - - const handleCardHeightMouseDown = useCallback( - (e: React.MouseEvent, cardId: string, currentHeight: number) => { - e.preventDefault(); - const startY = e.clientY; - const startH = currentHeight; - const onMove = (ev: MouseEvent) => { - const newH = Math.max(180, startH + (ev.clientY - startY)); - if (!layout) return; - updateLayout({ - ...layout, - cards: layout.cards.map((c) => - c.id === cardId ? { ...c, height: Math.round(newH) } : c, - ), - }); - }; - const onUp = () => { - window.removeEventListener("mousemove", onMove); - window.removeEventListener("mouseup", onUp); - }; - window.addEventListener("mousemove", onMove); - window.addEventListener("mouseup", onUp); - }, - [layout, updateLayout], - ); - - let sidebarState: "expanded" | "collapsed" = "expanded"; - try { - const sidebar = useSidebar(); - sidebarState = sidebar.state; - } catch { - // Sidebar context is not available on every dashboard mount path. - } - - const topMarginPx = isTopbarOpen ? 74 : 26; - const leftMarginPx = sidebarState === "collapsed" ? 26 : 8; - const rightMarginPx = 17; - const bottomMarginPx = 8; - - useEffect(() => { - setLoggedIn(isAuthenticated); - }, [isAuthenticated]); - - useEffect(() => { - if (isAuthenticated) { - getUserInfo() - .then((meRes) => { - setIsAdmin(!!meRes.is_admin); - setUsername(meRes.username || null); - setUserId(meRes.userId || null); - setDbError(null); - }) - .catch((err) => { - if (isCurrentAuthInvalidationError(err)) { - setIsAdmin(false); - setUsername(null); - setUserId(null); - console.warn("Session expired - please log in again"); - setDbError("Session expired - please log in again"); - } else { - setDbError(null); - } - }); - - getDatabaseHealth() - .then(() => { - setDbError(null); - }) - .catch((err) => { - if (err?.response?.data?.error?.includes("Database")) { - setDbError( - "Could not connect to the database. Please try again later.", - ); - } - }); - } - }, [isAuthenticated]); - - useEffect(() => { - if (!loggedIn) return; - - const fetchDashboardData = async () => { - try { - const uptimeInfo = await getUptime(); - setUptime(uptimeInfo.formatted); - - const updateDisabled = - localStorage.getItem("disableUpdateCheck") === "true"; - setUpdateCheckDisabled(updateDisabled); - if (!updateDisabled) { - const versionInfo = await getVersionInfo(); - setVersionText(`v${versionInfo.localVersion}`); - if ( - versionInfo.status === "up_to_date" || - versionInfo.status === "requires_update" || - versionInfo.status === "beta" - ) { - setVersionStatus(versionInfo.status); - } - } else { - const versionInfo = await getVersionInfo(); - setVersionText(`v${versionInfo.localVersion}`); - } - - try { - await getDatabaseHealth(); - setDbHealth("healthy"); - } catch { - setDbHealth("error"); - } - - const hostsResponse = await getSSHHosts(); - const hosts = Array.isArray(hostsResponse) ? hostsResponse : []; - setTotalServers(hosts.length); - - let totalTunnelsCount = 0; - for (const host of hosts) { - if (host.tunnelConnections) { - try { - const tunnelConnections = Array.isArray(host.tunnelConnections) - ? host.tunnelConnections - : JSON.parse(host.tunnelConnections); - if (Array.isArray(tunnelConnections)) { - totalTunnelsCount += tunnelConnections.length; - } - } catch (error) { - console.error("Dashboard operation failed:", error); - } - } - } - setTotalTunnels(totalTunnelsCount); - - const credentialsResponse = await getCredentials(); - const credentials = Array.isArray(credentialsResponse) - ? credentialsResponse - : []; - setTotalCredentials(credentials.length); - - setRecentActivityLoading(true); - const activityResponse = await getRecentActivity(35); - const activity = Array.isArray(activityResponse) - ? activityResponse - : []; - setRecentActivity(activity); - setRecentActivityLoading(false); - - setServerStatsLoading(true); - - // Fetch current host statuses once so we can skip offline hosts - // before issuing per-host register-viewer / metrics requests. - let hostStatuses: Record = {}; - try { - hostStatuses = (await getAllServerStatuses()) as Record< - number, - { status?: string } - >; - } catch { - // Best-effort: if the status endpoint is unavailable, fall back - // to the previous behavior and still attempt each host. - hostStatuses = {}; - } - - const newViewerSessions = new Map(); - const serversWithStats = await Promise.all( - hosts.slice(0, 50).map( - async (host: { - id: number; - name: string; - authType?: string; - statsConfig?: - | string - | { - metricsEnabled?: boolean; - statusCheckEnabled?: boolean; - }; - }) => { - try { - let statsConfig: { - metricsEnabled?: boolean; - statusCheckEnabled?: boolean; - } = { - metricsEnabled: true, - statusCheckEnabled: true, - }; - if (host.statsConfig) { - if (typeof host.statsConfig === "string") { - statsConfig = JSON.parse(host.statsConfig); - } else { - statsConfig = host.statsConfig; - } - } - - if (statsConfig.metricsEnabled === false) { - return null; - } - - if (host.authType === "none") { - return null; - } - - if (host.authType === "opkssh") { - return null; - } - - // Skip hosts that are known to be offline: no metrics can - // possibly exist for them, and hitting /metrics/:id would - // just 404. If the status is unknown (e.g. no entry yet - // or statusCheckEnabled === false) we still attempt. - if (statsConfig.statusCheckEnabled !== false) { - const knownStatus = hostStatuses?.[host.id]?.status; - if (knownStatus === "offline") { - return null; - } - } - - const existingSession = viewerSessions.get(host.id); - let sessionId = existingSession; - let registrationSkipped = false; - - if (!existingSession) { - try { - const viewerResult = await registerMetricsViewer(host.id); - if (viewerResult.skipped) { - // Metrics disabled/unsupported on this host; don't - // poll and don't surface this as an error. - registrationSkipped = true; - } else if ( - viewerResult.success && - viewerResult.viewerSessionId - ) { - sessionId = viewerResult.viewerSessionId; - newViewerSessions.set(host.id, sessionId); - } - } catch (error) { - console.error( - `Failed to register viewer for host ${host.id}:`, - error, - ); - } - } else { - newViewerSessions.set(host.id, existingSession); - } - - if (registrationSkipped) { - return null; - } - - const metrics = await getServerMetricsById(host.id); - if (!metrics) { - return { - id: host.id, - name: host.name || `Host ${host.id}`, - cpu: null, - ram: null, - }; - } - return { - id: host.id, - name: host.name || `Host ${host.id}`, - cpu: metrics.cpu?.percent ?? null, - ram: metrics.memory?.percent ?? null, - }; - } catch { - return { - id: host.id, - name: host.name || `Host ${host.id}`, - cpu: null, - ram: null, - }; - } - }, - ), - ); - setViewerSessions(newViewerSessions); - const validServerStats = serversWithStats.filter( - ( - server, - ): server is { - id: number; - name: string; - cpu: number | null; - ram: number | null; - } => server !== null, - ); - setServerStats(validServerStats); - setServerStatsLoading(false); - } catch (error) { - console.error("Failed to fetch dashboard data:", error); - setRecentActivityLoading(false); - setServerStatsLoading(false); - } finally { - setInitialLoading(false); - } - }; - - fetchDashboardData(); - - const interval = setInterval(fetchDashboardData, 30000); - return () => clearInterval(interval); - }, [loggedIn]); - - useEffect(() => { - if (!loggedIn || viewerSessions.size === 0) return; - - const heartbeatInterval = setInterval(async () => { - for (const [, sessionId] of viewerSessions) { - try { - await sendMetricsHeartbeat(sessionId); - } catch (error) { - console.error("Failed to send heartbeat:", error); - } - } - }, 30000); - - return () => clearInterval(heartbeatInterval); - }, [loggedIn, viewerSessions]); - - const handleResetActivity = async () => { - try { - await resetRecentActivity(); - setRecentActivity([]); - } catch (error) { - console.error("Failed to reset activity:", error); - } - }; - - const handleActivityClick = (item: RecentActivityItem) => { - getSSHHosts().then((hosts) => { - const host = hosts.find((h: { id: number }) => h.id === item.hostId); - if (!host) return; - - if (item.type === "terminal") { - addTab({ - type: "terminal", - title: item.hostName, - hostConfig: host, - }); - } else if (item.type === "file_manager") { - addTab({ - type: "file_manager", - title: item.hostName, - hostConfig: host, - }); - } else if (item.type === "server_stats") { - addTab({ - type: "server_stats", - title: item.hostName, - hostConfig: host, - }); - } else if (item.type === "tunnel") { - addTab({ - type: "tunnel", - title: item.hostName, - hostConfig: host, - }); - } else if (item.type === "docker") { - addTab({ - type: "docker", - title: item.hostName, - hostConfig: host, - }); - } else if (item.type === "telnet") { - getGuacamoleTokenFromHost(host.id) - .then((result) => { - addTab({ - type: "telnet", - title: item.hostName, - hostConfig: host, - connectionConfig: { - token: result.token, - protocol: "telnet", - type: "telnet", - hostname: host.ip, - port: host.port, - username: host.username, - password: host.password, - domain: host.domain, - security: host.security, - "ignore-cert": host.ignoreCert, - dpi: getGuacamoleDpi(host), - }, - }); - }) - .catch((error) => { - console.error("Failed to get telnet token:", error); - }); - } else if (item.type === "vnc") { - getGuacamoleTokenFromHost(host.id) - .then((result) => { - addTab({ - type: "vnc", - title: item.hostName, - hostConfig: host, - connectionConfig: { - token: result.token, - protocol: "vnc", - type: "vnc", - hostname: host.ip, - port: host.port, - username: host.username, - password: host.password, - domain: host.domain, - security: host.security, - "ignore-cert": host.ignoreCert, - dpi: getGuacamoleDpi(host), - }, - }); - }) - .catch((error) => { - console.error("Failed to get vnc token:", error); - }); - } else if (item.type === "rdp") { - getGuacamoleTokenFromHost(host.id) - .then((result) => { - addTab({ - type: "rdp", - title: item.hostName, - hostConfig: host, - connectionConfig: { - token: result.token, - protocol: "rdp", - type: "rdp", - hostname: host.ip, - port: host.port, - username: host.username, - password: host.password, - domain: host.domain, - security: host.security, - "ignore-cert": host.ignoreCert, - dpi: getGuacamoleDpi(host), - }, - }); - }) - .catch((error) => { - console.error("Failed to get rdp token:", error); - }); - } - }); - }; - - const handleServerStatClick = (serverId: number, serverName: string) => { - getSSHHosts().then((hosts) => { - const host = hosts.find((h: { id: number }) => h.id === serverId); - if (!host) return; - - addTab({ - type: "server_stats", - title: serverName, - hostConfig: host, - }); - }); - }; - - const handleAddHost = () => { - const sshManagerTab = tabList.find((t) => t.type === "ssh_manager"); - if (sshManagerTab) { - setCurrentTab(sshManagerTab.id); - setTimeout(() => { - window.dispatchEvent(new CustomEvent("host-manager:add-host")); - }, 100); - } else { - const id = addTab({ - type: "ssh_manager", - title: "Host Manager", - initialTab: "hosts", - }); - setCurrentTab(id); - setTimeout(() => { - window.dispatchEvent(new CustomEvent("host-manager:add-host")); - }, 100); - } - }; - - const handleAddCredential = () => { - const sshManagerTab = tabList.find((t) => t.type === "ssh_manager"); - if (sshManagerTab) { - setCurrentTab(sshManagerTab.id); - setTimeout(() => { - window.dispatchEvent(new CustomEvent("host-manager:add-credential")); - }, 100); - } else { - const id = addTab({ - type: "ssh_manager", - title: "Host Manager", - initialTab: "credentials", - }); - setCurrentTab(id); - setTimeout(() => { - window.dispatchEvent(new CustomEvent("host-manager:add-credential")); - }, 100); - } - }; - - const handleOpenAdminSettings = () => { - const adminTab = tabList.find((t) => t.type === "admin"); - if (adminTab) { - setCurrentTab(adminTab.id); - } else { - const id = addTab({ type: "admin", title: "Admin Settings" }); - setCurrentTab(id); - } - }; - - const handleOpenUserProfile = () => { - const userProfileTab = tabList.find((t) => t.type === "user_profile"); - if (userProfileTab) { - setCurrentTab(userProfileTab.id); - } else { - const id = addTab({ type: "user_profile", title: "User Profile" }); - setCurrentTab(id); - } - }; - - return ( - <> - {!loggedIn ? ( -
- -
- ) : ( -
-
- -
-
-
- {t("dashboard.title")} -
-
-
-
-

- Press L Shift twice to open the command palette -

-
- - - - -
-
- - - -
- {!preferencesLoading && - layout && - (() => { - const enabledCards = layout.cards - .filter((card) => card.enabled) - .sort((a, b) => a.order - b.order); - - const mainCards = enabledCards.filter( - (c) => (c.panel ?? "main") === "main", - ); - const sideCards = enabledCards.filter( - (c) => c.panel === "side", - ); - - const renderCard = (card: (typeof enabledCards)[0]) => { - if (card.id === "server_overview") { - return ( - - ); - } else if (card.id === "recent_activity") { - return ( - - ); - } else if (card.id === "network_graph") { - return ( - - ); - } else if (card.id === "quick_actions") { - return ( - - ); - } else if (card.id === "server_stats") { - return ( - - ); - } - return null; - }; - - const renderCardWithHandle = ( - card: (typeof enabledCards)[0], - ) => { - const currentHeight = card.height ?? 280; - return ( -
-
{renderCard(card)}
-
- handleCardHeightMouseDown(e, card.id, currentHeight) - } - > -
-
-
- ); - }; - - return ( - <> -
- {mainCards.map((card) => renderCardWithHandle(card))} -
- -
-
-
- -
- {sideCards.map((card) => renderCardWithHandle(card))} -
- - ); - })()} -
-
-
- )} - - - - {layout && ( - - )} - - ); -} diff --git a/src/ui/dashboard/DashboardTab.tsx b/src/ui/dashboard/DashboardTab.tsx index 919e5900..9cf24b3b 100644 --- a/src/ui/dashboard/DashboardTab.tsx +++ b/src/ui/dashboard/DashboardTab.tsx @@ -403,7 +403,7 @@ function HostStatusCard({ return (
onOpenTab(host, "stats")} + onClick={() => onOpenTab(host, "host-metrics")} className="flex items-center justify-between px-4 py-2.5 border-b border-border last:border-0 hover:bg-muted/50 cursor-pointer" >
@@ -508,7 +508,7 @@ function RecentActivityCard({ const typeToTab: Record = { terminal: "terminal", file_manager: "files", - server_stats: "stats", + server_stats: "host-metrics", tunnel: "tunnel", docker: "docker", rdp: "rdp", @@ -1120,7 +1120,11 @@ export function DashboardTab({ getCredentials() .then((res) => setCredentialCount( - Array.isArray(res?.credentials) ? res.credentials.length : 0, + Array.isArray(res) + ? res.length + : Array.isArray(res?.credentials) + ? res.credentials.length + : 0, ), ) .catch(() => {}); @@ -1354,6 +1358,20 @@ export function DashboardTab({ {t("dashboard.discord")} +
{allSlots.map((slot) => ( @@ -1475,6 +1493,20 @@ export function DashboardTab({ {t("dashboard.discord")} + {editMode ? ( <> diff --git a/src/ui/dashboard/cards/NetworkGraphCard.tsx b/src/ui/dashboard/cards/NetworkGraphCard.tsx index 7c29319c..9505dab7 100644 --- a/src/ui/dashboard/cards/NetworkGraphCard.tsx +++ b/src/ui/dashboard/cards/NetworkGraphCard.tsx @@ -43,6 +43,7 @@ import { RotateCw, AlertCircle, Download, + ExternalLink, Upload, Link2, FolderPlus, @@ -806,11 +807,11 @@ export function NetworkGraphCard({ )} {!embedded && ( <> @@ -1303,6 +1304,16 @@ export function NetworkGraphCard({
+ {cytoscapeEl} {dialogs} @@ -1355,6 +1366,15 @@ export function NetworkGraphCard({ > + + +
{cytoscapeEl} diff --git a/src/ui/dashboard/cards/QuickActionsCard.tsx b/src/ui/dashboard/cards/QuickActionsCard.tsx deleted file mode 100644 index 1d2ce32b..00000000 --- a/src/ui/dashboard/cards/QuickActionsCard.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { FastForward, Server, Key, Settings, User } from "lucide-react"; -import { Button } from "@/components/button"; - -interface QuickActionsCardProps { - isAdmin: boolean; - onAddHost: () => void; - onAddCredential: () => void; - onOpenAdminSettings: () => void; - onOpenUserProfile: () => void; -} - -export function QuickActionsCard({ - isAdmin, - onAddHost, - onAddCredential, - onOpenAdminSettings, - onOpenUserProfile, -}: QuickActionsCardProps): React.ReactElement { - const { t } = useTranslation(); - - return ( -
-
-

- - {t("dashboard.quickActions")} -

-
- - - {isAdmin && ( - - )} - -
-
-
- ); -} diff --git a/src/ui/dashboard/cards/RecentActivityCard.tsx b/src/ui/dashboard/cards/RecentActivityCard.tsx deleted file mode 100644 index 48731fcc..00000000 --- a/src/ui/dashboard/cards/RecentActivityCard.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { - Clock, - Loader2, - Terminal, - FolderOpen, - Server, - ArrowDownUp, - Container, - Monitor, - Eye, - MessagesSquare, -} from "lucide-react"; -import { Button } from "@/components/button"; -import { type RecentActivityItem } from "@/main-axios"; - -interface RecentActivityCardProps { - activities: RecentActivityItem[]; - loading: boolean; - onReset: () => void; - onActivityClick: (item: RecentActivityItem) => void; -} - -function formatRelativeTime( - timestamp: string, - t: (key: string) => string, -): string { - const diffMs = Date.now() - new Date(timestamp).getTime(); - if (diffMs < 0) return t("dashboard.justNow"); - const diffSec = Math.floor(diffMs / 1000); - if (diffSec < 60) return t("dashboard.justNow"); - const diffMin = Math.floor(diffSec / 60); - if (diffMin < 60) return `${diffMin}m`; - const diffHour = Math.floor(diffMin / 60); - if (diffHour < 24) return `${diffHour}h`; - return `${Math.floor(diffHour / 24)}d`; -} - -export function RecentActivityCard({ - activities, - loading, - onReset, - onActivityClick, -}: RecentActivityCardProps): React.ReactElement { - const { t } = useTranslation(); - - return ( -
-
-
-

- - {t("dashboard.recentActivity")} -

- -
-
- {loading ? ( -
- - {t("dashboard.loadingRecentActivity")} -
- ) : activities.length === 0 ? ( -

- {t("dashboard.noRecentActivity")} -

- ) : ( - activities - .filter((item, index, array) => { - if (index === 0) return true; - - const prevItem = array[index - 1]; - return !( - item.hostId === prevItem.hostId && item.type === prevItem.type - ); - }) - .map((item) => ( - - )) - )} -
-
-
- ); -} diff --git a/src/ui/dashboard/cards/ServerOverviewCard.tsx b/src/ui/dashboard/cards/ServerOverviewCard.tsx deleted file mode 100644 index 019b3d95..00000000 --- a/src/ui/dashboard/cards/ServerOverviewCard.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { - Server, - History, - Clock, - Database, - Key, - ArrowDownUp, -} from "lucide-react"; -import { Button } from "@/components/button"; -import { UpdateLog } from "@/dashboard/panels/UpdateLog"; - -interface ServerOverviewCardProps { - loggedIn: boolean; - versionText: string; - versionStatus: "up_to_date" | "requires_update" | "beta"; - uptime: string; - dbHealth: "healthy" | "error"; - totalServers: number; - totalTunnels: number; - totalCredentials: number; - updateCheckDisabled?: boolean; -} - -export function ServerOverviewCard({ - loggedIn, - versionText, - versionStatus, - uptime, - dbHealth, - totalServers, - totalTunnels, - totalCredentials, - updateCheckDisabled = false, -}: ServerOverviewCardProps): React.ReactElement { - const { t } = useTranslation(); - - return ( -
-
-

- - {t("dashboard.serverOverview")} -

-
-
-
- -

- {t("dashboard.version")} -

-
- -
-

- {versionText} -

- {versionStatus === "beta" ? ( - - ) : !updateCheckDisabled ? ( - <> - - - - ) : null} -
-
- -
-
- -

- {t("dashboard.uptime")} -

-
- -
-

{uptime}

-
-
- -
-
- -

- {t("dashboard.database")} -

-
- -
-

- {dbHealth === "healthy" - ? t("dashboard.healthy") - : t("dashboard.error")} -

-
-
-
-
-
-
- -

- {t("dashboard.totalHosts")} -

-
-

- {totalServers} -

-
-
-
- -

- {t("dashboard.totalTunnels")} -

-
-

- {totalTunnels} -

-
-
-
-
-
- -

- {t("dashboard.totalCredentials")} -

-
-

- {totalCredentials} -

-
-
-
-
- ); -} diff --git a/src/ui/dashboard/cards/ServerStatsCard.tsx b/src/ui/dashboard/cards/ServerStatsCard.tsx deleted file mode 100644 index c95dcf5c..00000000 --- a/src/ui/dashboard/cards/ServerStatsCard.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { ChartLine, Loader2, Server } from "lucide-react"; -import { Button } from "@/components/button"; - -interface ServerStat { - id: number; - name: string; - cpu: number | null; - ram: number | null; -} - -interface ServerStatsCardProps { - serverStats: ServerStat[]; - loading: boolean; - onServerClick: (serverId: number, serverName: string) => void; -} - -export function ServerStatsCard({ - serverStats, - loading, - onServerClick, -}: ServerStatsCardProps): React.ReactElement { - const { t } = useTranslation(); - - const visibleStats = serverStats.filter( - (s) => s.cpu !== null || s.ram !== null, - ); - - return ( -
-
-

- - {t("dashboard.serverStats")} -

-
- {loading ? ( -
- - {t("dashboard.loadingServerStats")} -
- ) : visibleStats.length === 0 ? ( -

- {t("dashboard.noServerData")} -

- ) : ( - visibleStats.map((server) => ( - - )) - )} -
-
-
- ); -} diff --git a/src/ui/dashboard/components/DashboardSettingsDialog.tsx b/src/ui/dashboard/components/DashboardSettingsDialog.tsx deleted file mode 100644 index 8e89f81b..00000000 --- a/src/ui/dashboard/components/DashboardSettingsDialog.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/dialog"; -import { Button } from "@/components/button"; -import { Label } from "@/components/label"; -import { Checkbox } from "@/components/checkbox"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/select"; -import { useTranslation } from "react-i18next"; -import type { DashboardLayout } from "@/main-axios"; - -interface DashboardSettingsDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - currentLayout: DashboardLayout; - onSave: (layout: DashboardLayout) => void; - onReset: () => void; -} - -export function DashboardSettingsDialog({ - open, - onOpenChange, - currentLayout, - onSave, - onReset, -}: DashboardSettingsDialogProps): React.ReactElement { - const { t } = useTranslation(); - const [layout, setLayout] = useState(currentLayout); - - useEffect(() => { - setLayout(currentLayout); - }, [currentLayout, open]); - - const handleCardToggle = (cardId: string, enabled: boolean) => { - setLayout((prev) => ({ - ...prev, - cards: prev.cards.map((card) => - card.id === cardId ? { ...card, enabled } : card, - ), - })); - }; - - const handleCardPanel = (cardId: string, panel: "main" | "side") => { - setLayout((prev) => ({ - ...prev, - cards: prev.cards.map((card) => - card.id === cardId ? { ...card, panel } : card, - ), - })); - }; - - const handleSave = () => { - onSave(layout); - onOpenChange(false); - }; - - const handleReset = () => { - onReset(); - onOpenChange(false); - }; - - const cardLabels: Record = { - server_overview: t("dashboard.serverOverviewCard"), - recent_activity: t("dashboard.recentActivityCard"), - network_graph: t("dashboard.networkGraphCard"), - quick_actions: t("dashboard.quickActionsCard"), - server_stats: t("dashboard.serverStatsCard"), - }; - - return ( - - - - {t("dashboard.dashboardSettings")} - - {t("dashboard.customizeLayout")} - - - -
-
- -
- {layout.cards?.map((card) => ( -
- - handleCardToggle(card.id, checked === true) - } - /> - - -
- ))} -
-
-
- - - - - - -
-
- ); -} diff --git a/src/ui/dashboard/hooks/useDashboardPreferences.ts b/src/ui/dashboard/hooks/useDashboardPreferences.ts deleted file mode 100644 index 7860e227..00000000 --- a/src/ui/dashboard/hooks/useDashboardPreferences.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { useState, useEffect, useCallback } from "react"; -import { - getDashboardPreferences, - saveDashboardPreferences, - type DashboardLayout, -} from "@/main-axios"; - -const LS_KEY = "dashboardLayout"; - -const DEFAULT_LAYOUT: DashboardLayout = { - cards: [ - { id: "server_overview", enabled: true, order: 1, panel: "main" }, - { id: "quick_actions", enabled: true, order: 2, panel: "main" }, - { id: "server_stats", enabled: true, order: 3, panel: "main" }, - { id: "network_graph", enabled: false, order: 4, panel: "main" }, - { id: "recent_activity", enabled: true, order: 1, panel: "side" }, - ], - mainWidthPct: 68, -}; - -function migrateLayout(preferences: DashboardLayout): DashboardLayout { - const needsMigration = preferences.cards.some((c) => !c.panel); - if (!needsMigration) return preferences; - const defaultCardMap = new Map(DEFAULT_LAYOUT.cards.map((c) => [c.id, c])); - return { - ...preferences, - mainWidthPct: preferences.mainWidthPct ?? DEFAULT_LAYOUT.mainWidthPct, - cards: preferences.cards.map((c) => ({ - ...c, - panel: c.panel ?? defaultCardMap.get(c.id)?.panel ?? "main", - })), - }; -} - -function readFromLocalStorage(): DashboardLayout | null { - try { - const raw = localStorage.getItem(LS_KEY); - if (!raw) return null; - const parsed = JSON.parse(raw); - if (parsed?.cards && Array.isArray(parsed.cards)) { - return migrateLayout(parsed); - } - } catch { - // ignore - } - return null; -} - -function writeToLocalStorage(layout: DashboardLayout) { - try { - localStorage.setItem(LS_KEY, JSON.stringify(layout)); - } catch { - // ignore - } -} - -export function useDashboardPreferences(enabled: boolean = true) { - const [layout, setLayout] = useState(null); - const [loading, setLoading] = useState(true); - const [saveTimeout, setSaveTimeout] = useState(null); - - useEffect(() => { - if (!enabled) { - setLayout(DEFAULT_LAYOUT); - setLoading(false); - return; - } - - // Show cached layout immediately so the UI doesn't wait for the network - const cached = readFromLocalStorage(); - if (cached) { - setLayout(cached); - setLoading(false); - } - - const fetchPreferences = async () => { - try { - const preferences = await getDashboardPreferences(); - if (preferences?.cards && Array.isArray(preferences.cards)) { - const migrated = migrateLayout(preferences); - setLayout(migrated); - writeToLocalStorage(migrated); - } else { - if (!cached) { - setLayout(DEFAULT_LAYOUT); - } - } - } catch { - if (!cached) { - setLayout(DEFAULT_LAYOUT); - } - } finally { - setLoading(false); - } - }; - - fetchPreferences(); - }, [enabled]); - - const updateLayout = useCallback( - (newLayout: DashboardLayout) => { - setLayout(newLayout); - writeToLocalStorage(newLayout); - - if (saveTimeout) { - clearTimeout(saveTimeout); - } - - const timeout = setTimeout(async () => { - try { - await saveDashboardPreferences(newLayout); - } catch (error) { - console.error("Failed to save dashboard preferences:", error); - } - }, 1000); - - setSaveTimeout(timeout); - }, - [saveTimeout], - ); - - const resetLayout = useCallback(async () => { - setLayout(DEFAULT_LAYOUT); - writeToLocalStorage(DEFAULT_LAYOUT); - try { - await saveDashboardPreferences(DEFAULT_LAYOUT); - } catch (error) { - console.error("Failed to reset dashboard preferences:", error); - } - }, []); - - return { - layout, - loading, - updateLayout, - resetLayout, - }; -} diff --git a/src/ui/features/FullScreenAppWrapper.tsx b/src/ui/features/FullScreenAppWrapper.tsx index c839081e..8d658204 100644 --- a/src/ui/features/FullScreenAppWrapper.tsx +++ b/src/ui/features/FullScreenAppWrapper.tsx @@ -4,7 +4,7 @@ import { CommandHistoryProvider } from "@/features/terminal/command-history/Comm import { SidebarProvider } from "@/components/sidebar.tsx"; import { getSSHHosts, getUserInfo } from "@/main-axios.ts"; import type { SSHHost } from "@/types"; -import { Dashboard } from "@/dashboard/Dashboard.tsx"; +import { LoginScreen } from "@/auth/LoginScreen.tsx"; import { Toaster } from "@/components/sonner.tsx"; import { dbHealthMonitor } from "@/lib/db-health-monitor.ts"; import { useTranslation } from "react-i18next"; @@ -110,12 +110,9 @@ export const FullScreenAppWrapper: React.FC = ({ className="w-full h-screen overflow-hidden flex items-center justify-center" style={{ backgroundColor: "var(--bg-base)" }} > - {}} /> + + +
diff --git a/src/ui/features/docker/components/ConsoleTerminal.tsx b/src/ui/features/docker/components/ConsoleTerminal.tsx index 7d2104ef..fdd41335 100644 --- a/src/ui/features/docker/components/ConsoleTerminal.tsx +++ b/src/ui/features/docker/components/ConsoleTerminal.tsx @@ -3,6 +3,7 @@ import { useXTerm } from "react-xtermjs"; import { FitAddon } from "@xterm/addon-fit"; import { ClipboardAddon } from "@xterm/addon-clipboard"; import { RobustClipboardProvider } from "@/lib/clipboard-provider"; +import { copyToClipboard } from "@/lib/clipboard"; import { WebLinksAddon } from "@xterm/addon-web-links"; import { Button } from "@/components/button.tsx"; import { @@ -20,11 +21,8 @@ import type { SSHHost } from "@/types"; import { isElectron } from "@/main-axios.ts"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; import { useTranslation } from "react-i18next"; -import { - TERMINAL_THEMES, - DEFAULT_TERMINAL_CONFIG, - TERMINAL_FONTS, -} from "@/lib/terminal-themes"; +import { resolveTermixThemeColors } from "@/features/terminal/terminal-theme"; +import { DEFAULT_TERMINAL_CONFIG, TERMINAL_FONTS } from "@/lib/terminal-themes"; import { useTheme } from "@/components/theme-provider"; interface ConsoleTerminalProps { @@ -49,26 +47,10 @@ export function ConsoleTerminal({ [hostConfig.terminalConfig], ); - const isDarkMode = - appTheme === "dark" || - appTheme === "dracula" || - appTheme === "gentlemansChoice" || - appTheme === "midnightEspresso" || - appTheme === "catppuccinMocha" || - (appTheme === "system" && - window.matchMedia("(prefers-color-scheme: dark)").matches); - const themeColors = React.useMemo(() => { const activeTheme = terminalConfig.theme; - if (activeTheme === "termix") { - return isDarkMode - ? TERMINAL_THEMES.termixDark.colors - : TERMINAL_THEMES.termixLight.colors; - } - return ( - TERMINAL_THEMES[activeTheme]?.colors ?? TERMINAL_THEMES.termixDark.colors - ); - }, [terminalConfig.theme, isDarkMode]); + return resolveTermixThemeColors(activeTheme, appTheme); + }, [terminalConfig.theme, appTheme]); const [isConnected, setIsConnected] = React.useState(false); const [isConnecting, setIsConnecting] = React.useState(false); @@ -108,15 +90,11 @@ export function ConsoleTerminal({ if (window.electronClipboard) { return window.electronClipboard.readText(); } - return navigator.clipboard.readText(); + return (await navigator.clipboard?.readText?.()) ?? ""; }; const writeTextToClipboard = async (text: string): Promise => { - if (window.electronClipboard) { - await window.electronClipboard.writeText(text); - return; - } - await navigator.clipboard.writeText(text); + await copyToClipboard(text); }; terminal.attachCustomKeyEventHandler((e: KeyboardEvent): boolean => { diff --git a/src/ui/features/file-manager/FileManager.tsx b/src/ui/features/file-manager/FileManager.tsx index 540c8f3a..432f205d 100644 --- a/src/ui/features/file-manager/FileManager.tsx +++ b/src/ui/features/file-manager/FileManager.tsx @@ -34,6 +34,7 @@ import { } from "@/ssh/connection-log/ConnectionLogContext.tsx"; import { ConnectionLog } from "@/ssh/connection-log/ConnectionLog.tsx"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; +import { copyToClipboard } from "@/lib/clipboard.ts"; import { listSSHFiles, resolveSSHPath, @@ -1343,19 +1344,17 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { const paths = files.map((file) => file.path).join("\n"); - navigator.clipboard.writeText(paths).then( - () => { + copyToClipboard(paths).then((ok) => { + if (ok) { toast.success( files.length === 1 ? t("fileManager.pathCopiedToClipboard") : t("fileManager.pathsCopiedToClipboard", { count: files.length }), ); - }, - (err) => { - console.error("Failed to copy path to clipboard:", err); + } else { toast.error(t("fileManager.failedToCopyPath")); - }, - ); + } + }); } async function handlePasteFiles() { diff --git a/src/ui/features/file-manager/FileManagerContextMenu.tsx b/src/ui/features/file-manager/FileManagerContextMenu.tsx index 6ab99fd3..5dd9d86b 100644 --- a/src/ui/features/file-manager/FileManagerContextMenu.tsx +++ b/src/ui/features/file-manager/FileManagerContextMenu.tsx @@ -192,6 +192,8 @@ export function FileManagerContextMenu({ adjustedX = viewportWidth - menuWidth - 10; if (y + menuHeight > viewportHeight) adjustedY = Math.max(10, viewportHeight - menuHeight - 10); + adjustedX = Math.max(8, adjustedX); + adjustedY = Math.max(8, adjustedY); setMenuPosition({ x: adjustedX, y: adjustedY }); }, [isVisible, x, y, files.length]); diff --git a/src/ui/features/file-manager/FileManagerGrid.tsx b/src/ui/features/file-manager/FileManagerGrid.tsx index 8ca6ca91..6656220b 100644 --- a/src/ui/features/file-manager/FileManagerGrid.tsx +++ b/src/ui/features/file-manager/FileManagerGrid.tsx @@ -632,6 +632,18 @@ export function FileManagerGrid({ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { + // Every open file-manager tab mounts its own grid with a global keydown + // listener, so without this guard a shortcut (notably Delete) would fire + // in every tab at once - including hidden ones - and act on their + // selections. Only the visible grid should respond. See issue #783. + const grid = gridRef.current; + if (!grid) return; + const isVisible = + typeof grid.checkVisibility === "function" + ? grid.checkVisibility({ visibilityProperty: true }) + : grid.offsetParent !== null; + if (!isVisible) return; + const activeElement = document.activeElement; if ( activeElement && diff --git a/src/ui/features/guacamole/GuacamoleApp.tsx b/src/ui/features/guacamole/GuacamoleApp.tsx index d33ced03..43a6e328 100644 --- a/src/ui/features/guacamole/GuacamoleApp.tsx +++ b/src/ui/features/guacamole/GuacamoleApp.tsx @@ -3,8 +3,11 @@ import { GuacamoleDisplay, type GuacamoleDisplayHandle, } from "@/features/guacamole/GuacamoleDisplay.tsx"; -import { FullScreenAppWrapper } from "@/features/FullScreenAppWrapper.tsx"; -import { getGuacamoleTokenFromHost, getGuacdStatus } from "@/main-axios.ts"; +import { + getGuacamoleTokenFromHost, + getGuacdStatus, + getSSHHosts, +} from "@/main-axios.ts"; import { useTranslation } from "react-i18next"; import { AlertCircle, RefreshCw } from "lucide-react"; import { GuacamoleToolbar } from "@/features/guacamole/GuacamoleToolbar.tsx"; @@ -24,68 +27,58 @@ const GuacamoleApp: React.FC = ({ protocol, }) => { const { t } = useTranslation(); + const [hostConfig, setHostConfig] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!hostId) { + setLoading(false); + return; + } + getSSHHosts() + .then((hosts) => { + const host = hosts.find((h) => h.id === parseInt(hostId, 10)); + setHostConfig(host ?? null); + }) + .catch(() => setHostConfig(null)) + .finally(() => setLoading(false)); + }, [hostId]); + + if (loading) { + return ( +
+ +
+ ); + } + + if (!hostConfig || !hostId) { + return ( +
+ + + {t("guacamole.hostNotFound")} + +
+ ); + } return ( - - {(hostConfig, loading) => { - if (loading) { - return ( -
- -
- ); - } - - if (!hostConfig) { - return ( -
- - - {t("guacamole.hostNotFound")} - -
- ); - } - - if (!hostId) { - return ( -
- - - {t("guacamole.hostNotFound")} - -
- ); - } - - return ( - - ); - }} -
+ ); }; diff --git a/src/ui/features/guacamole/GuacamoleDisplay.tsx b/src/ui/features/guacamole/GuacamoleDisplay.tsx index 57ecdba2..212ad887 100644 --- a/src/ui/features/guacamole/GuacamoleDisplay.tsx +++ b/src/ui/features/guacamole/GuacamoleDisplay.tsx @@ -275,9 +275,27 @@ export const GuacamoleDisplay = forwardRef< requestAnimationFrame(() => requestAnimationFrame(() => resolve())), ); - const rect = containerRef.current?.getBoundingClientRect(); - let containerWidth = rect?.width || 0; - let containerHeight = rect?.height || 0; + // The tab's DOM node can still be display:none (and report 0x0) when this + // tab is restored in the background. Measuring then would force the + // window-size fallback, which ignores the tab bar and makes the remote + // resolution too tall (the bottom gets cut off). Poll briefly for a real + // size before connecting so we capture the actual visible viewport. + const measureContainer = () => { + const rect = containerRef.current?.getBoundingClientRect(); + return { width: rect?.width || 0, height: rect?.height || 0 }; + }; + + let { width: containerWidth, height: containerHeight } = measureContainer(); + for ( + let attempt = 0; + (containerWidth < 100 || containerHeight < 100) && attempt < 40; + attempt++ + ) { + await new Promise((resolve) => + requestAnimationFrame(() => resolve()), + ); + ({ width: containerWidth, height: containerHeight } = measureContainer()); + } if (containerWidth < 100 || containerHeight < 100) { containerWidth = window.innerWidth || 1280; diff --git a/src/ui/features/server-stats/ServerStatsApp.tsx b/src/ui/features/host-metrics/HostMetricsApp.tsx similarity index 65% rename from src/ui/features/server-stats/ServerStatsApp.tsx rename to src/ui/features/host-metrics/HostMetricsApp.tsx index 3b553d60..164728f7 100644 --- a/src/ui/features/server-stats/ServerStatsApp.tsx +++ b/src/ui/features/host-metrics/HostMetricsApp.tsx @@ -1,22 +1,22 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { ServerStats } from "@/features/server-stats/ServerStats.tsx"; +import { HostMetricsTab } from "@/features/host-metrics/HostMetricsTab.tsx"; import { FullScreenAppWrapper } from "@/features/FullScreenAppWrapper.tsx"; -interface ServerStatsAppProps { +interface HostMetricsAppProps { hostId?: string; } -const ServerStatsApp: React.FC = ({ hostId }) => { +const HostMetricsApp: React.FC = ({ hostId }) => { const { t } = useTranslation(); return ( {(hostConfig, loading) => { if (loading) { return ( -
+
-
+

{t("hosts.loadingHost")}

@@ -27,16 +27,16 @@ const ServerStatsApp: React.FC = ({ hostId }) => { if (!hostConfig) { return ( -
+
-

{t("hosts.hostNotFound")}

+

{t("hosts.hostNotFound")}

); } return ( - = ({ hostId }) => { ); }; -export default ServerStatsApp; +export default HostMetricsApp; diff --git a/src/ui/features/server-stats/ServerStats.tsx b/src/ui/features/host-metrics/HostMetricsTab.tsx similarity index 56% rename from src/ui/features/server-stats/ServerStats.tsx rename to src/ui/features/host-metrics/HostMetricsTab.tsx index 55fa555f..64e007fb 100644 --- a/src/ui/features/server-stats/ServerStats.tsx +++ b/src/ui/features/host-metrics/HostMetricsTab.tsx @@ -19,30 +19,37 @@ import { useTabsSafe } from "@/shell/TabContext.tsx"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { - type WidgetType, type StatsConfig, DEFAULT_STATS_CONFIG, } from "@/types/stats-widgets.ts"; import { - CpuWidget, - MemoryWidget, - DiskWidget, - NetworkWidget, - UptimeWidget, - ProcessesWidget, - SystemWidget, - LoginStatsWidget, - PortsWidget, - FirewallWidget, -} from "./widgets"; + defaultLayoutFromWidgets, + type HostMetricsLayout, +} from "@/types/host-metrics.ts"; import { SimpleLoader } from "@/lib/SimpleLoader.tsx"; -import { RefreshCw, Server } from "lucide-react"; +import { RefreshCw, Server, LayoutDashboard } from "lucide-react"; import { ConnectionLogProvider, useConnectionLog, } from "@/ssh/connection-log/ConnectionLogContext.tsx"; import { ConnectionLog } from "@/ssh/connection-log/ConnectionLog.tsx"; import type { LogEntry } from "@/types/connection-log.ts"; +import { + CardGridCanvas, + ColumnCountStepper, +} from "@/components/card-grid/CardGridCanvas.tsx"; +import type { GridCardCatalogEntry } from "@/components/card-grid/types.ts"; +import { useHostMetricsPreferences } from "./hooks/useHostMetricsPreferences.ts"; +import { + CARD_DEFINITIONS, + IMPLEMENTED_CARD_IDS, + getCardDefinition, + defaultColSpanFor, + defaultHeightFor, + type MetricCardHistories, +} from "./cards"; + +const HISTORY_LEN = 30; interface QuickAction { name: string; @@ -50,7 +57,6 @@ interface QuickAction { } type ConnectionLogPayload = Omit; - type ConnectionLogError = Error & { connectionLogs?: ConnectionLogPayload[]; }; @@ -60,15 +66,14 @@ interface HostConfig { name: string; ip: string; username: string; - folder?: string; - enableFileManager?: boolean; - tunnelConnections?: unknown[]; quickActions?: QuickAction[]; statsConfig?: string | StatsConfig; + authType?: string; + port?: number; [key: string]: unknown; } -interface ServerProps { +interface HostMetricsProps { hostConfig?: HostConfig; title?: string; isVisible?: boolean; @@ -76,13 +81,23 @@ interface ServerProps { embedded?: boolean; } -function ServerStatsInner({ +function parseStatsConfig(raw?: string | StatsConfig): StatsConfig { + if (!raw) return DEFAULT_STATS_CONFIG; + try { + const parsed = typeof raw === "string" ? JSON.parse(raw) : raw; + return { ...DEFAULT_STATS_CONFIG, ...parsed }; + } catch { + return DEFAULT_STATS_CONFIG; + } +} + +function HostMetricsInner({ hostConfig, title, isVisible = true, isTopbarOpen = true, embedded = false, -}: ServerProps): React.ReactElement { +}: HostMetricsProps): React.ReactElement { const { t } = useTranslation(); const { addLog, @@ -90,17 +105,19 @@ function ServerStatsInner({ isExpanded: isConnectionLogExpanded, } = useConnectionLog(); const { currentTab, removeTab } = useTabsSafe(); + const [serverStatus, setServerStatus] = React.useState<"online" | "offline">( "offline", ); const [metrics, setMetrics] = React.useState(null); - const [metricsHistory, setMetricsHistory] = React.useState( - [], - ); + const [histories, setHistories] = React.useState({ + cpu: [], + memory: [], + disk: [], + }); const [currentHostConfig, setCurrentHostConfig] = React.useState(hostConfig); const [isLoadingMetrics, setIsLoadingMetrics] = React.useState(false); const [isRefreshing, setIsRefreshing] = React.useState(false); - const [showStatsUI, setShowStatsUI] = React.useState(true); const [executingActions, setExecutingActions] = React.useState>( new Set(), ); @@ -113,61 +130,81 @@ function ServerStatsInner({ null, ); const [hasConnectionError, setHasConnectionError] = React.useState(false); + const [editMode, setEditMode] = React.useState(false); const activityLoggedRef = React.useRef(false); const activityLoggingRef = React.useRef(false); - const statsConfig = React.useMemo((): StatsConfig => { - if (!currentHostConfig?.statsConfig) { - return DEFAULT_STATS_CONFIG; - } - try { - const parsed = - typeof currentHostConfig.statsConfig === "string" - ? JSON.parse(currentHostConfig.statsConfig) - : currentHostConfig.statsConfig; - return { ...DEFAULT_STATS_CONFIG, ...parsed }; - } catch (error) { - console.error("Failed to parse statsConfig:", error); - return DEFAULT_STATS_CONFIG; - } - }, [currentHostConfig?.statsConfig]); - - const enabledWidgets = statsConfig.enabledWidgets; - const statusCheckEnabled = statsConfig.statusCheckEnabled !== false; + const statsConfig = React.useMemo( + () => parseStatsConfig(currentHostConfig?.statsConfig), + [currentHostConfig?.statsConfig], + ); const metricsEnabled = statsConfig.metricsEnabled !== false; + const statusCheckEnabled = statsConfig.statusCheckEnabled !== false; + + const hostId = currentHostConfig?.id ?? null; + const { layout, setLayout } = useHostMetricsPreferences(hostId); + + const effectiveLayout: HostMetricsLayout = React.useMemo(() => { + if (layout) return layout; + return defaultLayoutFromWidgets(statsConfig.enabledWidgets ?? []); + }, [layout, statsConfig.enabledWidgets]); + + // Only render/keep cards that are implemented (metric cards in Phase A). + const visibleSlots = React.useMemo( + () => effectiveLayout.slots.filter((s) => getCardDefinition(s.id)), + [effectiveLayout.slots], + ); + + const cardCatalog: GridCardCatalogEntry[] = React.useMemo( + () => + IMPLEMENTED_CARD_IDS.map((id) => ({ + id, + label: t(CARD_DEFINITIONS[id].labelKey), + defaultColSpan: defaultColSpanFor(id), + defaultHeight: defaultHeightFor(id), + })), + [t], + ); + + const cardLabel = React.useCallback( + (id: string) => { + const def = getCardDefinition(id); + return def ? t(def.labelKey) : id; + }, + [t], + ); + + const renderCard = React.useCallback( + (id: string) => { + const def = getCardDefinition(id); + if (!def) return null; + return def.render({ metrics, histories, hostId }); + }, + [metrics, histories, hostId], + ); React.useEffect(() => { - const handleVisibilityChange = () => { - setIsPageVisible(!document.hidden); - }; - document.addEventListener("visibilitychange", handleVisibilityChange); - return () => - document.removeEventListener("visibilitychange", handleVisibilityChange); + const onVis = () => setIsPageVisible(!document.hidden); + document.addEventListener("visibilitychange", onVis); + return () => document.removeEventListener("visibilitychange", onVis); }, []); const isActuallyVisible = isVisible && isPageVisible; React.useEffect(() => { if (!viewerSessionId || !isActuallyVisible) return; - - const heartbeatInterval = setInterval(async () => { - try { - await sendMetricsHeartbeat(viewerSessionId); - } catch (error) { - console.error("Failed to send heartbeat:", error); - } + const interval = setInterval(() => { + sendMetricsHeartbeat(viewerSessionId).catch(() => {}); }, 30000); - - return () => clearInterval(heartbeatInterval); + return () => clearInterval(interval); }, [viewerSessionId, isActuallyVisible]); React.useEffect(() => { if (hostConfig?.id !== currentHostConfig?.id) { setServerStatus("offline"); setMetrics(null); - setMetricsHistory([]); - setShowStatsUI(true); + setHistories({ cpu: [], memory: [], disk: [] }); } setCurrentHostConfig(hostConfig); }, [hostConfig?.id]); @@ -180,146 +217,72 @@ function ServerStatsInner({ ) { return; } - activityLoggingRef.current = true; activityLoggedRef.current = true; - try { const hostName = currentHostConfig.name || `${currentHostConfig.username}@${currentHostConfig.ip}`; await logActivity("server_stats", currentHostConfig.id, hostName); - } catch (err) { - console.warn("Failed to log server stats activity:", err); + } catch { activityLoggedRef.current = false; } finally { activityLoggingRef.current = false; } }; + const pushHistory = React.useCallback((data: ServerMetrics) => { + setHistories((prev) => { + const add = (arr: number[], v: number | null | undefined) => + [...arr, v ?? 0].slice(-HISTORY_LEN); + return { + cpu: add(prev.cpu, data.cpu?.percent), + memory: add(prev.memory, data.memory?.percent), + disk: add(prev.disk, data.disk?.percent), + }; + }); + }, []); + const handleTOTPSubmit = async (totpCode: string) => { if (!totpSessionId || !currentHostConfig) return; - try { const result = await submitMetricsTOTP(totpSessionId, totpCode); if (result.success) { setTotpRequired(false); setTotpSessionId(null); - setShowStatsUI(true); setTotpVerified(true); - if (result.viewerSessionId) { - setViewerSessionId(result.viewerSessionId); - } + if (result.viewerSessionId) setViewerSessionId(result.viewerSessionId); } else { - toast.error(t("serverStats.totpFailed")); + toast.error(t("hostMetrics.totpFailed")); } - } catch (error) { - toast.error(t("serverStats.totpFailed")); - console.error("TOTP verification failed:", error); + } catch { + toast.error(t("hostMetrics.totpFailed")); } }; const handleTOTPCancel = async () => { setTotpRequired(false); if (currentHostConfig?.id) { - try { - await stopMetricsPolling(currentHostConfig.id); - } catch (error) { - console.error("Failed to stop metrics polling:", error); - } - } - if (currentTab !== null) { - removeTab(currentTab); - } - }; - - const renderWidget = (widgetType: WidgetType) => { - switch (widgetType) { - case "cpu": - return ; - - case "memory": - return ( - - ); - - case "disk": - return ; - - case "network": - return ( - - ); - - case "uptime": - return ( - - ); - - case "processes": - return ( - - ); - - case "system": - return ( - - ); - - case "login_stats": - return ( - - ); - - case "ports": - return ( - - ); - - case "firewall": - return ( - - ); - - default: - return null; + await stopMetricsPolling(currentHostConfig.id).catch(() => {}); } + if (currentTab !== null) removeTab(currentTab); }; React.useEffect(() => { - const fetchLatestHostConfig = async () => { - if (hostConfig?.id) { - try { - const hosts = await getSSHHosts(); - const updatedHost = hosts.find((h) => h.id === hostConfig.id); - if (updatedHost) { - setCurrentHostConfig(updatedHost); - } - } catch { - toast.error(t("serverStats.failedToFetchHostConfig")); - } + const fetchLatest = async () => { + if (!hostConfig?.id) return; + try { + const hosts = await getSSHHosts(); + const updated = hosts.find((h) => h.id === hostConfig.id); + if (updated) setCurrentHostConfig(updated as unknown as HostConfig); + } catch { + toast.error(t("hostMetrics.failedToFetchHostConfig")); } }; - - fetchLatestHostConfig(); - - const handleHostsChanged = async () => { - if (hostConfig?.id) { - try { - const hosts = await getSSHHosts(); - const updatedHost = hosts.find((h) => h.id === hostConfig.id); - if (updatedHost) { - setCurrentHostConfig(updatedHost); - } - } catch { - toast.error(t("serverStats.failedToFetchHostConfig")); - } - } - }; - - window.addEventListener("ssh-hosts:changed", handleHostsChanged); - return () => - window.removeEventListener("ssh-hosts:changed", handleHostsChanged); + fetchLatest(); + const onChanged = () => fetchLatest(); + window.addEventListener("ssh-hosts:changed", onChanged); + return () => window.removeEventListener("ssh-hosts:changed", onChanged); }, [hostConfig?.id]); React.useEffect(() => { @@ -327,41 +290,25 @@ function ServerStatsInner({ setServerStatus("offline"); return; } - let cancelled = false; const fetchStatus = async () => { try { - const res = await getServerStatusById(currentHostConfig?.id); + const res = await getServerStatusById(currentHostConfig.id); if (!cancelled) { setServerStatus(res?.status === "online" ? "online" : "offline"); } - } catch (error: unknown) { - if (!cancelled) { - const err = error as { - response?: { status?: number }; - }; - if (err?.response?.status === 503) { - setServerStatus("offline"); - } else if (err?.response?.status === 504) { - setServerStatus("offline"); - } else if (err?.response?.status === 404) { - setServerStatus("offline"); - } else { - setServerStatus("offline"); - } - } + } catch { + if (!cancelled) setServerStatus("offline"); } }; - fetchStatus(); - const intervalId = window.setInterval( + const id = window.setInterval( fetchStatus, statsConfig.statusCheckInterval * 1000, ); - return () => { cancelled = true; - window.clearInterval(intervalId); + window.clearInterval(id); }; }, [ currentHostConfig?.id, @@ -370,37 +317,24 @@ function ServerStatsInner({ ]); React.useEffect(() => { - if (!metricsEnabled || !currentHostConfig?.id) { - return; - } + if (!metricsEnabled || !currentHostConfig?.id) return; let cancelled = false; - let pollingIntervalId: number | undefined; - if (isActuallyVisible && !metrics) { - setIsLoadingMetrics(true); - setShowStatsUI(true); - } else if (!isActuallyVisible) { - setIsLoadingMetrics(false); - } + let pollingId: number | undefined; + if (isActuallyVisible && !metrics) setIsLoadingMetrics(true); + else if (!isActuallyVisible) setIsLoadingMetrics(false); const startMetrics = async () => { if (cancelled) return; - if (currentHostConfig.authType === "none") { - toast.error(t("serverStats.noneAuthNotSupported")); + toast.error(t("hostMetrics.noneAuthNotSupported")); setIsLoadingMetrics(false); - if (currentTab !== null) { - removeTab(currentTab); - } + if (currentTab !== null) removeTab(currentTab); return; } - const hasExistingMetrics = metrics !== null; - - if (!hasExistingMetrics) { - setIsLoadingMetrics(true); - } - setShowStatsUI(true); + const hadMetrics = metrics !== null; + if (!hadMetrics) setIsLoadingMetrics(true); setHasConnectionError(false); clearLogs(); @@ -412,20 +346,10 @@ function ServerStatsInner({ message: `Connecting to ${currentHostConfig.username}@${currentHostConfig.ip}:${currentHostConfig.port}`, }); const result = await startMetricsPolling(currentHostConfig.id); - if (cancelled) return; - - if (result?.connectionLogs) { - result.connectionLogs.forEach((log) => { - addLog({ - type: log.type, - stage: log.stage, - message: log.message, - details: log.details, - }); - }); - } - + result?.connectionLogs?.forEach((log) => + addLog(log as ConnectionLogPayload), + ); if (result.requires_totp) { setTotpRequired(true); setTotpSessionId(result.sessionId || null); @@ -433,114 +357,89 @@ function ServerStatsInner({ setIsLoadingMetrics(false); return; } - - if (result.viewerSessionId) { + if (result.viewerSessionId) setViewerSessionId(result.viewerSessionId); - } } - let retryCount = 0; - let data = null; + let retry = 0; + let data: ServerMetrics | null = null; const maxRetries = 15; - const retryDelay = 2000; - - while (retryCount < maxRetries && !cancelled) { + while (retry < maxRetries && !cancelled) { try { data = await getServerMetricsById(currentHostConfig.id); break; - } catch (error: unknown) { - retryCount++; - if (retryCount === 1) { - const initialDelay = totpVerified ? 3000 : 5000; - await new Promise((resolve) => setTimeout(resolve, initialDelay)); - } else if (retryCount < maxRetries && !cancelled) { - await new Promise((resolve) => setTimeout(resolve, retryDelay)); + } catch (error) { + retry++; + if (retry === 1) { + await new Promise((r) => + setTimeout(r, totpVerified ? 3000 : 5000), + ); + } else if (retry < maxRetries && !cancelled) { + await new Promise((r) => setTimeout(r, 2000)); } else { throw error; } } } - if (cancelled) return; if (data) { setMetrics(data); setServerStatus("online"); - if (!hasExistingMetrics) { + if (!hadMetrics) { setIsLoadingMetrics(false); logServerActivity(); } } - pollingIntervalId = window.setInterval(async () => { + pollingId = window.setInterval(async () => { if (cancelled) return; try { - const data = await getServerMetricsById(currentHostConfig.id); - if (!cancelled && data) { - setMetrics(data); - setMetricsHistory((prev) => { - const newHistory = [...prev, data]; - return newHistory.slice(-20); - }); - } - } catch (error) { - if (!cancelled) { - console.error("Failed to fetch metrics:", error); + const next = await getServerMetricsById(currentHostConfig.id); + if (!cancelled && next) { + setMetrics(next); + pushHistory(next); } + } catch { + /* keep prior */ } }, statsConfig.metricsInterval * 1000); } catch (error: unknown) { - if (!cancelled) { - const logError = error as ConnectionLogError; - console.error("Failed to start metrics polling:", error); - setIsLoadingMetrics(false); - setHasConnectionError(true); - - if (logError.connectionLogs) { - logError.connectionLogs.forEach((log) => { - addLog({ - type: log.type, - stage: log.stage, - message: log.message, - details: log.details, - }); - }); - } else { - addLog({ - type: "error", - stage: "connection", - message: - error instanceof Error - ? error.message - : t("serverStats.connectionFailed"), - }); - } + if (cancelled) return; + const logError = error as ConnectionLogError; + setIsLoadingMetrics(false); + setHasConnectionError(true); + if (logError.connectionLogs) { + logError.connectionLogs.forEach((log) => addLog(log)); + } else { + addLog({ + type: "error", + stage: "connection", + message: + error instanceof Error + ? error.message + : t("hostMetrics.connectionFailed"), + }); } } }; const stopMetrics = async () => { - if (pollingIntervalId) { - window.clearInterval(pollingIntervalId); - pollingIntervalId = undefined; + if (pollingId) { + window.clearInterval(pollingId); + pollingId = undefined; } if (currentHostConfig?.id) { - try { - await stopMetricsPolling( - currentHostConfig.id, - viewerSessionId || undefined, - ); - } catch (error) { - console.error("Failed to stop metrics polling:", error); - } + await stopMetricsPolling( + currentHostConfig.id, + viewerSessionId || undefined, + ).catch(() => {}); } }; - const debounceTimeout = setTimeout(() => { + const debounce = setTimeout(() => { if (isActuallyVisible) { - if (!hasConnectionError) { - startMetrics(); - } + if (!hasConnectionError) startMetrics(); } else { stopMetrics(); } @@ -548,8 +447,8 @@ function ServerStatsInner({ return () => { cancelled = true; - clearTimeout(debounceTimeout); - if (pollingIntervalId) window.clearInterval(pollingIntervalId); + clearTimeout(debounce); + if (pollingId) window.clearInterval(pollingId); if (currentHostConfig?.id) { stopMetricsPolling(currentHostConfig.id).catch(() => {}); } @@ -573,13 +472,11 @@ function ServerStatsInner({ const handleRefresh = async () => { if (!currentHostConfig?.id) return; - if (hasConnectionError) { setHasConnectionError(false); clearLogs(); return; } - try { setIsRefreshing(true); const res = await getServerStatusById(currentHostConfig.id); @@ -587,7 +484,7 @@ function ServerStatsInner({ const data = await getServerMetricsById(currentHostConfig.id); if (data) { setMetrics(data); - setShowStatsUI(true); + pushHistory(data); } } catch { setServerStatus("offline"); @@ -596,13 +493,22 @@ function ServerStatsInner({ } }; + const showCards = + metricsEnabled && !isLoadingMetrics && metrics && !hasConnectionError; + const showOffline = + metricsEnabled && + !isLoadingMetrics && + !metrics && + serverStatus === "offline" && + !hasConnectionError; + return (
-
+
{!totpRequired && !isLoadingMetrics && !hasConnectionError && ( -
+
-
+
-
-

{title}

-
+

{title}

{currentHostConfig?.quickActions && currentHostConfig.quickActions.length > 0 && ( <> -
+
{currentHostConfig.quickActions.map((action, index) => { const isExecuting = executingActions.has( action.snippetId, @@ -643,7 +547,7 @@ function ServerStatsInner({ new Set(prev).add(action.snippetId), ); toast.loading( - t("serverStats.executingQuickAction", { + t("hostMetrics.executingQuickAction", { name: action.name, }), { id: `quick-action-${action.snippetId}` }, @@ -655,7 +559,7 @@ function ServerStatsInner({ ); if (result.success) { toast.success( - t("serverStats.quickActionSuccess", { + t("hostMetrics.quickActionSuccess", { name: action.name, }), { @@ -669,7 +573,7 @@ function ServerStatsInner({ ); } else { toast.error( - t("serverStats.quickActionFailed", { + t("hostMetrics.quickActionFailed", { name: action.name, }), { @@ -682,7 +586,7 @@ function ServerStatsInner({ } } catch (error) { toast.error( - t("serverStats.quickActionError", { + t("hostMetrics.quickActionError", { name: action.name, }), { @@ -705,7 +609,7 @@ function ServerStatsInner({ > {isExecuting ? ( <> - + {action.name} ) : ( @@ -715,53 +619,97 @@ function ServerStatsInner({ ); })}
- + )} + {editMode && ( + <> + + setLayout({ ...effectiveLayout, columns }) + } + /> + + + )} +
)} - {metricsEnabled && - showStatsUI && - !isLoadingMetrics && - !metrics && - serverStatus === "offline" && - !hasConnectionError && ( -
-
-
- -

- {t("serverStats.serverOffline")} -

-

- {t("serverStats.cannotFetchMetrics")} -

-
-
-
- )} + {editMode && ( +
+ + + {t("hostMetrics.editModeInstructions")} + +
+ )} - {metricsEnabled && showStatsUI && !isLoadingMetrics && metrics && ( -
- {enabledWidgets.map((widgetType) => ( -
- {renderWidget(widgetType)} -
- ))} + {showOffline && ( +
+
+ +

+ {t("hostMetrics.serverOffline")} +

+

+ {t("hostMetrics.cannotFetchMetrics")} +

+
+
+ )} + + {showCards && ( +
+ + setLayout({ + slots: slots as HostMetricsLayout["slots"], + columns, + }) + } + />
)}
@@ -769,7 +717,7 @@ function ServerStatsInner({ {metricsEnabled && ( )}
@@ -791,10 +739,10 @@ function ServerStatsInner({ ); } -export function ServerStats(props: ServerProps): React.ReactElement { +export function HostMetricsTab(props: HostMetricsProps): React.ReactElement { return ( - + ); } diff --git a/src/ui/features/host-metrics/cards/CpuCard.tsx b/src/ui/features/host-metrics/cards/CpuCard.tsx new file mode 100644 index 00000000..0334e37c --- /dev/null +++ b/src/ui/features/host-metrics/cards/CpuCard.tsx @@ -0,0 +1,52 @@ +import { Cpu } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { RadialGauge, Sparkline, MiniStat } from "@/components/charts"; +import { MetricCard } from "./MetricCard"; + +export function CpuCard({ + metrics, + history, +}: { + metrics: ServerMetrics | null; + history: number[]; +}) { + const { t } = useTranslation(); + const percent = metrics?.cpu?.percent ?? null; + const cores = metrics?.cpu?.cores ?? null; + const load = metrics?.cpu?.load ?? null; + + return ( + } + > +
+
+ +
+ {load && ( +
+ + + +
+ )} + +
+
+
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/DiskCard.tsx b/src/ui/features/host-metrics/cards/DiskCard.tsx new file mode 100644 index 00000000..811a759d --- /dev/null +++ b/src/ui/features/host-metrics/cards/DiskCard.tsx @@ -0,0 +1,51 @@ +import { HardDrive } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { RadialGauge, Sparkline, MiniStat } from "@/components/charts"; +import { MetricCard } from "./MetricCard"; + +export function DiskCard({ + metrics, + history, +}: { + metrics: ServerMetrics | null; + history: number[]; +}) { + const { t } = useTranslation(); + const percent = metrics?.disk?.percent ?? null; + const usedHuman = metrics?.disk?.usedHuman ?? null; + const totalHuman = metrics?.disk?.totalHuman ?? null; + const availableHuman = metrics?.disk?.availableHuman ?? null; + + return ( + } + > +
+
+ +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/FirewallCard.tsx b/src/ui/features/host-metrics/cards/FirewallCard.tsx new file mode 100644 index 00000000..db570a03 --- /dev/null +++ b/src/ui/features/host-metrics/cards/FirewallCard.tsx @@ -0,0 +1,56 @@ +import { ShieldCheck } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { MetricCard } from "./MetricCard"; + +export function FirewallCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const fw = metrics?.firewall; + + const totalRules = + fw?.chains?.reduce((sum, c) => sum + (c.rules?.length ?? 0), 0) ?? 0; + + return ( + } + > + {!fw || fw.type === "none" ? ( + + {t("hostMetrics.firewall.noData")} + + ) : ( +
+
+ + {fw.status === "inactive" + ? t("hostMetrics.firewall.inactive") + : fw.status} + + + {fw.type} + +
+
+ + + {fw.chains?.length ?? 0} + {" "} + {t("hostMetrics.firewall.chains")} + + + {totalRules}{" "} + {t("hostMetrics.firewall.rules")} + +
+
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/LoginStatsCard.tsx b/src/ui/features/host-metrics/cards/LoginStatsCard.tsx new file mode 100644 index 00000000..dbb9cd19 --- /dev/null +++ b/src/ui/features/host-metrics/cards/LoginStatsCard.tsx @@ -0,0 +1,62 @@ +import { UserCheck, UserX } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { MetricCard } from "./MetricCard"; + +export function LoginStatsCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const loginStats = metrics?.login_stats; + const recentLogins = loginStats?.recentLogins ?? []; + const failedLogins = loginStats?.failedLogins ?? []; + + const allLogins = [ + ...recentLogins.map((l) => ({ ...l, status: "success" as const })), + ...failedLogins.map((l) => ({ ...l, status: "failed" as const })), + ] + .sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()) + .slice(0, 10); + + return ( + } + scroll + > + {allLogins.length === 0 ? ( + + {t("hostMetrics.noRecentLoginData")} + + ) : ( +
+ {allLogins.map((login, i) => ( +
+
+
+ {login.status === "failed" ? ( + + ) : ( + + )} + + {login.user} + +
+ + {login.ip} + +
+ + {new Date(login.time).toLocaleTimeString()} + +
+ ))} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/MemoryCard.tsx b/src/ui/features/host-metrics/cards/MemoryCard.tsx new file mode 100644 index 00000000..2d9f081f --- /dev/null +++ b/src/ui/features/host-metrics/cards/MemoryCard.tsx @@ -0,0 +1,54 @@ +import { MemoryStick } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { RadialGauge, Sparkline, MiniStat } from "@/components/charts"; +import { MetricCard } from "./MetricCard"; + +export function MemoryCard({ + metrics, + history, +}: { + metrics: ServerMetrics | null; + history: number[]; +}) { + const { t } = useTranslation(); + const percent = metrics?.memory?.percent ?? null; + const usedGiB = metrics?.memory?.usedGiB ?? null; + const totalGiB = metrics?.memory?.totalGiB ?? null; + const freeGiB = + usedGiB !== null && totalGiB !== null ? totalGiB - usedGiB : null; + + return ( + } + > +
+
+ +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/MetricCard.tsx b/src/ui/features/host-metrics/cards/MetricCard.tsx new file mode 100644 index 00000000..26ba3bd8 --- /dev/null +++ b/src/ui/features/host-metrics/cards/MetricCard.tsx @@ -0,0 +1,53 @@ +import type { ReactNode } from "react"; +import { cn } from "@/lib/utils"; + +/** Default cap (px) for a scrollable card body before it scrolls. */ +const DEFAULT_SCROLL_MAX = 360; + +/** + * Card shell for Host Metrics tiles. In the masonry grid tiles are + * content-sized (auto height), so the card grows to fit its content. Scrollable + * cards (long lists/logs) cap their body at `scrollMax` px and scroll beyond, + * which keeps the masonry tidy while short cards stay compact. + */ +export function MetricCard({ + title, + icon, + action, + children, + bodyClassName, + scroll = false, + scrollMax = DEFAULT_SCROLL_MAX, +}: { + title: string; + icon: ReactNode; + action?: ReactNode; + children: ReactNode; + bodyClassName?: string; + /** When true the body caps its height and scrolls instead of growing. */ + scroll?: boolean; + /** Max body height in px for scrollable cards. */ + scrollMax?: number; +}) { + return ( +
+
+ {icon} + + {title} + + {action &&
{action}
} +
+
+ {children} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/NetworkCard.tsx b/src/ui/features/host-metrics/cards/NetworkCard.tsx new file mode 100644 index 00000000..dde1992c --- /dev/null +++ b/src/ui/features/host-metrics/cards/NetworkCard.tsx @@ -0,0 +1,66 @@ +import { Cable, Container, Network, Wifi, WifiOff } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { MetricCard } from "./MetricCard"; + +function ifaceIcon(name: string) { + if (/^(wl|wlan|wifi|ath)/.test(name)) + return ; + if (/^(eth|en|enp|eno|ens)/.test(name)) + return ; + if (/^(docker|br-|veth|virbr|vlan|bond|tun|tap|wg|lo)/.test(name)) + return ; + return ; +} + +export function NetworkCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const interfaces = metrics?.network?.interfaces ?? []; + + return ( + } + scroll + > + {interfaces.length === 0 ? ( +
+ + {t("hostMetrics.noInterfacesFound")} +
+ ) : ( +
+ {interfaces.map((iface, i) => ( +
+
+
+ {ifaceIcon(iface.name)} + + + {iface.name} + +
+ + {iface.state} + +
+
+ {iface.ip} + {(iface.rx || iface.tx) && ( + + ↓ {iface.rx ?? "—"} / ↑ {iface.tx ?? "—"} + + )} +
+
+ ))} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/PortsCard.tsx b/src/ui/features/host-metrics/cards/PortsCard.tsx new file mode 100644 index 00000000..3de3c6d9 --- /dev/null +++ b/src/ui/features/host-metrics/cards/PortsCard.tsx @@ -0,0 +1,101 @@ +import { useMemo, useState } from "react"; +import { Unplug, Search } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics, ListeningPort } from "@/main-axios"; +import { MetricCard } from "./MetricCard"; + +function formatAddress(addr: string) { + return addr === "0.0.0.0" || addr === "*" || addr === "::" ? "*" : addr; +} + +function PortRow({ port }: { port: ListeningPort }) { + return ( +
+ + {port.localPort} + + + {port.protocol.toUpperCase()} + + + {port.process ?? (port.pid ? `PID:${port.pid}` : "—")} + + + {formatAddress(port.localAddress)} + +
+ ); +} + +export function PortsCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const [query, setQuery] = useState(""); + const [proto, setProto] = useState<"all" | "tcp" | "udp">("all"); + const ports = useMemo(() => metrics?.ports?.ports ?? [], [metrics]); + + const filtered = useMemo(() => { + const q = query.toLowerCase(); + return ports.filter((p) => { + if (proto !== "all" && p.protocol.toLowerCase() !== proto) return false; + if (!q) return true; + return ( + String(p.localPort).includes(q) || + (p.process ?? "").toLowerCase().includes(q) || + String(p.pid ?? "").includes(q) || + p.localAddress.toLowerCase().includes(q) + ); + }); + }, [ports, query, proto]); + + return ( + } + scroll + > +
+
+ + setQuery(e.target.value)} + placeholder={t("hostMetrics.ports.search")} + className="h-7 w-full border border-border bg-background pl-7 pr-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> +
+ + + {filtered.length} + +
+
+
+ {t("hostMetrics.ports.port")} + {t("hostMetrics.ports.protocol")} + {t("hostMetrics.ports.process")} + {t("hostMetrics.ports.address")} +
+ {filtered.length === 0 ? ( + + {t("hostMetrics.ports.noData")} + + ) : ( + filtered.map((port, i) => ( + + )) + )} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/ProcessesCard.tsx b/src/ui/features/host-metrics/cards/ProcessesCard.tsx new file mode 100644 index 00000000..95eb9110 --- /dev/null +++ b/src/ui/features/host-metrics/cards/ProcessesCard.tsx @@ -0,0 +1,54 @@ +import { Cpu } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { StatRow } from "@/components/charts"; +import { MetricCard } from "./MetricCard"; + +export function ProcessesCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const procs = metrics?.processes; + const top = procs?.top ?? []; + + return ( + } + scroll + > +
+ {procs?.total != null && ( + + {procs.total}{" "} + + {t("hostMetrics.processesTotal")} + + + )} + {procs?.running != null && ( + + {procs.running}{" "} + + {t("hostMetrics.processesRunning")} + + + )} +
+ {top.length === 0 ? ( + + {t("hostMetrics.noProcessesFound")} + + ) : ( +
+ {top.slice(0, 10).map((p) => ( + + ))} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/SystemCard.tsx b/src/ui/features/host-metrics/cards/SystemCard.tsx new file mode 100644 index 00000000..9cb70b29 --- /dev/null +++ b/src/ui/features/host-metrics/cards/SystemCard.tsx @@ -0,0 +1,37 @@ +import { Server } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { StatRow } from "@/components/charts"; +import { MetricCard } from "./MetricCard"; + +export function SystemCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const system = metrics?.system; + const uptime = metrics?.uptime; + + const rows = [ + { label: t("hostMetrics.hostname"), value: system?.hostname }, + { label: t("hostMetrics.operatingSystem"), value: system?.os }, + { label: t("hostMetrics.kernel"), value: system?.kernel }, + { label: t("hostMetrics.architecture"), value: system?.arch }, + { label: t("hostMetrics.uptime"), value: uptime?.formatted }, + ].filter((r) => r.value); + + return ( + } + scroll + > + {rows.length === 0 ? ( + N/A + ) : ( +
+ {rows.map(({ label, value }) => ( + + ))} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/UptimeCard.tsx b/src/ui/features/host-metrics/cards/UptimeCard.tsx new file mode 100644 index 00000000..b6e5ab06 --- /dev/null +++ b/src/ui/features/host-metrics/cards/UptimeCard.tsx @@ -0,0 +1,28 @@ +import { Clock } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { ServerMetrics } from "@/main-axios"; +import { MetricCard } from "./MetricCard"; + +export function UptimeCard({ metrics }: { metrics: ServerMetrics | null }) { + const { t } = useTranslation(); + const uptime = metrics?.uptime; + + return ( + } + > +
+ + {uptime?.formatted ?? "N/A"} + + {uptime?.seconds != null && ( + + {Math.floor(uptime.seconds).toLocaleString()}{" "} + {t("hostMetrics.seconds")} + + )} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/index.tsx b/src/ui/features/host-metrics/cards/index.tsx new file mode 100644 index 00000000..8558b7e1 --- /dev/null +++ b/src/ui/features/host-metrics/cards/index.tsx @@ -0,0 +1,191 @@ +import type { ComponentType, ReactNode } from "react"; +import type { ServerMetrics } from "@/main-axios"; +import type { HostMetricsCardId } from "@/types/host-metrics"; +import { defaultColSpanFor, defaultHeightFor } from "@/types/host-metrics"; +import { CpuCard } from "./CpuCard"; +import { MemoryCard } from "./MemoryCard"; +import { DiskCard } from "./DiskCard"; +import { NetworkCard } from "./NetworkCard"; +import { UptimeCard } from "./UptimeCard"; +import { SystemCard } from "./SystemCard"; +import { LoginStatsCard } from "./LoginStatsCard"; +import { PortsCard } from "./PortsCard"; +import { ProcessesCard } from "./ProcessesCard"; +import { FirewallCard } from "./FirewallCard"; +import { ServiceManagerCard } from "./managers/ServiceManagerCard"; +import { ProcessInspectorCard } from "./managers/ProcessInspectorCard"; +import { PackageManagerCard } from "./managers/PackageManagerCard"; +import { CronManagerCard } from "./managers/CronManagerCard"; +import { SslManagerCard } from "./managers/SslManagerCard"; +import { FirewallManagerCard } from "./managers/FirewallManagerCard"; +import { UserManagerCard } from "./managers/UserManagerCard"; +import { HealthCheckCard } from "./managers/HealthCheckCard"; +import { LogViewerCard } from "./managers/LogViewerCard"; +import { + TopMemoryCard, + SystemdTimersCard, + DiskBreakdownCard, +} from "./managers/SimpleManagerCards"; + +export interface MetricCardHistories { + cpu: number[]; + memory: number[]; + disk: number[]; +} + +export interface CardRenderContext { + metrics: ServerMetrics | null; + histories: MetricCardHistories; + hostId: number | null; +} + +export interface CardDefinition { + id: HostMetricsCardId; + /** i18n key for the card's display label. */ + labelKey: string; + kind: "metric" | "manager"; + render: (ctx: CardRenderContext) => ReactNode; +} + +type SimpleMetricCard = ComponentType<{ metrics: ServerMetrics | null }>; +type HistoryMetricCard = ComponentType<{ + metrics: ServerMetrics | null; + history: number[]; +}>; + +function metricCard( + id: HostMetricsCardId, + labelKey: string, + Comp: SimpleMetricCard, +): CardDefinition { + return { + id, + labelKey, + kind: "metric", + render: ({ metrics }) => , + }; +} + +function historyCard( + id: HostMetricsCardId, + labelKey: string, + Comp: HistoryMetricCard, + series: keyof MetricCardHistories, +): CardDefinition { + return { + id, + labelKey, + kind: "metric", + render: ({ metrics, histories }) => ( + + ), + }; +} + +type ManagerCardComp = ComponentType<{ hostId: number | null }>; + +function managerCard( + id: HostMetricsCardId, + labelKey: string, + Comp: ManagerCardComp, +): CardDefinition { + return { + id, + labelKey, + kind: "manager", + render: ({ hostId }) => , + }; +} + +export const CARD_DEFINITIONS: Record = { + cpu: historyCard("cpu", "hostMetrics.cpuUsage", CpuCard, "cpu"), + memory: historyCard( + "memory", + "hostMetrics.memoryUsage", + MemoryCard, + "memory", + ), + disk: historyCard("disk", "hostMetrics.diskUsage", DiskCard, "disk"), + network: metricCard("network", "hostMetrics.networkInterfaces", NetworkCard), + uptime: metricCard("uptime", "hostMetrics.uptime", UptimeCard), + system: metricCard("system", "hostMetrics.systemInfo", SystemCard), + login_stats: metricCard( + "login_stats", + "hostMetrics.loginStats", + LoginStatsCard, + ), + ports: metricCard("ports", "hostMetrics.ports.title", PortsCard), + processes: metricCard("processes", "hostMetrics.processes", ProcessesCard), + firewall: metricCard("firewall", "hostMetrics.firewall.title", FirewallCard), + service_manager: managerCard( + "service_manager", + "hostMetrics.managers.services", + ServiceManagerCard, + ), + process_inspector: managerCard( + "process_inspector", + "hostMetrics.managers.processInspector", + ProcessInspectorCard, + ), + log_viewer: managerCard( + "log_viewer", + "hostMetrics.managers.logViewer", + LogViewerCard, + ), + cron_manager: managerCard( + "cron_manager", + "hostMetrics.managers.cron", + CronManagerCard, + ), + package_manager: managerCard( + "package_manager", + "hostMetrics.managers.packages", + PackageManagerCard, + ), + ssl_manager: managerCard( + "ssl_manager", + "hostMetrics.managers.ssl", + SslManagerCard, + ), + firewall_manager: managerCard( + "firewall_manager", + "hostMetrics.managers.firewall", + FirewallManagerCard, + ), + user_manager: managerCard( + "user_manager", + "hostMetrics.managers.users", + UserManagerCard, + ), + health_check: managerCard( + "health_check", + "hostMetrics.managers.healthCheck", + HealthCheckCard, + ), + disk_breakdown: managerCard( + "disk_breakdown", + "hostMetrics.managers.diskBreakdown", + DiskBreakdownCard, + ), + systemd_timers: managerCard( + "systemd_timers", + "hostMetrics.managers.systemdTimers", + SystemdTimersCard, + ), + top_memory: managerCard( + "top_memory", + "hostMetrics.managers.topMemory", + TopMemoryCard, + ), +}; + +/** All card ids implemented and available in the Add tray. */ +export const IMPLEMENTED_CARD_IDS = Object.keys( + CARD_DEFINITIONS, +) as HostMetricsCardId[]; + +export function getCardDefinition(id: string): CardDefinition | undefined { + return CARD_DEFINITIONS[id]; +} + +export { defaultColSpanFor, defaultHeightFor }; diff --git a/src/ui/features/host-metrics/cards/managers/CronManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/CronManagerCard.tsx new file mode 100644 index 00000000..2e44f512 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/CronManagerCard.tsx @@ -0,0 +1,162 @@ +import { useEffect, useMemo, useState } from "react"; +import { Clock4, Plus, Trash2, Save } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface CronEntry { + raw: string; + enabled: boolean; + schedule: string; + command: string; +} + +export function CronManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + entries: CronEntry[]; + }>(hostId, "cron"); + const [entries, setEntries] = useState([]); + const [dirty, setDirty] = useState(false); + const [saving, setSaving] = useState(false); + const [query, setQuery] = useState(""); + + // Keep the real array index so edits/removes target the right entry even when + // the list is filtered by the search box. + const visible = useMemo(() => { + const q = query.toLowerCase(); + return entries + .map((entry, index) => ({ entry, index })) + .filter( + ({ entry }) => + !q || + entry.schedule.toLowerCase().includes(q) || + entry.command.toLowerCase().includes(q), + ); + }, [entries, query]); + + useEffect(() => { + if (data?.entries) { + setEntries(data.entries); + setDirty(false); + } + }, [data]); + + const update = (i: number, patch: Partial) => { + setEntries((prev) => + prev.map((e, idx) => (idx === i ? { ...e, ...patch } : e)), + ); + setDirty(true); + }; + const remove = (i: number) => { + setEntries((prev) => prev.filter((_, idx) => idx !== i)); + setDirty(true); + }; + const add = () => { + setEntries((prev) => [ + ...prev, + { raw: "", enabled: true, schedule: "0 * * * *", command: "" }, + ]); + setDirty(true); + }; + + const save = async () => { + if (hostId == null) return; + setSaving(true); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "cron", + { entries }, + ); + if (res.success) { + toast.success(t("hostMetrics.managers.cronSaved")); + setDirty(false); + refresh(); + } else { + toast.error(res.output || t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setSaving(false); + } + }; + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + headerExtra={ + <> + + {dirty && ( + + )} + + } + empty={!loading && entries.length === 0} + > + {entries.length > 5 && ( + + )} +
+ {visible.map(({ entry: e, index: i }) => ( +
+
+ update(i, { enabled: ev.target.checked })} + className="accent-accent-brand" + title={t("hostMetrics.managers.enabled")} + /> + update(i, { schedule: ev.target.value })} + placeholder="0 * * * *" + className="h-6 w-28 border border-border bg-background px-1.5 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring" + /> + +
+ update(i, { command: ev.target.value })} + placeholder={t("hostMetrics.managers.command")} + className="h-6 w-full border border-border bg-background px-1.5 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring" + /> +
+ ))} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/FirewallManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/FirewallManagerCard.tsx new file mode 100644 index 00000000..7bab6556 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/FirewallManagerCard.tsx @@ -0,0 +1,230 @@ +import { useMemo, useState } from "react"; +import { ShieldCheck, Plus, Trash2, Save } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { managerPost } from "@/main-axios"; +import type { FirewallChain } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface FirewallData { + type: "iptables" | "nftables" | "none"; + status: string; + chains: FirewallChain[]; +} + +export function FirewallManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData( + hostId, + "firewall", + ); + const [proto, setProto] = useState<"tcp" | "udp">("tcp"); + const [port, setPort] = useState(""); + const [target, setTarget] = useState<"ACCEPT" | "DROP" | "REJECT">("ACCEPT"); + const [busy, setBusy] = useState(false); + const [query, setQuery] = useState(""); + + const rule = async (op: "add" | "delete") => { + if (hostId == null) return; + const portNum = Number(port); + if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) { + toast.error(t("hostMetrics.managers.invalidPort")); + return; + } + setBusy(true); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "firewall", + { op, protocol: proto, port: portNum, target }, + "rule", + ); + if (res.success) { + toast.success(t("hostMetrics.managers.ruleApplied")); + refresh(); + } else { + toast.error(res.output || t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setBusy(false); + } + }; + + const persist = async () => { + if (hostId == null) return; + setBusy(true); + toast.loading(t("hostMetrics.managers.working"), { id: "fw-persist" }); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "firewall", + {}, + "persist", + ); + toast[res.success ? "success" : "error"]( + res.success + ? t("hostMetrics.managers.firewallPersisted") + : t("hostMetrics.managers.actionFailed"), + { id: "fw-persist", description: res.output?.slice(-200) }, + ); + } catch (e) { + toast.error(extractError(e).message, { id: "fw-persist" }); + } finally { + setBusy(false); + } + }; + + const chains = useMemo(() => { + const q = query.toLowerCase(); + return (data?.chains ?? []) + .map((c) => ({ + ...c, + rules: c.rules.filter( + (r) => + !q || + r.target.toLowerCase().includes(q) || + (r.dport ?? "").includes(q) || + r.protocol.toLowerCase().includes(q) || + r.source.toLowerCase().includes(q), + ), + })) + .filter((c) => c.rules.length > 0 || !q); + }, [data?.chains, query]); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + headerExtra={ + <> + + {data?.type ?? ""} + + + + } + > +
+ + {t("hostMetrics.managers.addInputRule")} + +
+ + setPort(e.target.value)} + placeholder={t("hostMetrics.ports.port")} + className="h-7 w-16 border border-border bg-background px-1.5 text-xs outline-none focus:ring-1 focus:ring-ring" + /> + + + +
+ + {t("hostMetrics.managers.firewallWarning")} + +
+ + + + {chains.length === 0 ? ( + + {t("hostMetrics.firewall.noData")} + + ) : ( +
+ {chains.map((chain) => ( +
+
+ + {chain.name} + + {chain.policy && ( + + {t("hostMetrics.firewall.policy")}: {chain.policy} + + )} +
+ {chain.rules.length === 0 ? ( + + {t("hostMetrics.firewall.noRules")} + + ) : ( + <> +
+ {t("hostMetrics.firewall.action")} + {t("hostMetrics.firewall.protocol")} + {t("hostMetrics.firewall.port")} + {t("hostMetrics.firewall.source")} +
+ {chain.rules.map((r, i) => ( +
+ {r.target} + + {r.protocol.toUpperCase()} + + {r.dport ?? "—"} + + {r.source} + +
+ ))} + + )} +
+ ))} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/HealthCheckCard.tsx b/src/ui/features/host-metrics/cards/managers/HealthCheckCard.tsx new file mode 100644 index 00000000..2e714caf --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/HealthCheckCard.tsx @@ -0,0 +1,310 @@ +import { useEffect, useMemo, useState } from "react"; +import { + HeartPulse, + CheckCircle2, + XCircle, + Plus, + Trash2, + Play, + Pencil, +} from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { Sparkline } from "@/components/charts"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; + +interface HealthCheck { + id: string; + name: string; + type: "tcp" | "http"; + target: string; + port?: number; + path?: string; +} +interface HealthResult { + checkId: string; + ok: boolean; + latencyMs: number | null; + detail: string; +} +interface HistoryRow { + checkId: string; + ts: string; + ok: boolean; + latencyMs: number | null; +} +interface HealthData { + checks: HealthCheck[]; + results: HealthResult[]; + history: HistoryRow[]; +} + +function newCheck(): HealthCheck { + return { + id: `c_${Math.random().toString(36).slice(2, 9)}`, + name: "", + type: "tcp", + target: "", + port: 80, + }; +} + +export function HealthCheckCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData( + hostId, + "health", + ); + const [editing, setEditing] = useState(false); + const [draft, setDraft] = useState([]); + const [saving, setSaving] = useState(false); + const [running, setRunning] = useState(false); + + useEffect(() => { + if (data?.checks && !editing) setDraft(data.checks); + }, [data?.checks, editing]); + + const byCheck = useMemo(() => { + const map = new Map(); + for (const h of data?.history ?? []) { + const arr = map.get(h.checkId) ?? []; + arr.push(h); + map.set(h.checkId, arr); + } + return map; + }, [data?.history]); + + const save = async () => { + if (hostId == null) return; + for (const c of draft) { + if (!c.name.trim() || !c.target.trim()) { + toast.error(t("hostMetrics.managers.healthMissingFields")); + return; + } + } + setSaving(true); + try { + const res = await managerPost<{ success: boolean }>( + hostId, + "health", + { checks: draft }, + "config", + ); + if (res.success) { + toast.success(t("hostMetrics.managers.healthSaved")); + setEditing(false); + refresh(); + } else { + toast.error(t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setSaving(false); + } + }; + + const runNow = async () => { + if (hostId == null) return; + setRunning(true); + try { + await managerPost(hostId, "health", {}, "run"); + refresh(); + } catch (e) { + toast.error(extractError(e).message); + } finally { + setRunning(false); + } + }; + + const updateDraft = (id: string, patch: Partial) => + setDraft((d) => d.map((c) => (c.id === id ? { ...c, ...patch } : c))); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + headerExtra={ + editing ? ( + <> + + + + ) : ( + <> + + + + ) + } + > + {editing ? ( +
+ {draft.map((c) => ( +
+
+ updateDraft(c.id, { name: e.target.value })} + placeholder={t("hostMetrics.managers.healthName")} + className="h-7 flex-1 border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> + + +
+
+ + updateDraft(c.id, { target: e.target.value }) + } + placeholder={t("hostMetrics.managers.healthTarget")} + className="h-7 flex-1 border border-border bg-background px-2 font-mono text-xs outline-none focus:ring-1 focus:ring-ring" + /> + {c.type === "tcp" ? ( + + updateDraft(c.id, { port: Number(e.target.value) }) + } + placeholder={t("hostMetrics.ports.port")} + className="h-7 w-16 border border-border bg-background px-1.5 text-xs outline-none focus:ring-1 focus:ring-ring" + /> + ) : ( + + updateDraft(c.id, { path: e.target.value }) + } + placeholder="/health" + className="h-7 w-24 border border-border bg-background px-1.5 font-mono text-xs outline-none focus:ring-1 focus:ring-ring" + /> + )} +
+
+ ))} + +
+ ) : (data?.checks?.length ?? 0) === 0 ? ( +
+ + {t("hostMetrics.managers.noHealthChecks")} + + +
+ ) : ( +
+ {(data?.checks ?? []).map((check) => { + const result = data?.results?.find((r) => r.checkId === check.id); + const hist = (byCheck.get(check.id) ?? []).slice().reverse(); + const upPct = + hist.length > 0 + ? Math.round( + (hist.filter((h) => h.ok).length / hist.length) * 100, + ) + : null; + return ( +
+
+
+ {result?.ok ? ( + + ) : ( + + )} + {check.name} + + {check.type === "tcp" + ? `${check.target}:${check.port}` + : check.target} + +
+ + {result?.latencyMs != null ? `${result.latencyMs}ms` : "—"} + {upPct != null ? ` · ${upPct}%` : ""} + +
+ {hist.length > 1 && ( + h.latencyMs ?? 0)} + height={28} + showLastDot={false} + /> + )} +
+ ); + })} +
+ )} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/LogViewerCard.tsx b/src/ui/features/host-metrics/cards/managers/LogViewerCard.tsx new file mode 100644 index 00000000..9443e4d7 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/LogViewerCard.tsx @@ -0,0 +1,183 @@ +import { useEffect, useMemo, useRef, useState } from "react"; +import { ScrollText } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { managerGet, managerGetSub } from "@/main-axios"; +import { extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; + +interface LogFiles { + common: string[]; + files: string[]; +} + +type Mode = "file" | "unit"; + +export function LogViewerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const [mode, setMode] = useState("file"); + const [path, setPath] = useState("/var/log/syslog"); + const [unit, setUnit] = useState(""); + const [customPath, setCustomPath] = useState(""); + const [lines, setLines] = useState(300); + const [content, setContent] = useState(""); + const [grep, setGrep] = useState(""); + const [follow, setFollow] = useState(false); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [files, setFiles] = useState([]); + const bodyRef = useRef(null); + + // Load the host's actual log files once. + useEffect(() => { + if (hostId == null) return; + managerGetSub(hostId, "logs", "files") + .then((res) => { + const merged = Array.from( + new Set([...(res.common ?? []), ...(res.files ?? [])]), + ); + setFiles(merged); + if (merged.length && !merged.includes(path)) setPath(merged[0]); + }) + .catch(() => {}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hostId]); + + const fetchLog = async () => { + if (hostId == null) return; + const params: Record = { lines }; + if (mode === "unit") { + if (!unit.trim()) return; + params.unit = unit.trim(); + } else { + const p = customPath.trim() || path; + if (!p) return; + params.path = p; + } + setLoading(true); + setError(null); + try { + const res = await managerGet<{ content: string }>(hostId, "logs", params); + setContent(res.content || ""); + requestAnimationFrame(() => { + if (bodyRef.current) + bodyRef.current.scrollTop = bodyRef.current.scrollHeight; + }); + } catch (e) { + setError(extractError(e).message); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchLog(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mode, path, unit, lines]); + + useEffect(() => { + if (!follow) return; + const id = setInterval(fetchLog, 3000); + return () => clearInterval(id); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [follow, mode, path, unit, lines, customPath]); + + const shown = useMemo(() => { + if (!grep) return content; + const needle = grep.toLowerCase(); + return content + .split("\n") + .filter((l) => l.toLowerCase().includes(needle)) + .join("\n"); + }, [content, grep]); + + return ( + } + loading={loading} + error={error ? { message: error } : null} + onRefresh={fetchLog} + headerExtra={ + + } + > +
+ + {mode === "file" ? ( + + ) : ( + setUnit(e.target.value)} + onBlur={fetchLog} + placeholder="nginx.service" + className="h-7 flex-1 border border-border bg-background px-2 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring" + /> + )} + +
+ + {mode === "file" && ( + setCustomPath(e.target.value)} + onBlur={() => customPath && fetchLog()} + placeholder={t("hostMetrics.managers.logCustomPath")} + className="mb-2 h-7 w-full border border-border bg-background px-2 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring" + /> + )} + + setGrep(e.target.value)} + placeholder={t("hostMetrics.managers.logGrep")} + className="mb-2 h-7 w-full border border-border bg-background px-2 text-[11px] outline-none focus:ring-1 focus:ring-ring" + /> + +
+        {shown || t("hostMetrics.managers.noLogData")}
+      
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/ManagerCardShell.tsx b/src/ui/features/host-metrics/cards/managers/ManagerCardShell.tsx new file mode 100644 index 00000000..98ac0914 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/ManagerCardShell.tsx @@ -0,0 +1,75 @@ +import type { ReactNode } from "react"; +import { RefreshCw, AlertTriangle } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { Button } from "@/components/button"; +import { MetricCard } from "../MetricCard"; +import type { ManagerError } from "./useManagerData"; + +export function ManagerCardShell({ + title, + icon, + loading, + error, + onRefresh, + empty, + emptyMessage, + children, + headerExtra, +}: { + title: string; + icon: ReactNode; + loading: boolean; + error: ManagerError | null; + onRefresh: () => void; + empty?: boolean; + emptyMessage?: string; + children: ReactNode; + headerExtra?: ReactNode; +}) { + const { t } = useTranslation(); + return ( + + {headerExtra} + +
+ } + > + {error ? ( +
+ + {error.message} + {error.code === "SUDO_REQUIRED" && ( + + {t("hostMetrics.managers.sudoHint")} + + )} + +
+ ) : empty ? ( +
+ {emptyMessage ?? t("hostMetrics.managers.noData")} +
+ ) : ( + children + )} + + ); +} diff --git a/src/ui/features/host-metrics/cards/managers/ManagerToolbar.tsx b/src/ui/features/host-metrics/cards/managers/ManagerToolbar.tsx new file mode 100644 index 00000000..c98ee85c --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/ManagerToolbar.tsx @@ -0,0 +1,42 @@ +import type { ReactNode } from "react"; +import { Search } from "lucide-react"; +import { useTranslation } from "react-i18next"; + +/** + * Compact search row shared by manager cards. Optional `extra` renders to the + * right (filters, action buttons) and `count` shows a result tally. + */ +export function ManagerSearch({ + value, + onChange, + placeholder, + count, + extra, +}: { + value: string; + onChange: (v: string) => void; + placeholder?: string; + count?: number; + extra?: ReactNode; +}) { + const { t } = useTranslation(); + return ( +
+
+ + onChange(e.target.value)} + placeholder={placeholder ?? t("hostMetrics.managers.filter")} + className="h-7 w-full border border-border bg-background pl-7 pr-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> +
+ {count != null && ( + + {count} + + )} + {extra} +
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/PackageManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/PackageManagerCard.tsx new file mode 100644 index 00000000..52568094 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/PackageManagerCard.tsx @@ -0,0 +1,115 @@ +import { useMemo, useState } from "react"; +import { Package, ArrowUpCircle } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface UpgradablePackage { + name: string; + currentVersion?: string; + newVersion?: string; +} + +export function PackageManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + pkg: string | null; + upgradable: UpgradablePackage[]; + }>(hostId, "packages"); + const [busy, setBusy] = useState(null); + const [filter, setFilter] = useState(""); + const all = useMemo(() => data?.upgradable ?? [], [data?.upgradable]); + const pkgs = useMemo( + () => + all.filter( + (p) => !filter || p.name.toLowerCase().includes(filter.toLowerCase()), + ), + [all, filter], + ); + + const run = async (action: string, pkg?: string) => { + if (hostId == null) return; + setBusy(pkg ?? action); + toast.loading(t("hostMetrics.managers.working"), { id: "pkg-op" }); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "packages", + { action, pkg }, + "action", + ); + toast[res.success ? "success" : "error"]( + res.success + ? t("hostMetrics.managers.actionDone", { name: pkg ?? "upgrade" }) + : t("hostMetrics.managers.actionFailed"), + { id: "pkg-op", description: res.output?.slice(-200) }, + ); + if (res.success) refresh(); + } catch (e) { + toast.error(extractError(e).message, { id: "pkg-op" }); + } finally { + setBusy(null); + } + }; + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && all.length === 0} + emptyMessage={t("hostMetrics.managers.allUpToDate")} + headerExtra={ + all.length > 0 ? ( + + ) : undefined + } + > + {all.length > 5 && ( + + )} +
+ {pkgs.map((p) => ( +
+
+ {p.name} + + {p.currentVersion ? `${p.currentVersion} → ` : ""} + {p.newVersion ?? ""} + +
+ +
+ ))} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/ProcessInspectorCard.tsx b/src/ui/features/host-metrics/cards/managers/ProcessInspectorCard.tsx new file mode 100644 index 00000000..4869ab1b --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/ProcessInspectorCard.tsx @@ -0,0 +1,190 @@ +import { useMemo, useState } from "react"; +import { ListTree, X, Skull } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface ProcessRow { + pid: number; + ppid: number; + user: string; + cpu: number; + mem: number; + command: string; + args: string; +} + +type SortKey = "cpu" | "mem" | "pid"; +type Signal = "TERM" | "KILL"; + +export function ProcessInspectorCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + processes: ProcessRow[]; + }>(hostId, "processes"); + const [filter, setFilter] = useState(""); + const [sort, setSort] = useState("cpu"); + const [tree, setTree] = useState(false); + const [busy, setBusy] = useState(null); + + const all = useMemo(() => data?.processes ?? [], [data?.processes]); + + const filtered = useMemo(() => { + const q = filter.toLowerCase(); + const list = all.filter( + (p) => + !q || + p.args.toLowerCase().includes(q) || + p.command.toLowerCase().includes(q) || + String(p.pid).includes(q) || + p.user.toLowerCase().includes(q), + ); + return [...list].sort((a, b) => { + if (sort === "pid") return a.pid - b.pid; + return b[sort] - a[sort]; + }); + }, [all, filter, sort]); + + // Build a depth map for the tree view from ppid links (only among visible). + const treeRows = useMemo(() => { + if (!tree) return null; + const byId = new Map(filtered.map((p) => [p.pid, p])); + const children = new Map(); + const roots: ProcessRow[] = []; + for (const p of filtered) { + if (p.ppid && byId.has(p.ppid)) { + const arr = children.get(p.ppid) ?? []; + arr.push(p); + children.set(p.ppid, arr); + } else { + roots.push(p); + } + } + const out: Array<{ proc: ProcessRow; depth: number }> = []; + const walk = (p: ProcessRow, depth: number) => { + out.push({ proc: p, depth }); + for (const c of children.get(p.pid) ?? []) walk(c, depth + 1); + }; + roots.forEach((r) => walk(r, 0)); + return out; + }, [tree, filtered]); + + const kill = async (pid: number, signal: Signal) => { + if (hostId == null) return; + setBusy(pid); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "processes", + { pid, signal }, + "signal", + ); + if (res.success) { + toast.success(t("hostMetrics.managers.signalSent", { pid })); + refresh(); + } else { + toast.error(res.output || t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setBusy(null); + } + }; + + const rows = treeRows + ? treeRows.slice(0, 200) + : filtered.slice(0, 200).map((proc) => ({ proc, depth: 0 })); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && filtered.length === 0} + headerExtra={ + <> + + + + } + > + +
+
+ PID + CPU + MEM + CMD + +
+ {rows.map(({ proc: p, depth }) => ( +
+ {p.pid} + + {p.cpu.toFixed(0)}% + + {p.mem.toFixed(0)}% + + {depth > 0 && ( + + )} + {p.command} + + +
+ ))} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/ServiceManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/ServiceManagerCard.tsx new file mode 100644 index 00000000..c3c1fa35 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/ServiceManagerCard.tsx @@ -0,0 +1,154 @@ +import { useMemo, useState } from "react"; +import { Cog, Play, Square, RotateCw, Power } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface SystemdService { + unit: string; + active: string; + sub: string; + description: string; +} + +export function ServiceManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + services: SystemdService[]; + }>(hostId, "services"); + const [filter, setFilter] = useState(""); + const [busy, setBusy] = useState(null); + + const services = useMemo( + () => + (data?.services ?? []).filter( + (s) => + !filter || + s.unit.toLowerCase().includes(filter.toLowerCase()) || + s.description.toLowerCase().includes(filter.toLowerCase()), + ), + [data?.services, filter], + ); + + const act = async (unit: string, action: string) => { + if (hostId == null) return; + setBusy(`${unit}:${action}`); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "services", + { unit, action }, + "action", + ); + if (res.success) { + toast.success(t("hostMetrics.managers.actionDone", { name: unit })); + refresh(); + } else { + toast.error(res.output || t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setBusy(null); + } + }; + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && services.length === 0} + > + +
+ {services.map((s) => ( +
+
+ +
+ + {s.unit.replace(/\.service$/, "")} + + + {s.sub} + +
+
+
+ act(s.unit, "start")} + busy={busy === `${s.unit}:start`} + title={t("hostMetrics.managers.start")} + > + + + act(s.unit, "restart")} + busy={busy === `${s.unit}:restart`} + title={t("hostMetrics.managers.restart")} + > + + + act(s.unit, "stop")} + busy={busy === `${s.unit}:stop`} + title={t("hostMetrics.managers.stop")} + > + + + + act(s.unit, s.sub === "running" ? "disable" : "enable") + } + busy={ + busy === `${s.unit}:enable` || busy === `${s.unit}:disable` + } + title={t("hostMetrics.managers.enableDisable")} + > + + +
+
+ ))} +
+
+ ); +} + +function ActionBtn({ + onClick, + busy, + title, + children, +}: { + onClick: () => void; + busy: boolean; + title: string; + children: React.ReactNode; +}) { + return ( + + ); +} diff --git a/src/ui/features/host-metrics/cards/managers/SimpleManagerCards.tsx b/src/ui/features/host-metrics/cards/managers/SimpleManagerCards.tsx new file mode 100644 index 00000000..09b43c5f --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/SimpleManagerCards.tsx @@ -0,0 +1,165 @@ +import { useMemo, useState } from "react"; +import { MemoryStick, Timer, HardDrive } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { BarSeries, StatRow } from "@/components/charts"; +import { useManagerData } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface MemProc { + pid: number; + user: string; + mem: number; + rss: number; + command: string; +} +interface TimerRow { + unit: string; + activates: string; + next: string; +} +interface MountUsage { + filesystem: string; + usePct: number; + usedKb: number; + sizeKb: number; + mount: string; +} + +function fmtGiB(kb: number): string { + return `${(kb / 1024 / 1024).toFixed(1)}G`; +} + +export function TopMemoryCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + processes: MemProc[]; + }>(hostId, "top-memory"); + const [query, setQuery] = useState(""); + const all = useMemo(() => data?.processes ?? [], [data?.processes]); + const procs = useMemo(() => { + const q = query.toLowerCase(); + return all.filter( + (p) => + !q || + p.command.toLowerCase().includes(q) || + String(p.pid).includes(q) || + p.user.toLowerCase().includes(q), + ); + }, [all, query]); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && all.length === 0} + > + {all.length > 5 && ( + + )} + ({ + label: `${p.command} (${p.pid})`, + value: p.mem, + valueLabel: `${p.mem.toFixed(1)}%`, + }))} + max={Math.max(1, ...procs.map((p) => p.mem))} + /> + + ); +} + +export function SystemdTimersCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + timers: TimerRow[]; + }>(hostId, "timers"); + const [query, setQuery] = useState(""); + const all = useMemo(() => data?.timers ?? [], [data?.timers]); + const timers = useMemo(() => { + const q = query.toLowerCase(); + return all.filter( + (tm) => + !q || + tm.unit.toLowerCase().includes(q) || + tm.activates.toLowerCase().includes(q), + ); + }, [all, query]); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && all.length === 0} + > + {all.length > 5 && ( + + )} +
+ {timers.map((tm) => ( + + ))} +
+
+ ); +} + +export function DiskBreakdownCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData<{ + mounts: MountUsage[]; + }>(hostId, "disk-breakdown"); + const [query, setQuery] = useState(""); + const all = useMemo(() => data?.mounts ?? [], [data?.mounts]); + const mounts = useMemo(() => { + const q = query.toLowerCase(); + return all.filter( + (m) => + !q || + m.mount.toLowerCase().includes(q) || + m.filesystem.toLowerCase().includes(q), + ); + }, [all, query]); + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && all.length === 0} + > + {all.length > 5 && ( + + )} + ({ + label: `${m.mount} (${fmtGiB(m.usedKb)}/${fmtGiB(m.sizeKb)})`, + value: m.usePct, + valueLabel: `${m.usePct}%`, + }))} + max={100} + /> + + ); +} diff --git a/src/ui/features/host-metrics/cards/managers/SslManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/SslManagerCard.tsx new file mode 100644 index 00000000..161457dd --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/SslManagerCard.tsx @@ -0,0 +1,301 @@ +import { useMemo, useState } from "react"; +import { ShieldCheck, RefreshCw, Plus, X, Trash2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { Button } from "@/components/button"; +import { managerPost } from "@/main-axios"; +import { + useManagerData, + useManagerAction, + extractError, +} from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; + +interface CertInfo { + client: string; + name: string; + domains: string[]; + expiry: string | null; +} +interface SslData { + clients: { certbot: boolean; acmeSh: boolean }; + certs: CertInfo[]; +} + +type Challenge = "http-standalone" | "http-webroot" | "dns"; + +/** Days until the given expiry string, or null if unparseable. */ +function daysUntil(expiry: string | null): number | null { + if (!expiry) return null; + const ts = Date.parse(expiry); + if (Number.isNaN(ts)) return null; + return Math.round((ts - Date.now()) / 86400000); +} + +function ExpiryBadge({ expiry }: { expiry: string | null }) { + const { t } = useTranslation(); + const days = daysUntil(expiry); + if (days == null) + return {expiry ?? "—"}; + const tone = + days < 0 + ? "text-destructive" + : days < 14 + ? "text-yellow-500" + : "text-accent-brand"; + return ( + + {days < 0 + ? t("hostMetrics.managers.sslExpired") + : t("hostMetrics.managers.sslInDays", { days })} + + ); +} + +export function SslManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData( + hostId, + "ssl", + ); + const { busy, run } = useManagerAction(hostId); + const [showIssue, setShowIssue] = useState(false); + const [domains, setDomains] = useState(""); + const [challenge, setChallenge] = useState("http-standalone"); + const [webroot, setWebroot] = useState(""); + const [dnsProvider, setDnsProvider] = useState(""); + const [issuing, setIssuing] = useState(false); + + const clients = data?.clients; + const activeClient: "certbot" | "acme.sh" | null = clients?.certbot + ? "certbot" + : clients?.acmeSh + ? "acme.sh" + : null; + + const certs = useMemo(() => data?.certs ?? [], [data?.certs]); + + const renew = (dryRun: boolean) => { + if (!activeClient) return; + run( + "ssl", + { client: activeClient, dryRun }, + { + action: "renew", + toastId: "ssl-op", + loadingMsg: t("hostMetrics.managers.working"), + successMsg: t("hostMetrics.managers.actionDone", { name: "renew" }), + failMsg: t("hostMetrics.managers.actionFailed"), + onDone: refresh, + }, + ); + }; + + const revoke = (cert: CertInfo) => { + if (cert.client !== "certbot" && cert.client !== "acme.sh") return; + if ( + !window.confirm( + t("hostMetrics.managers.sslRevokeConfirm", { name: cert.name }), + ) + ) + return; + run( + "ssl", + { client: cert.client, name: cert.name }, + { + action: "revoke", + toastId: "ssl-op", + loadingMsg: t("hostMetrics.managers.working"), + successMsg: t("hostMetrics.managers.sslRevoked"), + failMsg: t("hostMetrics.managers.actionFailed"), + onDone: refresh, + }, + ); + }; + + const issue = async () => { + if (hostId == null || !activeClient) return; + const list = domains + .split(/[\s,]+/) + .map((d) => d.trim()) + .filter(Boolean); + if (list.length === 0) { + const { toast } = await import("sonner"); + toast.error(t("hostMetrics.managers.sslNeedDomain")); + return; + } + setIssuing(true); + const { toast } = await import("sonner"); + toast.loading(t("hostMetrics.managers.working"), { id: "ssl-issue" }); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "ssl", + { + client: activeClient, + domains: list, + challenge, + webroot: challenge === "http-webroot" ? webroot : undefined, + dnsProvider: challenge === "dns" ? dnsProvider : undefined, + }, + "issue", + ); + toast[res.success ? "success" : "error"]( + res.success + ? t("hostMetrics.managers.sslIssued") + : t("hostMetrics.managers.actionFailed"), + { id: "ssl-issue", description: res.output?.slice(-200) }, + ); + if (res.success) { + setShowIssue(false); + setDomains(""); + refresh(); + } + } catch (e) { + toast.error(extractError(e).message, { id: "ssl-issue" }); + } finally { + setIssuing(false); + } + }; + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + empty={!loading && certs.length === 0 && !activeClient} + emptyMessage={t("hostMetrics.managers.noAcmeClient")} + headerExtra={ + activeClient ? ( + <> + + + + ) : undefined + } + > +
+
+ {t("hostMetrics.managers.clients")}: + + certbot + + + acme.sh + +
+ {activeClient && ( + + )} +
+ + {showIssue && activeClient && ( +
+ setDomains(e.target.value)} + placeholder={t("hostMetrics.managers.sslDomainsPlaceholder")} + className="h-7 w-full border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> +
+ +
+ {challenge === "http-webroot" && ( + setWebroot(e.target.value)} + placeholder="/var/www/html" + className="h-7 w-full border border-border bg-background px-2 font-mono text-xs outline-none focus:ring-1 focus:ring-ring" + /> + )} + {challenge === "dns" && ( + setDnsProvider(e.target.value)} + placeholder={t("hostMetrics.managers.sslDnsProvider")} + className="h-7 w-full border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> + )} + + + {t("hostMetrics.managers.sslIssueHint")} + +
+ )} + +
+ {certs.map((c) => ( +
+ + {c.name} + +
+ + + + {(c.client === "certbot" || c.client === "acme.sh") && ( + + )} +
+
+ ))} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/UserManagerCard.tsx b/src/ui/features/host-metrics/cards/managers/UserManagerCard.tsx new file mode 100644 index 00000000..ab7404d2 --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/UserManagerCard.tsx @@ -0,0 +1,145 @@ +import { useMemo, useState } from "react"; +import { Users, Plus, Trash2, ShieldCheck, Shield } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { Button } from "@/components/button"; +import { managerPost } from "@/main-axios"; +import { useManagerData, extractError } from "./useManagerData"; +import { ManagerCardShell } from "./ManagerCardShell"; +import { ManagerSearch } from "./ManagerToolbar"; + +interface SystemUser { + name: string; + uid: number; + shell: string; +} +interface UsersData { + users: SystemUser[]; + sudoers: string[]; +} + +export function UserManagerCard({ hostId }: { hostId: number | null }) { + const { t } = useTranslation(); + const { data, loading, error, refresh } = useManagerData( + hostId, + "users", + ); + const [newUser, setNewUser] = useState(""); + const [filter, setFilter] = useState(""); + const [busy, setBusy] = useState(null); + const sudoers = new Set(data?.sudoers ?? []); + + const users = useMemo( + () => + (data?.users ?? []).filter( + (u) => !filter || u.name.toLowerCase().includes(filter.toLowerCase()), + ), + [data?.users, filter], + ); + + const action = async (act: string, username: string, group?: string) => { + if (hostId == null) return; + setBusy(username); + try { + const res = await managerPost<{ success: boolean; output: string }>( + hostId, + "users", + { action: act, username, group }, + "action", + ); + if (res.success) { + toast.success(t("hostMetrics.managers.actionDone", { name: username })); + if (act === "create") setNewUser(""); + refresh(); + } else { + toast.error(res.output || t("hostMetrics.managers.actionFailed")); + } + } catch (e) { + toast.error(extractError(e).message); + } finally { + setBusy(null); + } + }; + + return ( + } + loading={loading} + error={error} + onRefresh={refresh} + > +
+ setNewUser(e.target.value)} + placeholder={t("hostMetrics.managers.newUsername")} + className="h-7 flex-1 border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring" + /> + +
+ +
+ {users.map((u) => { + const isSudoer = sudoers.has(u.name); + return ( +
+
+ {u.name} + {isSudoer && ( + + )} + + {u.uid} + +
+
+ + +
+
+ ); + })} +
+
+ ); +} diff --git a/src/ui/features/host-metrics/cards/managers/useManagerData.ts b/src/ui/features/host-metrics/cards/managers/useManagerData.ts new file mode 100644 index 00000000..67d92f8f --- /dev/null +++ b/src/ui/features/host-metrics/cards/managers/useManagerData.ts @@ -0,0 +1,119 @@ +import { useCallback, useEffect, useState } from "react"; +import { toast } from "sonner"; +import { managerGet, managerPost } from "@/main-axios"; + +interface ManagerError { + message: string; + code?: string; +} + +function extractError(err: unknown): ManagerError { + const e = err as { + response?: { data?: { error?: string; code?: string } }; + message?: string; + }; + return { + message: e?.response?.data?.error || e?.message || "Request failed", + code: e?.response?.data?.code, + }; +} + +/** + * Fetch a manager read resource on mount + manual refresh, with loading/error + * state. `hostId` null disables fetching. + */ +export function useManagerData( + hostId: number | null, + resource: string, + params?: Record, +) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const paramsKey = params ? JSON.stringify(params) : ""; + + const refresh = useCallback(async () => { + if (hostId == null) return; + setLoading(true); + setError(null); + try { + const res = await managerGet(hostId, resource, params); + setData(res); + } catch (err) { + setError(extractError(err)); + } finally { + setLoading(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hostId, resource, paramsKey]); + + useEffect(() => { + refresh(); + }, [refresh]); + + return { data, loading, error, refresh, setData }; +} + +interface ActionResult { + success: boolean; + output?: string; +} + +/** + * Run a manager POST action with consistent toast feedback (loading -> success + * /error) and a busy flag. On success the optional `onDone` callback runs (e.g. + * to refresh the card). Returns the result so callers can branch further. + */ +export function useManagerAction(hostId: number | null) { + const [busy, setBusy] = useState(false); + + const run = useCallback( + async ( + resource: string, + body: unknown, + opts: { + action?: string; + toastId?: string; + loadingMsg?: string; + successMsg?: string; + failMsg?: string; + onDone?: () => void; + } = {}, + ): Promise => { + if (hostId == null) return null; + const id = opts.toastId ?? `${resource}-action`; + setBusy(true); + if (opts.loadingMsg) toast.loading(opts.loadingMsg, { id }); + try { + const res = await managerPost(hostId, resource, body, opts.action); + if (res.success) { + if (opts.successMsg) + toast.success(opts.successMsg, { + id, + description: res.output?.slice(-200), + }); + else toast.dismiss(id); + opts.onDone?.(); + } else { + toast.error(opts.failMsg ?? res.output ?? "Action failed", { + id, + description: res.output?.slice(-200), + }); + } + return res; + } catch (err) { + toast.error(extractError(err).message, { id }); + return null; + } finally { + setBusy(false); + } + }, + [hostId], + ); + + return { busy, run }; +} + +export { extractError }; +export type { ManagerError }; diff --git a/src/ui/features/host-metrics/hooks/useHostMetricsPreferences.ts b/src/ui/features/host-metrics/hooks/useHostMetricsPreferences.ts new file mode 100644 index 00000000..2462d548 --- /dev/null +++ b/src/ui/features/host-metrics/hooks/useHostMetricsPreferences.ts @@ -0,0 +1,91 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { getHostMetricsLayout, saveHostMetricsLayout } from "@/main-axios"; +import type { HostMetricsLayout } from "@/types/host-metrics"; + +const SAVE_DEBOUNCE_MS = 800; + +function lsKey(hostId: number) { + return `hostMetricsLayout:${hostId}`; +} + +function readCache(hostId: number): HostMetricsLayout | null { + try { + const raw = localStorage.getItem(lsKey(hostId)); + if (!raw) return null; + const parsed = JSON.parse(raw); + if (parsed && Array.isArray(parsed.slots)) + return parsed as HostMetricsLayout; + } catch { + /* ignore */ + } + return null; +} + +function writeCache(hostId: number, layout: HostMetricsLayout) { + try { + localStorage.setItem(lsKey(hostId), JSON.stringify(layout)); + } catch { + /* ignore */ + } +} + +/** + * Per-host Host Metrics layout, cached in localStorage for instant paint and + * synced to the backend (so it follows the user across devices). Saves are + * debounced. Returns null layout until the first load resolves. + */ +export function useHostMetricsPreferences(hostId: number | null) { + const [layout, setLayout] = useState(() => + hostId != null ? readCache(hostId) : null, + ); + const [loaded, setLoaded] = useState(false); + const saveTimer = useRef | null>(null); + + useEffect(() => { + if (hostId == null) return; + let cancelled = false; + setLoaded(false); + const cached = readCache(hostId); + if (cached) setLayout(cached); + + getHostMetricsLayout(hostId) + .then((remote) => { + if (cancelled || !remote) return; + setLayout(remote); + writeCache(hostId, remote); + }) + .catch(() => { + /* keep cache/default */ + }) + .finally(() => { + if (!cancelled) setLoaded(true); + }); + + return () => { + cancelled = true; + }; + }, [hostId]); + + const save = useCallback( + (next: HostMetricsLayout) => { + if (hostId == null) return; + setLayout(next); + writeCache(hostId, next); + if (saveTimer.current) clearTimeout(saveTimer.current); + saveTimer.current = setTimeout(() => { + saveHostMetricsLayout(hostId, next).catch(() => { + /* best-effort; cache already holds it */ + }); + }, SAVE_DEBOUNCE_MS); + }, + [hostId], + ); + + useEffect(() => { + return () => { + if (saveTimer.current) clearTimeout(saveTimer.current); + }; + }, []); + + return { layout, setLayout: save, loaded }; +} diff --git a/src/ui/features/server-stats/widgets/CpuWidget.tsx b/src/ui/features/server-stats/widgets/CpuWidget.tsx deleted file mode 100644 index 687a9c99..00000000 --- a/src/ui/features/server-stats/widgets/CpuWidget.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from "react"; -import { Cpu } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface CpuWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -function Sparkline({ - history, - current, -}: { - history: ServerMetrics[]; - current: number | null; -}) { - const points = [ - ...history.map((m) => m?.cpu?.percent ?? 0), - current ?? 0, - ].slice(-20); - - const w = 300; - const h = 48; - - const hasData = points.length >= 2; - const max = hasData ? Math.max(...points, 1) : 1; - const coords = hasData - ? points.map((v, i) => { - const x = (i / (points.length - 1)) * w; - const y = h - (v / max) * h; - return `${x},${y}`; - }) - : []; - - const d = hasData ? `M ${coords.join(" L ")}` : ""; - const fill = hasData ? `M 0,${h} L ${coords.join(" L ")} L ${w},${h} Z` : ""; - - return ( -
- {hasData && ( - - - - - )} -
- ); -} - -export function CpuWidget({ metrics, metricsHistory }: CpuWidgetProps) { - const { t } = useTranslation(); - const percent = metrics?.cpu?.percent ?? null; - const cores = metrics?.cpu?.cores ?? null; - const load = metrics?.cpu?.load ?? null; - - return ( - } - > -
-
-
- - {percent !== null ? `${percent.toFixed(1)}%` : "N/A"} - - - {cores !== null - ? t("serverStats.cpuCores", { count: cores }) - : t("serverStats.naCpus")} - -
- {load && ( -
- - {t("serverStats.loadAvg")} - -
- {load[0].toFixed(2)}  {load[1].toFixed(2)}   - {load[2].toFixed(2)} -
-
- )} -
-
-
-
- -
- - ); -} diff --git a/src/ui/features/server-stats/widgets/DiskWidget.tsx b/src/ui/features/server-stats/widgets/DiskWidget.tsx deleted file mode 100644 index df83808b..00000000 --- a/src/ui/features/server-stats/widgets/DiskWidget.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { HardDrive } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface DiskWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -function Sparkline({ - history, - current, -}: { - history: ServerMetrics[]; - current: number | null; -}) { - const points = [ - ...history.map((m) => m?.disk?.percent ?? 0), - current ?? 0, - ].slice(-20); - - const w = 300; - const h = 48; - - const hasData = points.length >= 2; - const max = hasData ? Math.max(...points, 1) : 1; - const coords = hasData - ? points.map((v, i) => { - const x = (i / (points.length - 1)) * w; - const y = h - (v / max) * h; - return `${x},${y}`; - }) - : []; - - const d = hasData ? `M ${coords.join(" L ")}` : ""; - const fill = hasData ? `M 0,${h} L ${coords.join(" L ")} L ${w},${h} Z` : ""; - - return ( -
- {hasData && ( - - - - - )} -
- ); -} - -export function DiskWidget({ metrics, metricsHistory }: DiskWidgetProps) { - const { t } = useTranslation(); - const percent = metrics?.disk?.percent ?? null; - const usedHuman = metrics?.disk?.usedHuman ?? null; - const totalHuman = metrics?.disk?.totalHuman ?? null; - const availableHuman = metrics?.disk?.availableHuman ?? null; - - return ( - } - > -
-
-
- - {percent !== null ? `${percent}%` : "N/A"} - - - {usedHuman && totalHuman ? `${usedHuman} / ${totalHuman}` : "N/A"} - -
-
-
-
-
-
-
- - {t("serverStats.available")} - - {availableHuman ?? "N/A"} -
-
- -
- - ); -} diff --git a/src/ui/features/server-stats/widgets/FirewallWidget.tsx b/src/ui/features/server-stats/widgets/FirewallWidget.tsx deleted file mode 100644 index 958fc992..00000000 --- a/src/ui/features/server-stats/widgets/FirewallWidget.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import React from "react"; -import { Shield, ShieldOff, ShieldCheck, ChevronDown } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import type { - FirewallMetrics, - FirewallChain, - FirewallRule, -} from "@/types/stats-widgets"; -import { SectionCard } from "@/components/section-card"; - -interface FirewallWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -function RuleRow({ rule }: { rule: FirewallRule }) { - const { t } = useTranslation(); - const targetClass = - rule.target.toUpperCase() === "ACCEPT" - ? "text-accent-brand" - : rule.target.toUpperCase() === "DROP" - ? "text-destructive" - : rule.target.toUpperCase() === "REJECT" - ? "text-yellow-500" - : "text-muted-foreground"; - - const src = - rule.interface ?? - rule.state ?? - (rule.source === "0.0.0.0/0" - ? t("serverStats.firewall.anywhere") - : rule.source); - - return ( -
- {rule.target} - - {rule.protocol.toUpperCase()} - - {rule.dport ?? "—"} - - {src} - -
- ); -} - -function ChainSection({ chain }: { chain: FirewallChain }) { - const { t } = useTranslation(); - const [open, setOpen] = React.useState(true); - const policyClass = - chain.policy.toUpperCase() === "ACCEPT" - ? "text-accent-brand" - : chain.policy.toUpperCase() === "DROP" - ? "text-destructive" - : "text-yellow-500"; - - return ( -
- - {open && chain.rules.length > 0 && ( -
-
- {t("serverStats.firewall.action")} - {t("serverStats.firewall.protocol")} - {t("serverStats.firewall.port")} - {t("serverStats.firewall.source")} -
- {chain.rules.map((rule, i) => ( - - ))} -
- )} -
- ); -} - -export function FirewallWidget({ metrics }: FirewallWidgetProps) { - const { t } = useTranslation(); - const firewall = (metrics as ServerMetrics & { firewall?: FirewallMetrics }) - ?.firewall; - - const statusIcon = - !firewall || firewall.type === "none" ? ( - - ) : firewall.status === "active" ? ( - - ) : ( - - ); - - const statusBadge = - firewall?.status === "active" ? ( - - ACTIVE - - ) : ( - - {t("serverStats.firewall.inactive").toUpperCase()} - - ); - - return ( - -
-
- - {t("serverStats.firewall.title")} - - {statusBadge} -
- {firewall?.type && firewall.type !== "none" && ( - - {firewall.type} - - )} - {firewall && firewall.chains.length > 0 ? ( -
- {firewall.chains.map((chain) => ( - - ))} -
- ) : ( - - {t("serverStats.firewall.noData")} - - )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/LoginStatsWidget.tsx b/src/ui/features/server-stats/widgets/LoginStatsWidget.tsx deleted file mode 100644 index d327989b..00000000 --- a/src/ui/features/server-stats/widgets/LoginStatsWidget.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { UserCheck, UserX } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import { SectionCard } from "@/components/section-card"; - -interface LoginRecord { - user: string; - ip: string; - time: string; - status: "success" | "failed"; -} - -interface LoginStatsMetrics { - recentLogins: LoginRecord[]; - failedLogins: LoginRecord[]; - totalLogins: number; - uniqueIPs: number; -} - -interface LoginStatsWidgetProps { - metrics: { login_stats?: LoginStatsMetrics } | null; - metricsHistory: unknown[]; -} - -export function LoginStatsWidget({ metrics }: LoginStatsWidgetProps) { - const { t } = useTranslation(); - - const loginStats = metrics?.login_stats; - const recentLogins = loginStats?.recentLogins ?? []; - const failedLogins = loginStats?.failedLogins ?? []; - - const allLogins = [ - ...recentLogins.map((l) => ({ ...l, status: "success" as const })), - ...failedLogins.map((l) => ({ ...l, status: "failed" as const })), - ] - .sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()) - .slice(0, 6); - - return ( - } - > -
- {allLogins.length === 0 ? ( - - {t("serverStats.noRecentLoginData")} - - ) : ( -
- {allLogins.map((login, i) => ( -
-
-
- {login.status === "failed" ? ( - - ) : ( - - )} - - {login.user} - -
- - {login.ip} - -
-
- - {login.status} - - - {new Date(login.time).toLocaleTimeString()} - -
-
- ))} -
- )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/MemoryWidget.tsx b/src/ui/features/server-stats/widgets/MemoryWidget.tsx deleted file mode 100644 index fa9b8c9d..00000000 --- a/src/ui/features/server-stats/widgets/MemoryWidget.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { MemoryStick } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface MemoryWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -function Sparkline({ - history, - current, -}: { - history: ServerMetrics[]; - current: number | null; -}) { - const points = [ - ...history.map((m) => m?.memory?.percent ?? 0), - current ?? 0, - ].slice(-20); - - const w = 300; - const h = 48; - - const hasData = points.length >= 2; - const max = hasData ? Math.max(...points, 1) : 1; - const coords = hasData - ? points.map((v, i) => { - const x = (i / (points.length - 1)) * w; - const y = h - (v / max) * h; - return `${x},${y}`; - }) - : []; - - const d = hasData ? `M ${coords.join(" L ")}` : ""; - const fill = hasData ? `M 0,${h} L ${coords.join(" L ")} L ${w},${h} Z` : ""; - - return ( -
- {hasData && ( - - - - - )} -
- ); -} - -export function MemoryWidget({ metrics, metricsHistory }: MemoryWidgetProps) { - const { t } = useTranslation(); - const percent = metrics?.memory?.percent ?? null; - const usedGiB = metrics?.memory?.usedGiB ?? null; - const totalGiB = metrics?.memory?.totalGiB ?? null; - - return ( - } - > -
-
-
- - {percent !== null ? `${percent.toFixed(1)}%` : "N/A"} - - - {usedGiB !== null && totalGiB !== null - ? `${usedGiB.toFixed(1)} / ${totalGiB.toFixed(1)} GiB` - : "N/A"} - -
-
-
-
-
-
-
- - {t("serverStats.swap")} - - N/A -
-
- - {t("serverStats.free")} - - - {usedGiB !== null && totalGiB !== null - ? `${(totalGiB - usedGiB).toFixed(1)} GiB` - : "N/A"} - -
-
- -
- - ); -} diff --git a/src/ui/features/server-stats/widgets/NetworkWidget.tsx b/src/ui/features/server-stats/widgets/NetworkWidget.tsx deleted file mode 100644 index ff1b0ea4..00000000 --- a/src/ui/features/server-stats/widgets/NetworkWidget.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Cable, Container, Network, Wifi, WifiOff } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface NetworkWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -export function NetworkWidget({ metrics }: NetworkWidgetProps) { - const { t } = useTranslation(); - - const metricsWithNetwork = metrics as ServerMetrics & { - network?: { - interfaces?: Array<{ - name: string; - state: string; - ip: string; - rx?: string; - tx?: string; - }>; - }; - }; - const interfaces = metricsWithNetwork?.network?.interfaces ?? []; - - const ifaceIcon = (name: string) => { - if (/^(wl|wlan|wifi|ath)/.test(name)) - return ; - if (/^(eth|en|enp|eno|ens)/.test(name)) - return ; - if (/^(docker|br-|veth|virbr|vlan|bond|tun|tap|wg|lo)/.test(name)) - return ; - return ; - }; - - return ( - } - > -
- {interfaces.length === 0 ? ( -
- - - {t("serverStats.noInterfacesFound")} - -
- ) : ( -
- {interfaces.map((iface, i) => ( -
-
-
- {ifaceIcon(iface.name)} -
- - {iface.name} - -
- - {iface.state} - -
-
- {iface.ip} - {(iface.rx || iface.tx) && ( - - ↓ {iface.rx ?? "—"} / ↑ {iface.tx ?? "—"} - - )} -
-
- ))} -
- )} -
- - ); -} diff --git a/src/ui/features/server-stats/widgets/PortsWidget.tsx b/src/ui/features/server-stats/widgets/PortsWidget.tsx deleted file mode 100644 index 31f21300..00000000 --- a/src/ui/features/server-stats/widgets/PortsWidget.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Unplug } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import type { PortsMetrics, ListeningPort } from "@/types/stats-widgets"; -import { SectionCard } from "@/components/section-card"; - -interface PortsWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -function PortRow({ port }: { port: ListeningPort }) { - const formatAddress = (addr: string) => - addr === "0.0.0.0" || addr === "*" || addr === "::" ? "*" : addr; - - return ( -
- - {port.localPort} - - - {port.protocol.toUpperCase()} - - - {port.process ?? (port.pid ? `PID:${port.pid}` : "—")} - - - {formatAddress(port.localAddress)} - -
- ); -} - -export function PortsWidget({ metrics }: PortsWidgetProps) { - const { t } = useTranslation(); - - const portsData = (metrics as ServerMetrics & { ports?: PortsMetrics }) - ?.ports; - const ports = portsData?.ports ?? []; - - return ( - } - > -
-
- {t("serverStats.ports.port")} - {t("serverStats.ports.protocol")} - {t("serverStats.ports.process")} - {t("serverStats.ports.address")} -
- {ports.length === 0 ? ( - - {t("serverStats.ports.noData")} - - ) : ( -
- {ports.map((port, i) => ( - - ))} -
- )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/ProcessesWidget.tsx b/src/ui/features/server-stats/widgets/ProcessesWidget.tsx deleted file mode 100644 index 1d59ec50..00000000 --- a/src/ui/features/server-stats/widgets/ProcessesWidget.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { List, Activity } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface ProcessesWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -export function ProcessesWidget({ metrics }: ProcessesWidgetProps) { - const { t } = useTranslation(); - - const metricsWithProcesses = metrics as ServerMetrics & { - processes?: { - total?: number; - running?: number; - top?: Array<{ - pid: number; - cpu: number; - mem: number; - command: string; - user: string; - }>; - }; - }; - const processes = metricsWithProcesses?.processes; - const topProcesses = processes?.top ?? []; - - return ( - } - > -
-
- PID - CPU - MEM - CMD -
- {topProcesses.length === 0 ? ( -
- - {t("serverStats.noProcessesFound")} -
- ) : ( -
- {topProcesses.map((proc, i) => ( -
- {proc.pid} - {proc.cpu}% - {proc.mem}% - - {proc.command.split("/").pop()} - -
- ))} -
- )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/SystemWidget.tsx b/src/ui/features/server-stats/widgets/SystemWidget.tsx deleted file mode 100644 index dfacdbdf..00000000 --- a/src/ui/features/server-stats/widgets/SystemWidget.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Server } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface SystemWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -export function SystemWidget({ metrics }: SystemWidgetProps) { - const { t } = useTranslation(); - - const metricsWithSystem = metrics as ServerMetrics & { - system?: { hostname?: string; os?: string; kernel?: string; arch?: string }; - uptime?: { formatted?: string }; - }; - const system = metricsWithSystem?.system; - const uptime = metricsWithSystem?.uptime; - - const rows = [ - { label: t("serverStats.hostname"), value: system?.hostname }, - { label: t("serverStats.operatingSystem"), value: system?.os }, - { label: t("serverStats.kernel"), value: system?.kernel }, - { label: t("serverStats.architecture"), value: system?.arch }, - { label: t("serverStats.uptime"), value: uptime?.formatted }, - ].filter((r) => r.value); - - return ( - } - > -
- {rows.map(({ label, value }) => ( -
- - {label} - - - {value} - -
- ))} - {rows.length === 0 && ( - N/A - )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/UptimeWidget.tsx b/src/ui/features/server-stats/widgets/UptimeWidget.tsx deleted file mode 100644 index 9850fa0c..00000000 --- a/src/ui/features/server-stats/widgets/UptimeWidget.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Clock } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import type { ServerMetrics } from "@/main-axios.ts"; -import { SectionCard } from "@/components/section-card"; - -interface UptimeWidgetProps { - metrics: ServerMetrics | null; - metricsHistory: ServerMetrics[]; -} - -export function UptimeWidget({ metrics }: UptimeWidgetProps) { - const { t } = useTranslation(); - - const metricsWithUptime = metrics as ServerMetrics & { - uptime?: { formatted?: string; seconds?: number }; - }; - const uptime = metricsWithUptime?.uptime; - - return ( - } - > -
- - {uptime?.formatted ?? "N/A"} - - {uptime?.seconds && ( - - {Math.floor(uptime.seconds).toLocaleString()}{" "} - {t("serverStats.seconds")} - - )} -
-
- ); -} diff --git a/src/ui/features/server-stats/widgets/index.ts b/src/ui/features/server-stats/widgets/index.ts deleted file mode 100644 index db72d0fe..00000000 --- a/src/ui/features/server-stats/widgets/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { CpuWidget } from "./CpuWidget.tsx"; -export { MemoryWidget } from "./MemoryWidget.tsx"; -export { DiskWidget } from "./DiskWidget.tsx"; -export { NetworkWidget } from "./NetworkWidget.tsx"; -export { UptimeWidget } from "./UptimeWidget.tsx"; -export { ProcessesWidget } from "./ProcessesWidget.tsx"; -export { SystemWidget } from "./SystemWidget.tsx"; -export { LoginStatsWidget } from "./LoginStatsWidget.tsx"; -export { PortsWidget } from "./PortsWidget.tsx"; -export { FirewallWidget } from "./FirewallWidget.tsx"; diff --git a/src/ui/features/terminal/Terminal.tsx b/src/ui/features/terminal/Terminal.tsx index 6cab70e1..7102b013 100644 --- a/src/ui/features/terminal/Terminal.tsx +++ b/src/ui/features/terminal/Terminal.tsx @@ -11,6 +11,7 @@ import { useXTerm } from "react-xtermjs"; import { FitAddon } from "@xterm/addon-fit"; import { ClipboardAddon } from "@xterm/addon-clipboard"; import { RobustClipboardProvider } from "@/lib/clipboard-provider"; +import { copyToClipboard } from "@/lib/clipboard"; import { Unicode11Addon } from "@xterm/addon-unicode11"; import { WebLinksAddon } from "@xterm/addon-web-links"; import { useTranslation } from "react-i18next"; @@ -71,6 +72,8 @@ interface SSHTerminalProps { onTitleChange?: (title: string) => void; initialPath?: string; executeCommand?: string; + /** Attach to this tmux session right after connecting (tmux monitor). */ + tmuxAttachSession?: string; onOpenFileManager?: (path?: string) => void; previewTheme?: string | null; } @@ -84,6 +87,7 @@ const TerminalInner = forwardRef( onClose, initialPath, executeCommand, + tmuxAttachSession, onOpenFileManager, previewTheme, }, @@ -212,30 +216,8 @@ const TerminalInner = forwardRef( const totpTimeoutRef = useRef(null); const connectionTimeoutRef = useRef(null); const activityLoggedRef = useRef(false); - const [commandHistoryTrackingEnabled, setCommandHistoryTrackingEnabled] = - useState( - () => localStorage.getItem("commandHistoryTracking") === "true", - ); - - useEffect(() => { - const handleCommandHistoryTrackingChanged = () => { - setCommandHistoryTrackingEnabled( - localStorage.getItem("commandHistoryTracking") === "true", - ); - }; - - window.addEventListener( - "commandHistoryTrackingChanged", - handleCommandHistoryTrackingChanged, - ); - - return () => { - window.removeEventListener( - "commandHistoryTrackingChanged", - handleCommandHistoryTrackingChanged, - ); - }; - }, []); + const commandHistoryTrackingEnabled = + hostConfig.enableCommandHistory !== false; const { trackInput, getCurrentCommand, updateCurrentCommand } = useCommandTracker({ @@ -947,7 +929,14 @@ const TerminalInner = forwardRef( ws.send( JSON.stringify({ type: "connectToHost", - data: { cols, rows, hostConfig, initialPath, executeCommand }, + data: { + cols, + rows, + hostConfig, + initialPath, + executeCommand, + tmuxAttachSession, + }, }), ); } @@ -982,10 +971,13 @@ const TerminalInner = forwardRef( if (msg.type === "data") { if (typeof msg.data === "string") { const syntaxHighlightingEnabled = - localStorage.getItem("terminalSyntaxHighlighting") === "true"; + hostConfig.terminalConfig?.syntaxHighlighting !== false; const outputData = syntaxHighlightingEnabled - ? highlightTerminalOutput(msg.data) + ? highlightTerminalOutput( + msg.data, + hostConfig.terminalConfig?.syntaxHighlightingOptions, + ) : msg.data; terminal.write(outputData); @@ -1045,11 +1037,14 @@ const TerminalInner = forwardRef( } } else { const syntaxHighlightingEnabled = - localStorage.getItem("terminalSyntaxHighlighting") === "true"; + hostConfig.terminalConfig?.syntaxHighlighting !== false; const stringData = String(msg.data); const outputData = syntaxHighlightingEnabled - ? highlightTerminalOutput(stringData) + ? highlightTerminalOutput( + stringData, + hostConfig.terminalConfig?.syntaxHighlightingOptions, + ) : stringData; terminal.write(outputData); @@ -1666,33 +1661,9 @@ const TerminalInner = forwardRef( } async function writeTextToClipboard(text: string): Promise { - try { - if (window.electronClipboard) { - await window.electronClipboard.writeText(text); - return true; - } - if (navigator.clipboard && navigator.clipboard.writeText) { - await navigator.clipboard.writeText(text); - return true; - } - } catch { - // fall through to legacy method - } - try { - const textarea = document.createElement("textarea"); - textarea.value = text; - textarea.style.position = "fixed"; - textarea.style.left = "-9999px"; - document.body.appendChild(textarea); - textarea.focus(); - textarea.select(); - document.execCommand("copy"); - document.body.removeChild(textarea); - return true; - } catch { - toast.error(t("terminal.clipboardWriteFailed")); - return false; - } + const ok = await copyToClipboard(text); + if (!ok) toast.error(t("terminal.clipboardWriteFailed")); + return ok; } async function readTextFromClipboard(): Promise { diff --git a/src/ui/features/terminal/TerminalApp.tsx b/src/ui/features/terminal/TerminalApp.tsx index bb38dd3d..fce724af 100644 --- a/src/ui/features/terminal/TerminalApp.tsx +++ b/src/ui/features/terminal/TerminalApp.tsx @@ -5,9 +5,17 @@ import { FullScreenAppWrapper } from "@/features/FullScreenAppWrapper.tsx"; interface TerminalAppProps { hostId?: string; + /** tmux session to attach to once the shell is ready (tmux monitor "Attach"). */ + tmuxSession?: string; } -const TerminalApp: React.FC = ({ hostId }) => { +// Only the session name travels in the URL (never a raw command), so a crafted +// link cannot execute arbitrary input. `=` forces exact-name matching in tmux. +function tmuxAttachCommand(session: string): string { + return `tmux attach-session -t '=${session.replace(/'/g, "'\\''")}'`; +} + +const TerminalApp: React.FC = ({ hostId, tmuxSession }) => { const { t } = useTranslation(); return ( @@ -43,6 +51,9 @@ const TerminalApp: React.FC = ({ hostId }) => { showTitle={false} splitScreen={false} onClose={() => {}} + executeCommand={ + tmuxSession ? tmuxAttachCommand(tmuxSession) : undefined + } /> ); }} diff --git a/src/ui/features/tmux-monitor/PanePreview.tsx b/src/ui/features/tmux-monitor/PanePreview.tsx new file mode 100644 index 00000000..87b1ed58 --- /dev/null +++ b/src/ui/features/tmux-monitor/PanePreview.tsx @@ -0,0 +1,168 @@ +// Interactive view of a tmux pane. Embeds the real SSH terminal (the same +// component as the Terminal feature) and attaches to the selected session via +// the native tmux_attach flow, so the pane is fully usable — typing, mouse +// scrolling and tmux copy-mode all behave exactly like a normal terminal tab. + +import { useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + Activity, + Cpu, + MemoryStick, + RotateCw, + SquareSplitHorizontal, + SquareSplitVertical, + Trash2, + X, +} from "lucide-react"; +import { CommandHistoryProvider } from "@/features/terminal/command-history/CommandHistoryContext"; +import { Terminal } from "@/features/terminal/Terminal"; +import type { + TerminalHandle, + TerminalHostConfig, +} from "@/features/terminal/Terminal"; +import type { SSHHost } from "@/types/index"; +import type { TmuxPaneMetrics } from "@/api/tmux-monitor-api"; +import { formatMem } from "./format"; +import type { SelectedPane } from "./types"; + +interface PanePreviewProps { + host: SSHHost; + pane: SelectedPane; + metrics?: TmuxPaneMetrics; + /** Imperative handle of the embedded terminal, used by the parent to nudge + * a refit/redraw after layout-changing tmux actions. */ + terminalRef?: React.RefObject; + /** Split the window containing this pane ("h" = new pane to the right, + * "v" = below — tmux -h/-v semantics). */ + onSplit: (direction: "h" | "v") => void; + /** Ask for confirmation and kill this pane. */ + onKillPane: () => void; + onClose: () => void; +} + +function newInstanceId(): string { + return typeof crypto.randomUUID === "function" + ? crypto.randomUUID() + : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; +} + +export function PanePreview({ + host, + pane, + metrics, + terminalRef, + onSplit, + onKillPane, + onClose, +}: PanePreviewProps) { + const { t } = useTranslation(); + // One PTY per mount; the parent remounts this component (via key) when the + // host or session changes. Pane switches within a session go through the + // focus endpoint instead, so the connection is reused. + const instanceIdRef = useRef(newInstanceId()); + // Bumping this remounts the embedded terminal with a fresh PTY — the rescue + // hatch when the attached client's rendering gets out of sync. + const [attachNonce, setAttachNonce] = useState(0); + + function reattach() { + instanceIdRef.current = newInstanceId(); + setAttachNonce((n) => n + 1); + } + + return ( + <> +
+ + {pane.sessionName} · {pane.paneId} + + {metrics && ( + <> + + + {metrics.cpuPercent.toFixed(1)}% + + + + {formatMem(metrics.memRssKb)} + + {metrics.gpuMemMb > 0 && ( + + + {metrics.gpuMemMb} MB GPU + + )} + {metrics.topCommand && ( + + {metrics.topCommand} ({metrics.processCount}) + + )} + + )} +
+ + + +
+ + +
+
+
+ + + +
+ + ); +} diff --git a/src/ui/features/tmux-monitor/SearchResults.tsx b/src/ui/features/tmux-monitor/SearchResults.tsx new file mode 100644 index 00000000..4617e84d --- /dev/null +++ b/src/ui/features/tmux-monitor/SearchResults.tsx @@ -0,0 +1,85 @@ +import type { ReactNode } from "react"; +import { useTranslation } from "react-i18next"; +import { X } from "lucide-react"; +import type { TmuxSearchMatch, TmuxSearchResult } from "@/api/tmux-monitor-api"; + +interface SearchResultsProps { + results: TmuxSearchMatch[]; + searching: boolean; + /** The query that produced these results, used to highlight matches. */ + query: string; + /** Search-limit info; renders a "partial results" note when truncated. */ + limits: Pick< + TmuxSearchResult, + "truncated" | "searchedLines" | "maxPanes" + > | null; + onSelect: (match: TmuxSearchMatch) => void; + onClose: () => void; +} + +function highlightMatch(text: string, query: string): ReactNode { + if (!query) return text; + const index = text.toLowerCase().indexOf(query.toLowerCase()); + if (index === -1) return text; + return ( + <> + {text.slice(0, index)} + + {text.slice(index, index + query.length)} + + {text.slice(index + query.length)} + + ); +} + +export function SearchResults({ + results, + searching, + query, + limits, + onSelect, + onClose, +}: SearchResultsProps) { + const { t } = useTranslation(); + + return ( +
+
+ + {searching + ? t("common.loading") + : t("tmuxMonitor.searchResults", { count: results.length })} + {!searching && limits?.truncated && ( + + {t("tmuxMonitor.searchTruncated", { + lines: limits.searchedLines, + panes: limits.maxPanes, + })} + + )} + + +
+ {results.map((match, i) => ( +
onSelect(match)} + > + + {match.sessionName} · {match.paneId} + + + {highlightMatch(match.text, query)} + +
+ ))} +
+ ); +} diff --git a/src/ui/features/tmux-monitor/SessionTree.tsx b/src/ui/features/tmux-monitor/SessionTree.tsx new file mode 100644 index 00000000..3907782c --- /dev/null +++ b/src/ui/features/tmux-monitor/SessionTree.tsx @@ -0,0 +1,451 @@ +// Session/window/pane tree styled after the VSCode "tmux manager" view. +// Each session is a single row: status dot, name, inline tag badges, and a +// right-aligned meta label (relative time · cpu · mem) that is overlaid by +// the hover actions (attach, new window, …-menu). The overlay uses a grid +// stack with opacity so rows never shift and the actions stay reachable via +// keyboard focus. + +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + AppWindow, + BarChart2, + ChevronDown, + ChevronRight, + MoreHorizontal, + Pencil, + Play, + Plus, + SquareSplitHorizontal, + SquareSplitVertical, + SquareTerminal, + Tag, + Trash2, + X, +} from "lucide-react"; +import { Badge } from "@/components/badge"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/dropdown-menu"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"; +import type { + TmuxPaneMetrics, + TmuxSessionOverview, +} from "@/api/tmux-monitor-api"; +import { formatMem, formatRelativeTime } from "./format"; +import type { SelectedPane } from "./types"; + +export interface SessionMetricsAgg { + cpu: number; + memKb: number; + gpuMb: number; +} + +const MAX_INLINE_TAGS = 2; + +interface SessionTreeProps { + sessions: TmuxSessionOverview[]; + expandedSessions: Set; + onToggleSession: (name: string) => void; + selectedPaneId: string | null; + onSelectPane: (pane: SelectedPane) => void; + metricsByPane: Map; + metricsBySession: Map; + /** Open the tag editor for a session (… menu). */ + onEditTags: (sessionName: string) => void; + /** Open a terminal attached to the session (the ▷ hover action). */ + onAttachSession: (sessionName: string) => void; + /** Create a new window in the session (the + hover action). */ + onNewWindow: (sessionName: string) => void; + /** Open the rename dialog for a session (… menu). */ + onRenameSession: (sessionName: string) => void; + /** Open the kill confirmation for a session (… menu). */ + onKillSession: (sessionName: string) => void; + /** Open the kill confirmation for a single pane (hover ✕ on pane rows). */ + onKillPane: (paneId: string) => void; + /** Split the window containing a pane (hover actions on pane rows). */ + onSplitPane: (paneId: string, direction: "h" | "v") => void; + /** Open the kill confirmation for a window (hover ✕ on window rows). */ + onKillWindow: (sessionName: string, windowIndex: number) => void; + /** Narrow tree panel: the inline time/cpu/mem label is hidden (the hover + * tooltip still carries the full status). */ + compact: boolean; + /** Timestamp used to render relative times; bumped periodically by the + * parent so "Xm ago" labels do not go stale. */ + now: number; +} + +export function SessionTree({ + sessions, + expandedSessions, + onToggleSession, + selectedPaneId, + onSelectPane, + metricsByPane, + metricsBySession, + onEditTags, + onAttachSession, + onNewWindow, + onRenameSession, + onKillSession, + onKillPane, + onSplitPane, + onKillWindow, + compact, + now, +}: SessionTreeProps) { + const { t } = useTranslation(); + // Collapsed windows, keyed "session windowIndex". Windows default to + // expanded (panes visible) like the VSCode tmux manager. + const [collapsedWindows, setCollapsedWindows] = useState>( + new Set(), + ); + + function windowKey(sessionName: string, windowIndex: number): string { + return `${sessionName} ${windowIndex}`; + } + + function toggleWindow(sessionName: string, windowIndex: number) { + setCollapsedWindows((prev) => { + const next = new Set(prev); + const key = windowKey(sessionName, windowIndex); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + } + + function metaLabel(session: TmuxSessionOverview): string { + const time = formatRelativeTime(session.lastActivity, now, t); + const agg = metricsBySession.get(session.name); + if (!agg) return time; + return `${time} · ${agg.cpu.toFixed(0)}% · ${formatMem(agg.memKb)}`; + } + + return ( + <> + {sessions.map((session) => { + const expanded = expandedSessions.has(session.name); + const agg = metricsBySession.get(session.name); + const paneCount = session.windows.reduce( + (sum, w) => sum + w.panes.length, + 0, + ); + return ( +
+
onToggleSession(session.name)} + > + {expanded ? ( + + ) : ( + + )} + 0 ? "bg-accent-brand" : "bg-muted-foreground/40"}`} + title={ + session.attachedClients > 0 + ? t("tmuxMonitor.attached") + : t("tmuxMonitor.detached") + } + /> + + {session.name} + + {session.tags.slice(0, MAX_INLINE_TAGS).map((tag) => ( + + {tag} + + ))} + {session.tags.length > MAX_INLINE_TAGS && ( + + +{session.tags.length - MAX_INLINE_TAGS} + + )} + + {/* Meta label and hover actions stacked in one grid cell */} + + {!compact && ( + + {metaLabel(session)} + + )} + + + + + + + + e.stopPropagation()} + > +
+
+ 0 ? "bg-accent-brand" : "bg-muted-foreground/40"}`} + /> + + {session.name} + + + {session.attachedClients > 0 + ? t("tmuxMonitor.attached") + : t("tmuxMonitor.detached")} + +
+
+
+ {t("tmuxMonitor.statusActivity")} +
+
+ {formatRelativeTime(session.lastActivity, now, t)} +
+
+ {t("tmuxMonitor.statusWindows")} +
+
+ {session.windows.length} / {paneCount}{" "} + {t("tmuxMonitor.statusPanes")} +
+
CPU
+
+ {agg ? `${agg.cpu.toFixed(1)}%` : "—"} +
+
RAM
+
+ {agg ? formatMem(agg.memKb) : "—"} +
+ {agg && agg.gpuMb > 0 && ( + <> +
GPU
+
{agg.gpuMb} MB
+ + )} + {session.tags.length > 0 && ( + <> +
+ {t("tmuxMonitor.statusTags")} +
+
+ {session.tags.join(", ")} +
+ + )} +
+
+
+
+ + + + + e.stopPropagation()} + > + + setTimeout(() => onRenameSession(session.name), 0) + } + > + + {t("tmuxMonitor.rename")} + + + setTimeout(() => onEditTags(session.name), 0) + } + > + + {t("tmuxMonitor.editTags")} + + + setTimeout(() => onKillSession(session.name), 0) + } + > + + {t("tmuxMonitor.kill")} + + + +
+
+
+ + {expanded && + session.windows.map((win) => { + const winCollapsed = collapsedWindows.has( + windowKey(session.name, win.index), + ); + return ( +
+
toggleWindow(session.name, win.index)} + > + {winCollapsed ? ( + + ) : ( + + )} + + + {win.index}: {win.name} + + +
+ {!winCollapsed && + win.panes.map((pane) => { + const m = metricsByPane.get(pane.id); + const isSelected = selectedPaneId === pane.id; + return ( +
+ onSelectPane({ + paneId: pane.id, + sessionName: session.name, + windowIndex: win.index, + }) + } + > + + + {pane.index}: {pane.command} + + + {pane.path} + + {/* Same overlay stack as the session rows: cpu + label and hover actions share one grid cell so the + buttons never widen the row (they would get + clipped on a narrow panel). */} + + {m && m.cpuPercent > 0 && ( + + {m.cpuPercent.toFixed(0)}% + + )} + + + + + + +
+ ); + })} +
+ ); + })} +
+ ); + })} + + ); +} diff --git a/src/ui/features/tmux-monitor/TmuxMonitor.tsx b/src/ui/features/tmux-monitor/TmuxMonitor.tsx new file mode 100644 index 00000000..ea0124ef --- /dev/null +++ b/src/ui/features/tmux-monitor/TmuxMonitor.tsx @@ -0,0 +1,1276 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import { + ChevronsDownUp, + ChevronsUpDown, + ExternalLink, + Layers, + MonitorPlay, + Plus, + RefreshCw, + Search, + Server, + SquareTerminal, +} from "lucide-react"; +import { Button } from "@/components/button"; +import { Input } from "@/components/input"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/dialog"; +import { Skeleton } from "@/components/skeleton"; +import { ScrollArea } from "@/components/scroll-area"; +import { getSSHHosts } from "@/main-axios"; +import { isElectron } from "@/lib/electron"; +import type { SSHHost } from "@/types/index"; +import { + getTmuxOverview, + getTmuxMetrics, + searchTmux, + setTmuxSessionTags, + focusTmuxPane, + createTmuxSession, + createTmuxWindow, + renameTmuxSession, + killTmuxSession, + killTmuxWindow, + killTmuxPane, + splitTmuxPane, + type TmuxOverview, + type TmuxPaneMetrics, + type TmuxSearchMatch, + type TmuxSearchResult, +} from "@/api/tmux-monitor-api"; +import type { TerminalHandle } from "@/features/terminal/Terminal"; +import { SessionTree, type SessionMetricsAgg } from "./SessionTree"; +import { SearchResults } from "./SearchResults"; +import { PanePreview } from "./PanePreview"; +import type { SelectedPane } from "./types"; + +const OVERVIEW_POLL_MS = 10_000; +const METRICS_POLL_MS = 10_000; +const TIME_TICK_MS = 30_000; + +const LS_PREFIX = "termix-tmux-monitor-"; +const LS_LAST_HOST_KEY = `${LS_PREFIX}last-host`; +const LS_TREE_WIDTH_KEY = `${LS_PREFIX}tree-width`; + +const TREE_WIDTH_DEFAULT = 288; // matches the old fixed w-72 +const TREE_WIDTH_MIN = 200; +const TREE_WIDTH_MAX = 520; + +function expandedStorageKey(hostId: number): string { + return `${LS_PREFIX}expanded-${hostId}`; +} + +function readStoredExpanded(hostId: number): Set | null { + try { + const raw = localStorage.getItem(expandedStorageKey(hostId)); + if (!raw) return null; + const parsed: unknown = JSON.parse(raw); + if (!Array.isArray(parsed)) return null; + return new Set(parsed.filter((v): v is string => typeof v === "string")); + } catch { + return null; + } +} + +export function TmuxMonitor({ + initialHostId, + isVisible = true, +}: { + initialHostId?: number; + isVisible?: boolean; +}) { + const { t } = useTranslation(); + const [hosts, setHosts] = useState([]); + const [hostsLoading, setHostsLoading] = useState(true); + const [selectedHostId, setSelectedHostId] = useState(null); + const [overview, setOverview] = useState(null); + const [overviewLoading, setOverviewLoading] = useState(false); + const [overviewError, setOverviewError] = useState(null); + const [expandedSessions, setExpandedSessions] = useState>( + new Set(), + ); + const [selectedPane, setSelectedPane] = useState(null); + const [metrics, setMetrics] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [searchedQuery, setSearchedQuery] = useState(""); + const [searchResults, setSearchResults] = useState( + null, + ); + const [searchLimits, setSearchLimits] = useState | null>(null); + const [searching, setSearching] = useState(false); + // Bumped every 30s so relative "Xm ago" labels do not go stale. + const [now, setNow] = useState(() => Date.now()); + const searchInputRef = useRef(null); + // Host whose data is currently on screen; responses for any other host are + // stale (the user switched away while the request was in flight) and must + // not overwrite the current tree. + const activeHostRef = useRef(null); + // True when the expanded-session set for the current host was restored from + // localStorage (or touched by the user) and must not be overwritten by the + // default expand-all behavior. + const expandedRestoredRef = useRef(false); + // Imperative handle of the preview's embedded terminal. After tmux actions + // that change the layout (split / kill-pane / new-window) the attached + // client can render stale borders; a refit forces a PTY resize which makes + // tmux fully redraw the client. + const previewTermRef = useRef(null); + const nudgePreviewRedraw = useCallback(() => { + setTimeout(() => { + previewTermRef.current?.fit?.(); + previewTermRef.current?.notifyResize?.(); + previewTermRef.current?.refresh?.(); + }, 300); + }, []); + + // -- resizable tree panel (same pattern as the AppShell host sidebar) ------- + const [treeWidth, setTreeWidth] = useState(() => { + const saved = Number(localStorage.getItem(LS_TREE_WIDTH_KEY)); + return Number.isFinite(saved) && saved >= TREE_WIDTH_MIN + ? Math.min(saved, TREE_WIDTH_MAX) + : TREE_WIDTH_DEFAULT; + }); + const [treeDragging, setTreeDragging] = useState(false); + useEffect(() => { + try { + localStorage.setItem(LS_TREE_WIDTH_KEY, String(treeWidth)); + } catch { + // localStorage may be unavailable + } + }, [treeWidth]); + const onTreeResizeMouseDown = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + setTreeDragging(true); + const startX = e.clientX; + const startW = treeWidth; + function onMove(ev: MouseEvent) { + setTreeWidth( + Math.max( + TREE_WIDTH_MIN, + Math.min(TREE_WIDTH_MAX, startW + ev.clientX - startX), + ), + ); + } + function onUp() { + setTreeDragging(false); + window.removeEventListener("mousemove", onMove); + window.removeEventListener("mouseup", onUp); + } + window.addEventListener("mousemove", onMove); + window.addEventListener("mouseup", onUp); + }, + [treeWidth], + ); + + // -- hosts ---------------------------------------------------------------- + const loadHosts = useCallback( + async (initial: boolean) => { + try { + const all: SSHHost[] = await getSSHHosts(); + const sshHosts = all.filter( + (h) => + (h.connectionType ?? "ssh") === "ssh" && + h.enableTerminal !== false && + h.enableTmuxMonitor === true, + ); + setHosts(sshHosts); + setSelectedHostId((prev) => { + if (prev !== null && sshHosts.some((h) => h.id === prev)) return prev; + if (sshHosts.length === 0) return null; + if (initial) { + if ( + initialHostId != null && + sshHosts.some((h) => h.id === initialHostId) + ) + return initialHostId; + const stored = Number(localStorage.getItem(LS_LAST_HOST_KEY)); + if ( + Number.isFinite(stored) && + sshHosts.some((h) => h.id === stored) + ) + return stored; + } + return sshHosts[0].id; + }); + } catch { + if (initial) toast.error(t("tmuxMonitor.failedToLoadHosts")); + } finally { + if (initial) setHostsLoading(false); + } + }, + // initialHostId is only consulted on the initial load; the mount effect + // already runs once and the follow-initialHostId effect handles updates. + // eslint-disable-next-line react-hooks/exhaustive-deps + [t], + ); + + useEffect(() => { + loadHosts(true); + }, [loadHosts]); + + // The tab stays mounted while hosts are added or removed elsewhere (Host + // Manager, sidebar), so follow the app-wide change event to keep the list + // and selection current. + useEffect(() => { + const handler = () => loadHosts(false); + window.addEventListener("termix:hosts-changed", handler); + return () => window.removeEventListener("termix:hosts-changed", handler); + }, [loadHosts]); + + // Reopening the singleton tab from a host action updates initialHostId; + // follow it so the requested host gets selected without a remount. + useEffect(() => { + if (initialHostId == null) return; + if (hosts.some((h) => h.id === initialHostId)) + setSelectedHostId(initialHostId); + // Intentionally not depending on `hosts`: the mount effect already picks + // the initial host once hosts load. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [initialHostId]); + + // -- relative time refresh -------------------------------------------------- + useEffect(() => { + const interval = setInterval(() => setNow(Date.now()), TIME_TICK_MS); + return () => clearInterval(interval); + }, []); + + // -- overview polling ----------------------------------------------------- + const loadOverview = useCallback( + async (hostId: number, silent = false) => { + if (!silent) { + setOverviewLoading(true); + setOverviewError(null); + } + try { + const data = await getTmuxOverview(hostId); + if (activeHostRef.current !== hostId) return true; + setOverview(data); + setExpandedSessions((prev) => { + if (expandedRestoredRef.current || prev.size > 0) return prev; + return new Set(data.sessions.map((s) => s.name)); + }); + return true; + } catch (err) { + if (activeHostRef.current !== hostId) return true; + if (!silent) { + const axiosErr = err as { + code?: string; + response?: { data?: { error?: string; code?: string } }; + }; + const data = axiosErr.response?.data; + // The client request can time out before the backend finishes its + // own SSH connect timeout, so treat that like HOST_UNREACHABLE too. + const unreachable = + data?.code === "HOST_UNREACHABLE" || + axiosErr.code === "ECONNABORTED"; + if (data?.code === "TMUX_NOT_INSTALLED") { + // Same friendly empty-state as the overview's available:false. + setOverview({ available: false, sessions: [] }); + setOverviewError(null); + } else { + setOverview(null); + setOverviewError( + unreachable + ? t("tmuxMonitor.hostUnreachable") + : data?.code === "TMUX_NO_SERVER" + ? t("tmuxMonitor.noServer") + : data?.error || + (err instanceof Error + ? err.message + : t("tmuxMonitor.failedToLoad")), + ); + } + } + return false; + } finally { + if (!silent && activeHostRef.current === hostId) + setOverviewLoading(false); + } + }, + [t], + ); + + // Manual refresh keeps the current tree on screen (no skeleton flash) and + // only spins the refresh icons; the skeleton is reserved for the first load + // of a host. Failures surface as a toast instead of silently keeping stale + // data. + const [refreshing, setRefreshing] = useState(false); + const manualRefresh = useCallback(async () => { + if (selectedHostId === null || refreshing) return; + if (overview === null) { + loadOverview(selectedHostId); + return; + } + setRefreshing(true); + try { + const ok = await loadOverview(selectedHostId, true); + if (!ok) toast.error(t("tmuxMonitor.refreshFailed")); + } finally { + setRefreshing(false); + } + }, [selectedHostId, refreshing, overview, loadOverview, t]); + + useEffect(() => { + activeHostRef.current = selectedHostId; + setOverview(null); + setSelectedPane(null); + setSearchResults(null); + setMetrics([]); + if (selectedHostId === null) return; + try { + localStorage.setItem(LS_LAST_HOST_KEY, String(selectedHostId)); + } catch { + // localStorage may be unavailable + } + const storedExpanded = readStoredExpanded(selectedHostId); + expandedRestoredRef.current = storedExpanded !== null; + setExpandedSessions(storedExpanded ?? new Set()); + loadOverview(selectedHostId); + }, [selectedHostId, loadOverview]); + + // Poll only while this app tab is shown: the component stays mounted when + // the user switches tabs (AppShell keeps tab content alive) and would + // otherwise keep running SSH execs against the host forever. + useEffect(() => { + if (selectedHostId === null || !isVisible) return; + const interval = setInterval(() => { + if (!document.hidden) loadOverview(selectedHostId, true); + }, OVERVIEW_POLL_MS); + return () => clearInterval(interval); + }, [selectedHostId, isVisible, loadOverview]); + + // -- metrics polling ------------------------------------------------------ + useEffect(() => { + if (selectedHostId === null || !overview?.available || !isVisible) return; + let cancelled = false; + const load = () => { + if (document.hidden) return; + getTmuxMetrics(selectedHostId) + .then((m) => { + if (!cancelled) setMetrics(m); + }) + .catch(() => {}); + }; + load(); + const interval = setInterval(load, METRICS_POLL_MS); + return () => { + cancelled = true; + clearInterval(interval); + }; + }, [selectedHostId, overview?.available, isVisible]); + + // -- keyboard shortcuts ----------------------------------------------------- + // Gated on isVisible: the listener is global, and a hidden-but-mounted tab + // must not swallow "/" or Escape typed in other tabs. + useEffect(() => { + if (!isVisible) return; + const handler = (e: KeyboardEvent) => { + if (e.key === "/") { + const target = e.target as HTMLElement | null; + const tag = target?.tagName; + if (tag === "INPUT" || tag === "TEXTAREA" || target?.isContentEditable) + return; + e.preventDefault(); + searchInputRef.current?.focus(); + } else if (e.key === "Escape") { + if (searchResults !== null) setSearchResults(null); + else if (selectedPane) setSelectedPane(null); + } + }; + window.addEventListener("keydown", handler); + return () => window.removeEventListener("keydown", handler); + }, [searchResults, selectedPane, isVisible]); + + // -- search --------------------------------------------------------------- + async function runSearch() { + if (selectedHostId === null || !searchQuery.trim()) return; + const query = searchQuery.trim(); + setSearching(true); + try { + const result = await searchTmux(selectedHostId, query); + setSearchResults(result.matches); + setSearchLimits(result); + setSearchedQuery(query); + } catch { + toast.error(t("tmuxMonitor.searchFailed")); + } finally { + setSearching(false); + } + } + + const persistExpanded = useCallback((hostId: number, set: Set) => { + try { + localStorage.setItem( + expandedStorageKey(hostId), + JSON.stringify([...set]), + ); + } catch { + // localStorage may be unavailable + } + }, []); + + function toggleSession(name: string) { + const next = new Set(expandedSessions); + if (next.has(name)) next.delete(name); + else next.add(name); + setExpandedSessions(next); + expandedRestoredRef.current = true; + if (selectedHostId !== null) persistExpanded(selectedHostId, next); + } + + // Selecting a pane also focuses it on the server so the attached PTY (and + // any other tmux client) switches to that window/pane. + const selectPane = useCallback( + (pane: SelectedPane) => { + setSelectedPane(pane); + if (selectedHostId !== null) + focusTmuxPane(selectedHostId, pane.paneId).catch(() => {}); + }, + [selectedHostId], + ); + + function handleSearchSelect(match: TmuxSearchMatch) { + selectPane({ + paneId: match.paneId, + sessionName: match.sessionName, + windowIndex: match.windowIndex, + }); + if (!expandedSessions.has(match.sessionName)) { + const next = new Set(expandedSessions).add(match.sessionName); + setExpandedSessions(next); + expandedRestoredRef.current = true; + if (selectedHostId !== null) persistExpanded(selectedHostId, next); + } + } + + // -- create session --------------------------------------------------------- + const [newSessionName, setNewSessionName] = useState(""); + const [newSessionOpen, setNewSessionOpen] = useState(false); + const [creatingSession, setCreatingSession] = useState(false); + // Mirrors the backend's SESSION_NAME_RE (tmux forbids ":" and "."). + const newSessionNameValid = /^[A-Za-z0-9_@%+=-]{1,64}$/.test( + newSessionName.trim(), + ); + + async function createSession() { + if (selectedHostId === null || !newSessionNameValid || creatingSession) + return; + const name = newSessionName.trim(); + setCreatingSession(true); + try { + await createTmuxSession(selectedHostId, name); + toast.success(t("tmuxMonitor.sessionCreated", { name })); + setNewSessionOpen(false); + setNewSessionName(""); + const next = new Set(expandedSessions).add(name); + setExpandedSessions(next); + expandedRestoredRef.current = true; + persistExpanded(selectedHostId, next); + loadOverview(selectedHostId, true); + } catch (err) { + const axiosErr = err as { response?: { data?: { error?: string } } }; + toast.error( + axiosErr.response?.data?.error || t("tmuxMonitor.sessionCreateFailed"), + ); + } finally { + setCreatingSession(false); + } + } + + // -- split ------------------------------------------------------------------ + // Splits the window containing a pane. If the pane is in the previewed + // session the new pane appears in the attached terminal immediately; the + // silent overview reload updates the tree. + const splitPane = useCallback( + async (paneId: string, direction: "h" | "v") => { + if (selectedHostId === null) return; + try { + await splitTmuxPane(selectedHostId, paneId, direction); + loadOverview(selectedHostId, true); + nudgePreviewRedraw(); + } catch { + toast.error(t("tmuxMonitor.splitFailed")); + } + }, + [selectedHostId, loadOverview, nudgePreviewRedraw, t], + ); + + // -- new window --------------------------------------------------------------- + const newWindow = useCallback( + async (sessionName: string) => { + if (selectedHostId === null) return; + try { + await createTmuxWindow(selectedHostId, sessionName); + loadOverview(selectedHostId, true); + nudgePreviewRedraw(); + } catch { + toast.error(t("tmuxMonitor.windowCreateFailed")); + } + }, + [selectedHostId, loadOverview, nudgePreviewRedraw, t], + ); + + // -- collapse / expand all ---------------------------------------------------- + const anyExpanded = expandedSessions.size > 0; + function toggleAllSessions() { + if (selectedHostId === null || !overview) return; + const next = anyExpanded + ? new Set() + : new Set(overview.sessions.map((s) => s.name)); + setExpandedSessions(next); + expandedRestoredRef.current = true; + persistExpanded(selectedHostId, next); + } + + // -- rename / kill ------------------------------------------------------------ + const [renameTarget, setRenameTarget] = useState(null); + const [renameDraft, setRenameDraft] = useState(""); + const [renaming, setRenaming] = useState(false); + const renameDraftValid = /^[A-Za-z0-9_@%+=-]{1,64}$/.test(renameDraft.trim()); + const [killTarget, setKillTarget] = useState(null); + const [killing, setKilling] = useState(false); + + async function confirmRename() { + if ( + selectedHostId === null || + renameTarget === null || + !renameDraftValid || + renaming + ) + return; + const newName = renameDraft.trim(); + setRenaming(true); + try { + await renameTmuxSession(selectedHostId, renameTarget, newName); + toast.success(t("tmuxMonitor.sessionRenamed", { name: newName })); + if (expandedSessions.has(renameTarget)) { + const next = new Set(expandedSessions); + next.delete(renameTarget); + next.add(newName); + setExpandedSessions(next); + persistExpanded(selectedHostId, next); + } + // Remounts the preview (keyed by session name) so it re-attaches under + // the new name. + if (selectedPane?.sessionName === renameTarget) { + setSelectedPane({ ...selectedPane, sessionName: newName }); + } + setRenameTarget(null); + loadOverview(selectedHostId, true); + } catch (err) { + const axiosErr = err as { response?: { data?: { error?: string } } }; + toast.error( + axiosErr.response?.data?.error || t("tmuxMonitor.sessionRenameFailed"), + ); + } finally { + setRenaming(false); + } + } + + async function confirmKill() { + if (selectedHostId === null || killTarget === null || killing) return; + setKilling(true); + try { + await killTmuxSession(selectedHostId, killTarget); + toast.success(t("tmuxMonitor.sessionKilled", { name: killTarget })); + if (selectedPane?.sessionName === killTarget) setSelectedPane(null); + if (expandedSessions.has(killTarget)) { + const next = new Set(expandedSessions); + next.delete(killTarget); + setExpandedSessions(next); + persistExpanded(selectedHostId, next); + } + setKillTarget(null); + loadOverview(selectedHostId, true); + } catch (err) { + const axiosErr = err as { response?: { data?: { error?: string } } }; + toast.error( + axiosErr.response?.data?.error || t("tmuxMonitor.sessionKillFailed"), + ); + } finally { + setKilling(false); + } + } + + // After killing the selected pane/window, keep the preview on the same + // session by selecting its now-active pane (tmux focuses a sibling + // automatically); only fall back to the empty state when the whole session + // died with it. + const selectSurvivor = useCallback( + (data: TmuxOverview, sessionName: string) => { + const session = data.sessions.find((s) => s.name === sessionName); + const win = session?.windows.find((w) => w.active) ?? session?.windows[0]; + const pane = win?.panes.find((p) => p.active) ?? win?.panes[0]; + if (session && win && pane) { + setSelectedPane({ + paneId: pane.id, + sessionName: session.name, + windowIndex: win.index, + }); + } else { + setSelectedPane(null); + } + }, + [], + ); + + // -- kill window ------------------------------------------------------------ + const [killWindowTarget, setKillWindowTarget] = useState<{ + sessionName: string; + windowIndex: number; + } | null>(null); + const [killingWindow, setKillingWindow] = useState(false); + + async function confirmKillWindow() { + if (selectedHostId === null || killWindowTarget === null || killingWindow) + return; + setKillingWindow(true); + try { + await killTmuxWindow( + selectedHostId, + killWindowTarget.sessionName, + killWindowTarget.windowIndex, + ); + const wasViewing = + selectedPane?.sessionName === killWindowTarget.sessionName && + selectedPane?.windowIndex === killWindowTarget.windowIndex; + setKillWindowTarget(null); + const data = await getTmuxOverview(selectedHostId); + setOverview(data); + if (wasViewing) selectSurvivor(data, killWindowTarget.sessionName); + nudgePreviewRedraw(); + } catch (err) { + const axiosErr = err as { response?: { data?: { error?: string } } }; + toast.error( + axiosErr.response?.data?.error || t("tmuxMonitor.windowKillFailed"), + ); + } finally { + setKillingWindow(false); + } + } + + // -- kill pane ------------------------------------------------------------ + const [killPaneTarget, setKillPaneTarget] = useState(null); + const [killingPane, setKillingPane] = useState(false); + + async function confirmKillPane() { + if (selectedHostId === null || killPaneTarget === null || killingPane) + return; + setKillingPane(true); + try { + await killTmuxPane(selectedHostId, killPaneTarget); + const viewedSession = + selectedPane?.paneId === killPaneTarget + ? selectedPane.sessionName + : null; + setKillPaneTarget(null); + const data = await getTmuxOverview(selectedHostId); + setOverview(data); + if (viewedSession) selectSurvivor(data, viewedSession); + nudgePreviewRedraw(); + } catch (err) { + const axiosErr = err as { response?: { data?: { error?: string } } }; + toast.error( + axiosErr.response?.data?.error || t("tmuxMonitor.paneKillFailed"), + ); + } finally { + setKillingPane(false); + } + } + + // -- tags ----------------------------------------------------------------- + const [tagsTarget, setTagsTarget] = useState(null); + const [tagsDraft, setTagsDraft] = useState(""); + const [savingTags, setSavingTags] = useState(false); + + function openTagsEditor(sessionName: string) { + const session = overview?.sessions.find((s) => s.name === sessionName); + setTagsDraft(session?.tags.join(", ") ?? ""); + setTagsTarget(sessionName); + } + + async function confirmTags() { + if (selectedHostId === null || tagsTarget === null || savingTags) return; + const tags = tagsDraft + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + setSavingTags(true); + try { + await setTmuxSessionTags(selectedHostId, tagsTarget, tags); + toast.success(t("tmuxMonitor.tagsSaved")); + setTagsTarget(null); + loadOverview(selectedHostId, true); + } catch { + toast.error(t("tmuxMonitor.tagsSaveFailed")); + } finally { + setSavingTags(false); + } + } + + // Attach inside the monitor: expand the session and select its active pane + // so the embedded terminal (a real tmux attach) shows it. + const attachInline = useCallback( + (sessionName: string) => { + const session = overview?.sessions.find((s) => s.name === sessionName); + if (!session) return; + const activeWindow = + session.windows.find((w) => w.active) ?? session.windows[0]; + const activePane = + activeWindow?.panes.find((p) => p.active) ?? activeWindow?.panes[0]; + if (!activePane || !activeWindow) return; + if (!expandedSessions.has(sessionName)) { + const next = new Set(expandedSessions).add(sessionName); + setExpandedSessions(next); + expandedRestoredRef.current = true; + if (selectedHostId !== null) persistExpanded(selectedHostId, next); + } + selectPane({ + paneId: activePane.id, + sessionName, + windowIndex: activeWindow.index, + }); + }, + [overview, expandedSessions, selectedHostId, persistExpanded, selectPane], + ); + + function openTerminal(sessionName?: string) { + if (selectedHostId === null) return; + const session = sessionName ?? selectedPane?.sessionName; + // window.open is denied by Electron's window-open handler (internal + // file:// URLs never reach the browser), so the desktop app attaches in + // the monitor's own terminal instead. + if (isElectron()) { + if (session) attachInline(session); + return; + } + const params = new URLSearchParams({ + view: "terminal", + hostId: String(selectedHostId), + }); + if (session) params.set("tmuxSession", session); + window.open(`${window.location.pathname}?${params.toString()}`, "_blank"); + } + + const metricsByPane = useMemo(() => { + const map = new Map(); + for (const m of metrics) map.set(m.paneId, m); + return map; + }, [metrics]); + + const metricsBySession = useMemo(() => { + const map = new Map(); + for (const m of metrics) { + const agg = map.get(m.sessionName) || { cpu: 0, memKb: 0, gpuMb: 0 }; + agg.cpu += m.cpuPercent; + agg.memKb += m.memRssKb; + agg.gpuMb += m.gpuMemMb; + map.set(m.sessionName, agg); + } + return map; + }, [metrics]); + + const selectedHost = hosts.find((h) => h.id === selectedHostId); + const hostLabel = selectedHost + ? selectedHost.name || `${selectedHost.username}@${selectedHost.ip}` + : ""; + const selectedPaneMetrics = selectedPane + ? metricsByPane.get(selectedPane.paneId) + : undefined; + + return ( +
+ {/* Left rail: hosts + session tree. Resizable via the right-edge + handle; double-click resets to the default width. */} +
+ {/* VSCode tmux-manager style header: title + new-session / refresh */} +
+ + + {t("tmuxMonitor.title")} + + + { + setNewSessionOpen(open); + if (open) setNewSessionName(""); + }} + > + + + + +

+ {t("tmuxMonitor.newSessionHint")} +

+
+ setNewSessionName(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") createSession(); + }} + /> + +
+
+
+ + + + + +
+
+ {/* Radix wraps the viewport content in a display:table div sized to + the widest row, so one long pane path would stretch every row and + clip the right-aligned actions; force block so rows shrink and + truncate instead. */} + +
+ {!hostsLoading && hosts.length === 0 && ( +
+

+ {t("tmuxMonitor.noHosts")} +

+

+ {t("tmuxMonitor.noHostsHint")} +

+
+ )} + {overviewLoading && ( +
+ {[0, 1, 2].map((i) => ( +
+ + + +
+ ))} +
+ )} + {overviewError && ( +
+

{overviewError}

+ +
+ )} + {overview && !overview.available && ( +
+

+ {t("tmuxMonitor.tmuxUnavailable")} +

+

+ {t("tmuxMonitor.tmuxInstallHint")}{" "} + sudo apt install tmux +

+
+ )} + {overview?.available && overview.sessions.length === 0 && ( +

+ {t("tmuxMonitor.noSessions")} +

+ )} + {overview?.available && ( + { + setRenameDraft(name); + setRenameTarget(name); + }} + onKillSession={setKillTarget} + onKillPane={setKillPaneTarget} + onSplitPane={splitPane} + onKillWindow={(sessionName, windowIndex) => + setKillWindowTarget({ sessionName, windowIndex }) + } + compact={treeWidth < 280} + now={now} + /> + )} +
+
+ +
setTreeWidth(TREE_WIDTH_DEFAULT)} + title={t("tmuxMonitor.resizeTree")} + className={`absolute bottom-0 right-0 top-0 z-30 w-1 cursor-col-resize transition-colors ${treeDragging ? "bg-accent-brand/60" : "hover:bg-accent-brand/40"}`} + /> +
+ + {/* Main area */} +
+ {/* Toolbar */} +
+ + + {selectedHost ? hostLabel : t("tmuxMonitor.noHostSelected")} + +
+
+ + setSearchQuery(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") runSearch(); + }} + /> +
+ + +
+
+ + {/* Search results */} + {searchResults !== null && ( + setSearchResults(null)} + /> + )} + + {/* Pane preview */} +
+ {selectedPane && selectedHost ? ( + splitPane(selectedPane.paneId, direction)} + onKillPane={() => setKillPaneTarget(selectedPane.paneId)} + onClose={() => setSelectedPane(null)} + /> + ) : !hostsLoading && hosts.length === 0 ? ( +
+
+ +

{t("tmuxMonitor.noHosts")}

+

+ {t("tmuxMonitor.noHostsHint")} +

+
+
+ ) : ( +
+
+ +

{t("tmuxMonitor.selectPaneHint")}

+
+
+ )} +
+
+ + {/* Rename session dialog */} + { + if (!open) setRenameTarget(null); + }} + > + + + + {t("tmuxMonitor.renameSessionTitle", { name: renameTarget })} + + + setRenameDraft(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") confirmRename(); + }} + /> +

+ {t("tmuxMonitor.newSessionHint")} +

+ + + + +
+
+ + {/* Edit tags dialog */} + { + if (!open) setTagsTarget(null); + }} + > + + + + {t("tmuxMonitor.editTagsTitle", { name: tagsTarget })} + + + setTagsDraft(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") confirmTags(); + }} + /> +

+ {t("tmuxMonitor.tagsHint")} +

+ + + + +
+
+ + {/* Kill window confirmation */} + { + if (!open) setKillWindowTarget(null); + }} + > + + + + {t("tmuxMonitor.killWindowTitle", { + index: killWindowTarget?.windowIndex, + session: killWindowTarget?.sessionName, + })} + + +

+ {t("tmuxMonitor.killWindowBody")} +

+ + + + +
+
+ + {/* Kill pane confirmation */} + { + if (!open) setKillPaneTarget(null); + }} + > + + + + {t("tmuxMonitor.killPaneTitle", { id: killPaneTarget })} + + +

+ {t("tmuxMonitor.killPaneBody")} +

+ + + + +
+
+ + {/* Kill session confirmation */} + { + if (!open) setKillTarget(null); + }} + > + + + + {t("tmuxMonitor.killSessionTitle", { name: killTarget })} + + +

+ {t("tmuxMonitor.killSessionBody")} +

+ + + + +
+
+
+ ); +} + +export default TmuxMonitor; diff --git a/src/ui/features/tmux-monitor/TmuxMonitorApp.tsx b/src/ui/features/tmux-monitor/TmuxMonitorApp.tsx new file mode 100644 index 00000000..a435ab59 --- /dev/null +++ b/src/ui/features/tmux-monitor/TmuxMonitorApp.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { TmuxMonitor } from "@/features/tmux-monitor/TmuxMonitor.tsx"; + +interface TmuxMonitorAppProps { + hostId?: string; +} + +const TmuxMonitorApp: React.FC = ({ hostId }) => { + const parsed = hostId ? parseInt(hostId, 10) : NaN; + return ( +
+ +
+ ); +}; + +export default TmuxMonitorApp; diff --git a/src/ui/features/tmux-monitor/format.ts b/src/ui/features/tmux-monitor/format.ts new file mode 100644 index 00000000..0e705ab9 --- /dev/null +++ b/src/ui/features/tmux-monitor/format.ts @@ -0,0 +1,20 @@ +export function formatRelativeTime( + unixSeconds: number, + nowMs: number, + t: (k: string, o?: object) => string, +): string { + if (!unixSeconds) return ""; + const diff = Math.max(0, nowMs / 1000 - unixSeconds); + if (diff < 60) return t("tmuxMonitor.timeJustNow"); + if (diff < 3600) + return t("tmuxMonitor.timeMinutes", { count: Math.floor(diff / 60) }); + if (diff < 86400) + return t("tmuxMonitor.timeHours", { count: Math.floor(diff / 3600) }); + return t("tmuxMonitor.timeDays", { count: Math.floor(diff / 86400) }); +} + +export function formatMem(kb: number): string { + if (kb >= 1024 * 1024) return `${(kb / 1024 / 1024).toFixed(1)} GB`; + if (kb >= 1024) return `${(kb / 1024).toFixed(0)} MB`; + return `${kb} KB`; +} diff --git a/src/ui/features/tmux-monitor/types.ts b/src/ui/features/tmux-monitor/types.ts new file mode 100644 index 00000000..f9fbe667 --- /dev/null +++ b/src/ui/features/tmux-monitor/types.ts @@ -0,0 +1,5 @@ +export interface SelectedPane { + paneId: string; + sessionName: string; + windowIndex: number; +} diff --git a/src/ui/features/tunnel/TunnelTab.tsx b/src/ui/features/tunnel/TunnelTab.tsx index 97cfa8e9..d91efeb8 100644 --- a/src/ui/features/tunnel/TunnelTab.tsx +++ b/src/ui/features/tunnel/TunnelTab.tsx @@ -5,6 +5,7 @@ import { Card } from "@/components/card"; import { AlertCircle, Clock, + ExternalLink, Network, Play, RefreshCw, @@ -426,6 +427,15 @@ export function TunnelTab({ host }: { label: string; host?: DemoHost }) {
+ + + {tunnels.length > 0 ? ( diff --git a/src/ui/lib/clipboard.test.ts b/src/ui/lib/clipboard.test.ts new file mode 100644 index 00000000..f511b46c --- /dev/null +++ b/src/ui/lib/clipboard.test.ts @@ -0,0 +1,86 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { copyToClipboard } from "./clipboard"; + +describe("copyToClipboard", () => { + const originalClipboard = navigator.clipboard; + + beforeEach(() => { + delete (window as { electronClipboard?: unknown }).electronClipboard; + }); + + afterEach(() => { + Object.defineProperty(navigator, "clipboard", { + value: originalClipboard, + configurable: true, + }); + vi.restoreAllMocks(); + }); + + function setClipboard(value: unknown) { + Object.defineProperty(navigator, "clipboard", { + value, + configurable: true, + }); + } + + it("uses the electron bridge when present", async () => { + const writeText = vi.fn().mockResolvedValue(true); + (window as { electronClipboard?: unknown }).electronClipboard = { + writeText, + readText: vi.fn(), + }; + + const ok = await copyToClipboard("hello"); + + expect(ok).toBe(true); + expect(writeText).toHaveBeenCalledWith("hello"); + }); + + it("uses navigator.clipboard when available", async () => { + const writeText = vi.fn().mockResolvedValue(undefined); + setClipboard({ writeText }); + + const ok = await copyToClipboard("world"); + + expect(ok).toBe(true); + expect(writeText).toHaveBeenCalledWith("world"); + }); + + it("falls back to execCommand when navigator.clipboard is undefined (Brave / non-HTTPS)", async () => { + setClipboard(undefined); + const execCommand = vi.fn().mockReturnValue(true); + (document as unknown as { execCommand: typeof execCommand }).execCommand = + execCommand; + + const ok = await copyToClipboard("fallback"); + + expect(ok).toBe(true); + expect(execCommand).toHaveBeenCalledWith("copy"); + }); + + it("falls back to execCommand when the async write throws", async () => { + setClipboard({ + writeText: vi.fn().mockRejectedValue(new Error("denied")), + }); + const execCommand = vi.fn().mockReturnValue(true); + (document as unknown as { execCommand: typeof execCommand }).execCommand = + execCommand; + + const ok = await copyToClipboard("retry"); + + expect(ok).toBe(true); + expect(execCommand).toHaveBeenCalledWith("copy"); + }); + + it("returns false when every approach fails", async () => { + setClipboard(undefined); + (document as unknown as { execCommand: () => boolean }).execCommand = + () => { + throw new Error("no execCommand"); + }; + + const ok = await copyToClipboard("nope"); + + expect(ok).toBe(false); + }); +}); diff --git a/src/ui/lib/clipboard.ts b/src/ui/lib/clipboard.ts new file mode 100644 index 00000000..4fa1a74a --- /dev/null +++ b/src/ui/lib/clipboard.ts @@ -0,0 +1,48 @@ +/** + * Copies text to the clipboard, working around environments where the async + * Clipboard API is unavailable - notably Brave and any non-HTTPS origin, where + * `navigator.clipboard` is undefined. Tries the Electron bridge first, then the + * async Clipboard API, then a hidden-textarea execCommand fallback. + * + * Returns true on success so callers can decide whether to show a success or + * error toast. + */ +export async function copyToClipboard(text: string): Promise { + if (window.electronClipboard) { + try { + await window.electronClipboard.writeText(text); + return true; + } catch { + // fall through to browser approaches + } + } + + if (navigator.clipboard?.writeText) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + // fall through to the legacy fallback + } + } + + return legacyCopy(text); +} + +function legacyCopy(text: string): boolean { + try { + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.setAttribute("readonly", ""); + textarea.style.position = "fixed"; + textarea.style.top = "-9999px"; + textarea.style.left = "-9999px"; + document.body.appendChild(textarea); + textarea.select(); + const ok = document.execCommand("copy"); + document.body.removeChild(textarea); + return ok; + } catch { + return false; + } +} diff --git a/src/ui/lib/terminal-syntax-highlighter.test.ts b/src/ui/lib/terminal-syntax-highlighter.test.ts index 3c8b25f6..fb89f9b7 100644 --- a/src/ui/lib/terminal-syntax-highlighter.test.ts +++ b/src/ui/lib/terminal-syntax-highlighter.test.ts @@ -9,17 +9,36 @@ describe("highlightTerminalOutput", () => { expect(highlightTerminalOutput(" ")).toBe(" "); }); - it("wraps error keywords in red ANSI codes", () => { + it("wraps ERROR in bright red", () => { const out = highlightTerminalOutput("Something ERROR happened"); expect(out).toContain(ESC + "[91m"); - expect(out).toContain(ESC + "[0m"); expect(out).toContain("ERROR"); }); - it("highlights IPv4 addresses", () => { + it("wraps lowercase error in bright red (case-insensitive)", () => { + const out = highlightTerminalOutput("something error happened"); + expect(out).toContain(ESC + "[91m"); + }); + + it("wraps Error (mixed case) in bright red", () => { + const out = highlightTerminalOutput("Error: connection refused"); + expect(out).toContain(ESC + "[91m"); + }); + + it("wraps warn (lowercase) in bright yellow", () => { + const out = highlightTerminalOutput("warn: disk almost full"); + expect(out).toContain(ESC + "[93m"); + }); + + it("wraps warning (lowercase) in bright yellow", () => { + const out = highlightTerminalOutput("warning: something is off"); + expect(out).toContain(ESC + "[93m"); + }); + + it("highlights IPv4 addresses in magenta", () => { const out = highlightTerminalOutput("connect to 192.168.1.10 now"); expect(out).toContain("192.168.1.10"); - expect(out).toContain(ESC + "[35m"); // magenta + expect(out).toContain(ESC + "[35m"); }); it("leaves text with no matchable tokens unchanged", () => { @@ -27,20 +46,156 @@ describe("highlightTerminalOutput", () => { expect(highlightTerminalOutput(plain)).toBe(plain); }); - it("does not re-highlight text that already contains many ANSI codes", () => { - let heavy = ""; - for (let i = 0; i < 12; i++) heavy += `${ESC}[32mgreen${ESC}[0m `; - expect(highlightTerminalOutput(heavy)).toBe(heavy); - }); - it("leaves text ending in an incomplete ANSI escape untouched", () => { const partial = `loading${ESC}[`; expect(highlightTerminalOutput(partial)).toBe(partial); }); - it("does not exceed MAX_LINE_LENGTH processing on huge lines", () => { - const huge = "ERROR " + "x".repeat(6000); - // Over the 5000 char cap, highlightPlainText returns the input unchanged. + it("skips TUI cursor-positioning frames (nano/vim protection)", () => { + const tuiFrame = `${ESC}[H${ESC}[2J some nano content`; + expect(highlightTerminalOutput(tuiFrame)).toBe(tuiFrame); + }); + + it("highlights text that already has ANSI codes", () => { + let heavy = ""; + for (let i = 0; i < 12; i++) heavy += `${ESC}[32mgreen${ESC}[0m `; + heavy += "ERROR at line 5"; + const out = highlightTerminalOutput(heavy); + expect(out).toContain(ESC + "[91m"); + }); + + it("does not process lines exceeding MAX_LINE_LENGTH", () => { + const huge = "ERROR " + "x".repeat(3000); expect(highlightTerminalOutput(huge)).toBe(huge); }); + + it("highlights simple absolute paths in cyan", () => { + const out = highlightTerminalOutput("log at /var/log/nginx/access.log"); + expect(out).toContain(ESC + "[36m"); + expect(out).toContain("/var/log/nginx/access.log"); + }); + + it("highlights absolute paths with glob wildcards in cyan", () => { + const out = highlightTerminalOutput("files: /usr/share/doc/*/copyright"); + expect(out).toContain(ESC + "[36m"); + expect(out).toContain("/usr/share/doc/*/copyright"); + }); + + it("highlights home paths in cyan", () => { + const out = highlightTerminalOutput("file: ~/documents/notes.txt"); + expect(out).toContain(ESC + "[36m"); + expect(out).toContain("~/documents/notes.txt"); + }); + + it("highlights bracket timestamps in bright black", () => { + const out = highlightTerminalOutput("[12:34:56] server started"); + expect(out).toContain(ESC + "[90m"); + expect(out).toContain("[12:34:56]"); + }); + + it("does not highlight out-of-range bracket timestamps", () => { + // [99:99] is not a valid time + const out = highlightTerminalOutput("[99:99] something"); + expect(out).not.toContain(ESC + "[90m"); + }); + + it("highlights ISO date timestamps in bright black", () => { + const out = highlightTerminalOutput("2024-01-15 event occurred"); + expect(out).toContain(ESC + "[90m"); + expect(out).toContain("2024-01-15"); + }); + + it("does not highlight version strings as ISO dates", () => { + // 2.4.0 or 1.2024.3 should not trigger ISO date + const out = highlightTerminalOutput("version 2.4.0 released"); + expect(out).toBe("version 2.4.0 released"); + }); + + it("highlights labeled numbers (port)", () => { + const out = highlightTerminalOutput("listening on port 8080"); + expect(out).toContain(ESC + "[96m"); + expect(out).toContain("8080"); + }); + + it("highlights labeled numbers (exit)", () => { + const out = highlightTerminalOutput("process exited with exit 1"); + expect(out).toContain(ESC + "[96m"); + }); + + it("highlights labeled numbers (status)", () => { + const out = highlightTerminalOutput("returned status 404"); + expect(out).toContain(ESC + "[96m"); + }); + + it("does not highlight standalone numbers outside labeled context", () => { + const out = highlightTerminalOutput("there are 42 files here"); + expect(out).toBe("there are 42 files here"); + }); + + it("does not highlight 'up' or 'active' as success", () => { + const out = highlightTerminalOutput("service is up and running"); + expect(out).not.toContain(ESC + "[92m"); + }); + + it("highlights unambiguous success keywords (case-insensitive)", () => { + const out = highlightTerminalOutput("Test passed successfully"); + expect(out).toContain(ESC + "[92m"); + }); + + it("highlights URLs with blue+underline", () => { + const out = highlightTerminalOutput("visit https://example.com now"); + expect(out).toContain(ESC + "[34m"); + expect(out).toContain(ESC + "[4m"); + }); + + it("preserves \\r in CRLF terminal output", () => { + const out = highlightTerminalOutput("ERROR occurred\r\n"); + expect(out).toContain("\r\n"); + }); + + it("processes multi-line text line by line", () => { + const text = "some output\nERROR: failed"; + const out = highlightTerminalOutput(text); + expect(out).toContain(ESC + "[91m"); + }); + + it("respects category options - disabling logLevels skips error highlight", () => { + const out = highlightTerminalOutput("ERROR occurred", { logLevels: false }); + expect(out).not.toContain(ESC + "[91m"); + }); + + it("respects category options - disabling paths skips path highlight", () => { + const out = highlightTerminalOutput("at /usr/share/doc/something/file", { + paths: false, + }); + expect(out).not.toContain(ESC + "[36m"); + }); + + it("respects category options - disabling urls skips url highlight", () => { + const out = highlightTerminalOutput("see https://example.com", { + urls: false, + }); + expect(out).not.toContain(ESC + "[34m"); + }); + + it("respects category options - disabling ipAddresses skips IP highlight", () => { + const out = highlightTerminalOutput("connect to 192.168.1.1", { + ipAddresses: false, + }); + expect(out).not.toContain(ESC + "[35m"); + }); + + it("respects category options - disabling timestamps skips ISO date highlight", () => { + const out = highlightTerminalOutput("2024-01-15 something", { + timestamps: false, + }); + expect(out).not.toContain(ESC + "[90m"); + }); + + it("respects category options - disabling numbers skips port highlight", () => { + const out = highlightTerminalOutput("listening on port 8080", { + numbers: false, + }); + expect(out).not.toContain(ESC + "[96m"); + }); }); diff --git a/src/ui/lib/terminal-syntax-highlighter.ts b/src/ui/lib/terminal-syntax-highlighter.ts index b9195670..64a2825a 100644 --- a/src/ui/lib/terminal-syntax-highlighter.ts +++ b/src/ui/lib/terminal-syntax-highlighter.ts @@ -1,36 +1,36 @@ -const ANSI_CODES = { +const ANSI = { reset: "\x1b[0m", - colors: { - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - white: "\x1b[37m", - brightBlack: "\x1b[90m", - brightRed: "\x1b[91m", - brightGreen: "\x1b[92m", - brightYellow: "\x1b[93m", - brightBlue: "\x1b[94m", - brightMagenta: "\x1b[95m", - brightCyan: "\x1b[96m", - brightWhite: "\x1b[97m", - }, - styles: { - bold: "\x1b[1m", - dim: "\x1b[2m", - italic: "\x1b[3m", - underline: "\x1b[4m", - }, + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + brightBlack: "\x1b[90m", + brightRed: "\x1b[91m", + brightGreen: "\x1b[92m", + brightYellow: "\x1b[93m", + brightBlue: "\x1b[94m", + brightCyan: "\x1b[96m", + bold: "\x1b[1m", + underline: "\x1b[4m", } as const; +export interface SyntaxHighlightOptions { + logLevels?: boolean; + paths?: boolean; + timestamps?: boolean; + ipAddresses?: boolean; + urls?: boolean; + numbers?: boolean; +} + interface HighlightPattern { name: string; regex: RegExp; ansiCode: string; priority: number; - quickCheck?: string; + category: keyof SyntaxHighlightOptions; } interface MatchResult { @@ -38,226 +38,308 @@ interface MatchResult { end: number; ansiCode: string; priority: number; -} - -const MAX_LINE_LENGTH = 5000; -const MAX_ANSI_CODES = 10; - -const PATTERNS: HighlightPattern[] = [ - { - name: "ipv4", - regex: - /(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?::\d{1,5})?/g, - ansiCode: ANSI_CODES.colors.magenta, - priority: 10, - }, - - { - name: "log-error", - regex: - /\b(ERROR|FATAL|CRITICAL|FAIL(?:ED)?|denied|invalid|DENIED)\b|\[ERROR\]/gi, - ansiCode: ANSI_CODES.colors.brightRed, - priority: 9, - }, - - { - name: "log-warn", - regex: /\b(WARN(?:ING)?|ALERT)\b|\[WARN(?:ING)?\]/gi, - ansiCode: ANSI_CODES.colors.yellow, - priority: 9, - }, - - { - name: "log-success", - regex: - /\b(SUCCESS|OK|PASS(?:ED)?|COMPLETE(?:D)?|connected|active|up|Up|UP|FULL)\b/gi, - ansiCode: ANSI_CODES.colors.brightGreen, - priority: 8, - }, - - { - name: "url", - regex: /https?:\/\/[^\s\])}]+/g, - ansiCode: `${ANSI_CODES.colors.blue}${ANSI_CODES.styles.underline}`, - priority: 8, - }, - - { - name: "path-absolute", - regex: /\/[a-zA-Z][a-zA-Z0-9_\-@.]*(?:\/[a-zA-Z0-9_\-@.]+)+/g, - ansiCode: ANSI_CODES.colors.cyan, - priority: 7, - }, - - { - name: "path-home", - regex: /~\/[a-zA-Z0-9_\-@./]+/g, - ansiCode: ANSI_CODES.colors.cyan, - priority: 7, - }, - - { - name: "log-info", - regex: /\bINFO\b|\[INFO\]/gi, - ansiCode: ANSI_CODES.colors.blue, - priority: 6, - }, - { - name: "log-debug", - regex: /\b(?:DEBUG|TRACE)\b|\[(?:DEBUG|TRACE)\]/gi, - ansiCode: ANSI_CODES.colors.brightBlack, - priority: 6, - }, -]; - -function hasExistingAnsiCodes(text: string): boolean { - const ansiCount = ( - text.match( - /\x1b[[\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nq-uy=><~]/g, - ) || [] - ).length; - return ansiCount > MAX_ANSI_CODES; -} - -function hasIncompleteAnsiSequence(text: string): boolean { - return /\x1b(?:\[(?:[0-9;?>=!]*)?)?$/.test(text); + // capture group offset: some patterns capture a sub-group rather than whole match + captureStart?: number; + captureEnd?: number; } interface TextSegment { isAnsi: boolean; content: string; + // the last active SGR code before this segment (for state restoration) + activeSgr?: string; +} + +const MAX_LINE_LENGTH = 2000; + +// Cursor-positioning and erase sequences used by TUI apps (nano, vim, htop). +// If a chunk contains these, we skip highlighting entirely. +const TUI_SEQUENCE = /\x1b\[[\d;]*[ABCDEFGHJKST]/; + +// Matches any complete ANSI escape sequence +const ANSI_REGEX = /\x1b(?:[@-Z\\-_]|\[[0-9;?>=!]*[@-~])/g; + +// Matches SGR sequences (color/style setters) specifically — used to track active color state +const SGR_REGEX = /\x1b\[[0-9;]*m/; + +// All patterns, ordered roughly by specificity. Priority determines which wins on overlap. +const ALL_PATTERNS: HighlightPattern[] = [ + // IPv4 with optional :port — well-bounded, very specific + { + name: "ipv4", + regex: + /(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?::\d{1,5})?/g, + ansiCode: ANSI.magenta, + priority: 10, + category: "ipAddresses", + }, + + // Bracket timestamps [HH:MM] or [HH:MM:SS] — tight range checks prevent false positives + { + name: "timestamp-bracket", + regex: /\[(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?\]/g, + ansiCode: ANSI.brightBlack, + priority: 9, + category: "timestamps", + }, + + // ISO 8601 date with optional time. Require non-digit before to avoid matching + // inside version strings like "1.2024.3". The \b on the right ensures we don't + // match partial tokens. + { + name: "timestamp-iso", + regex: + /(?"']+/g, + ansiCode: `${ANSI.blue}${ANSI.underline}`, + priority: 8, + category: "urls", + }, + + // Absolute paths — permissive character class that allows *, ?, [], globs, etc. + // Requires at least one slash after the root segment so we don't match bare /dev or /tmp. + // Boundary: must start at beginning of text or after whitespace/colon/comma/paren. + { + name: "path-absolute", + regex: + /(?:(?<=^)|(?<=[\s:,;('"]))\/[^\s"'`|<>&;\\]+(?:\/[^\s"'`|<>&;\\]+)+/g, + ansiCode: ANSI.cyan, + priority: 7, + category: "paths", + }, + + // Home-relative paths + { + name: "path-home", + regex: /~\/[^\s"'`|<>&;(){}[\]\\]+/g, + ansiCode: ANSI.cyan, + priority: 7, + category: "paths", + }, + + // Info keyword + { + name: "log-info", + regex: /\binfo\b|\[info\]/gi, + ansiCode: ANSI.blue, + priority: 6, + category: "logLevels", + }, + + // Debug/trace + { + name: "log-debug", + regex: /\b(?:debug|trace|verbose)\b|\[(?:debug|trace)\]/gi, + ansiCode: ANSI.brightBlack, + priority: 6, + category: "logLevels", + }, + + // Labeled numbers: port 8080, exit 1, code 127, status 404, signal 9, returned 0 + // Captures just the number portion so only the digit is highlighted. + { + name: "number-labeled", + regex: /\b(?:port|exit|code|status|signal|returned?)\s+(\d+)\b/gi, + ansiCode: ANSI.brightCyan, + priority: 5, + category: "numbers", + }, +]; + +function hasIncompleteAnsiSequence(text: string): boolean { + return /\x1b\[[0-9;?>=!]*$/.test(text); } function parseAnsiSegments(text: string): TextSegment[] { const segments: TextSegment[] = []; - const ansiRegex = /\x1b(?:[@-Z\\-_]|\[[0-9;?>=!]*[@-~])/g; + ANSI_REGEX.lastIndex = 0; let lastIndex = 0; + let activeSgr = ""; let match; - while ((match = ansiRegex.exec(text)) !== null) { + while ((match = ANSI_REGEX.exec(text)) !== null) { if (match.index > lastIndex) { segments.push({ isAnsi: false, content: text.slice(lastIndex, match.index), + activeSgr, }); } - - segments.push({ - isAnsi: true, - content: match[0], - }); - - lastIndex = ansiRegex.lastIndex; + const seq = match[0]; + segments.push({ isAnsi: true, content: seq }); + // Track the active SGR state so we can restore it after a highlight reset + if (SGR_REGEX.test(seq)) { + activeSgr = seq === ANSI.reset ? "" : seq; + } + lastIndex = ANSI_REGEX.lastIndex; } if (lastIndex < text.length) { - segments.push({ - isAnsi: false, - content: text.slice(lastIndex), - }); + segments.push({ isAnsi: false, content: text.slice(lastIndex), activeSgr }); } return segments; } -function highlightPlainText(text: string): string { - if (text.length > MAX_LINE_LENGTH) { - return text; - } - - if (!text.trim()) { - return text; - } +function highlightPlainText( + text: string, + activePatterns: HighlightPattern[], + activeSgr: string, +): string { + if (text.length > MAX_LINE_LENGTH || !text.trim()) return text; const matches: MatchResult[] = []; - for (const pattern of PATTERNS) { + for (const pattern of activePatterns) { pattern.regex.lastIndex = 0; - - let match; - while ((match = pattern.regex.exec(text)) !== null) { - matches.push({ - start: match.index, - end: match.index + match[0].length, - ansiCode: pattern.ansiCode, - priority: pattern.priority, - }); + let m; + while ((m = pattern.regex.exec(text)) !== null) { + // For patterns with a capture group (labeled numbers), highlight only the capture + if (m[1] !== undefined) { + const captureOffset = m[0].indexOf(m[1], m[0].search(/\d/)); + const captureStart = m.index + captureOffset; + const captureEnd = captureStart + m[1].length; + matches.push({ + start: m.index, + end: m.index + m[0].length, + ansiCode: pattern.ansiCode, + priority: pattern.priority, + captureStart, + captureEnd, + }); + } else { + matches.push({ + start: m.index, + end: m.index + m[0].length, + ansiCode: pattern.ansiCode, + priority: pattern.priority, + }); + } } } - if (matches.length === 0) { - return text; - } + if (matches.length === 0) return text; - matches.sort((a, b) => { - if (a.priority !== b.priority) { - return b.priority - a.priority; - } - return a.start - b.start; - }); + // Highest priority wins; ties broken by earlier start position + matches.sort((a, b) => + a.priority !== b.priority ? b.priority - a.priority : a.start - b.start, + ); - const appliedRanges: Array<{ start: number; end: number }> = []; - const finalMatches = matches.filter((match) => { - const overlaps = appliedRanges.some( - (range) => - (match.start >= range.start && match.start < range.end) || - (match.end > range.start && match.end <= range.end) || - (match.start <= range.start && match.end >= range.end), + const used: Array<{ start: number; end: number }> = []; + const final = matches.filter((m) => { + const overlaps = used.some( + (r) => + (m.start >= r.start && m.start < r.end) || + (m.end > r.start && m.end <= r.end) || + (m.start <= r.start && m.end >= r.end), ); - if (!overlaps) { - appliedRanges.push({ start: match.start, end: match.end }); + used.push({ start: m.start, end: m.end }); return true; } return false; }); - let result = text; - finalMatches.reverse().forEach((match) => { - const before = result.slice(0, match.start); - const matched = result.slice(match.start, match.end); - const after = result.slice(match.end); + // Apply in reverse order so indices stay valid as we insert escape codes + final.sort((a, b) => a.start - b.start).reverse(); - result = before + match.ansiCode + matched + ANSI_CODES.reset + after; - }); + let result = text; + for (const m of final) { + const hs = m.captureStart ?? m.start; + const he = m.captureEnd ?? m.end; + + // After reset, re-emit the SGR that was active before this plain-text segment + // so we don't clobber colors set by the server earlier in the line. + const restore = activeSgr || ""; + result = + result.slice(0, hs) + + m.ansiCode + + result.slice(hs, he) + + ANSI.reset + + restore + + result.slice(he); + } return result; } -export function highlightTerminalOutput(text: string): string { - if (!text || !text.trim()) { - return text; - } - - if (hasIncompleteAnsiSequence(text)) { - return text; - } - - if (hasExistingAnsiCodes(text)) { - return text; - } - - const segments = parseAnsiSegments(text); - - if (segments.length === 0) { - return highlightPlainText(text); - } - - const highlightedSegments = segments.map((segment) => { - if (segment.isAnsi) { - return segment.content; - } else { - return highlightPlainText(segment.content); - } - }); - - return highlightedSegments.join(""); +function buildActivePatterns( + options: SyntaxHighlightOptions, +): HighlightPattern[] { + return ALL_PATTERNS.filter((p) => options[p.category] !== false); } -export function isSyntaxHighlightingEnabled(): boolean { - try { - return localStorage.getItem("terminalSyntaxHighlighting") === "true"; - } catch { - return false; - } +function highlightLine( + line: string, + activePatterns: HighlightPattern[], +): string { + const cr = line.endsWith("\r"); + const bare = cr ? line.slice(0, -1) : line; + + if (!bare.trim()) return line; + + const segments = parseAnsiSegments(bare); + const result = segments + .map((s) => + s.isAnsi + ? s.content + : highlightPlainText(s.content, activePatterns, s.activeSgr ?? ""), + ) + .join(""); + + return cr ? result + "\r" : result; +} + +export function highlightTerminalOutput( + text: string, + options: SyntaxHighlightOptions = {}, +): string { + if (!text || !text.trim()) return text; + if (hasIncompleteAnsiSequence(text)) return text; + + if (TUI_SEQUENCE.test(text)) return text; + + const activePatterns = buildActivePatterns(options); + if (activePatterns.length === 0) return text; + + return text + .split("\n") + .map((line) => highlightLine(line, activePatterns)) + .join("\n"); } diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index d5a613fd..54e5c5bd 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -20,7 +20,21 @@ "certTypeLabel": "Certificate type", "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", "hasCaCert": "Has CA Certificate", - "noCaCert": "No CA Certificate" + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { "failedToLoadAlerts": "Failed to load alerts", @@ -121,7 +135,12 @@ "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", "restore": "Restore", - "of": "of" + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { "home": "Home", @@ -129,7 +148,8 @@ "docker": "Docker", "tunnels": "Tunnels", "fileManager": "File Manager", - "serverStats": "Server Stats", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", "userProfile": "User Profile", "splitScreen": "Split Screen", @@ -148,9 +168,11 @@ "openFileManager": "Open File Manager", "dashboard": "Dashboard", "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", "quickConnect": "Quick Connect", "sshTools": "SSH Tools", "history": "History", + "sessionLogs": "Session Logs", "hosts": "Hosts", "snippets": "Snippets", "hostManager": "Host Manager", @@ -197,19 +219,21 @@ "uploadFile": "Upload File", "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Tunnels", "tabDocker": "Docker", "tabFiles": "Files", - "tabStats": "Stats", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", "tabSharing": "Sharing", "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", "fileManager": "File Manager", - "serverStats": "Server Stats", + "serverStats": "Host Metrics", "status": "Status", "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", "failedToRenameFolder": "Failed to rename folder", @@ -272,6 +296,7 @@ "unencryptedShell": "Unencrypted shell", "addressIp": "Address / IP", "friendlyName": "Friendly Name", + "macAddress": "MAC Address", "folderAndAdvanced": "Folder & Advanced", "privateNotes": "Private Notes", "privateNotesPlaceholder": "Details about this server...", @@ -311,6 +336,16 @@ "keyReplaceNotice": "paste a new key below to replace it", "keyPassphraseSaved": "Passphrase saved, type to change", "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", "terminalAppearance": "Terminal Appearance", @@ -326,6 +361,22 @@ "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", "rightClickSelectsWordLabel": "Right-click Selects Word", "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", "behaviorAndAdvanced": "Behavior & Advanced", "scrollbackBufferLabel": "Scrollback Buffer", "scrollbackMaxLines": "Maximum number of lines kept in history", @@ -335,8 +386,13 @@ "enableAutoMoshDesc": "Prefer Mosh over SSH if available", "enableAutoTmux": "Enable Auto-Tmux", "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", "sudoPasswordLabel": "Sudo Password", "environmentVariablesLabel": "Environment Variables", "addVariableBtn": "Add Variable", @@ -374,6 +430,39 @@ "dockerIntegration": "Docker Integration", "enableDockerMonitor": "Enable Docker", "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", "enableFileManagerMonitor": "Enable File Manager", "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", "defaultPathLabel": "Default Path", @@ -393,6 +482,7 @@ "metricsIntervalS": "Metrics Interval (s)", "metricsIntervalDesc2": "Seconds between metric snapshots", "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", "cpuUsageLabel": "CPU Usage", "cpuUsageDesc": "CPU percent, load averages, sparkline graph", "memoryLabel": "Memory Usage", @@ -414,7 +504,7 @@ "firewallLabel": "Firewall", "firewallDesc": "Firewall, AppArmor, SELinux status", "quickActionsLabel": "Quick Actions", - "quickActionsToolbar": "Quick actions appear as buttons in the Server Stats toolbar for one-click command execution.", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", "noQuickActions": "No quick actions yet.", "buttonLabel": "Button label", "selectSnippetPlaceholder": "Select snippet...", @@ -457,13 +547,52 @@ "deletedFolder": "Deleted folder \"{{name}}\"", "failedToDeleteFolder": "Failed to delete folder", "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", "deletedHost": "Deleted {{name}}", "copiedToClipboard": "Copied to clipboard", "terminalUrlCopied": "Terminal URL copied", "fileManagerUrlCopied": "File Manager URL copied", "tunnelUrlCopied": "Tunnel URL copied", "dockerUrlCopied": "Docker URL copied", - "serverStatsUrlCopied": "Server Stats URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", "rdpUrlCopied": "RDP URL copied", "vncUrlCopied": "VNC URL copied", "telnetUrlCopied": "Telnet URL copied", @@ -480,7 +609,8 @@ "copyFileManagerUrlAction": "Copy File Manager URL", "copyTunnelUrlAction": "Copy Tunnel URL", "copyDockerUrlAction": "Copy Docker URL", - "copyServerStatsUrlAction": "Copy Server Stats URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", "copyRdpUrlAction": "Copy RDP URL", "copyVncUrlAction": "Copy VNC URL", "copyTelnetUrlAction": "Copy Telnet URL", @@ -502,6 +632,8 @@ "disableTunnelsFeature": "Disable Tunnels", "enableDockerFeature": "Enable Docker", "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", "addTagsPlaceholder": "Add tags...", "authDetails": "Authentication Details", "credType": "Type", @@ -606,7 +738,6 @@ "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Tags", - "addHost": "Add Host", "shareHost": "Share Host", "shareHostTitle": "Share: {{name}}", "sharing": { @@ -773,6 +904,8 @@ "userProfileDesc": "Manage your account and preferences", "addCredential": "Add Credential", "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", "recentActivity": "Recent Activity", "serversAndHosts": "Servers & Hosts", "noHostsFound": "No hosts found matching \"{{search}}\"", @@ -1339,7 +1472,16 @@ "noHostSelected": "No host selected", "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", "memory": "Memory", "disk": "Disk", @@ -1360,7 +1502,8 @@ "serverOffline": "Server Offline", "cannotFetchMetrics": "Cannot fetch metrics from offline server", "totpFailed": "TOTP verification failed", - "noneAuthNotSupported": "Server Stats does not support 'none' authentication type.", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", "load": "Load", "systemInfo": "System Information", "hostname": "Hostname", @@ -1370,6 +1513,8 @@ "networkInterfaces": "Network Interfaces", "noInterfacesFound": "No network interfaces found", "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", "loginStats": "SSH Login Statistics", "noRecentLoginData": "No recent login data", "executingQuickAction": "Executing {{name}}...", @@ -1382,6 +1527,8 @@ "port": "Port", "address": "Address", "process": "Process", + "search": "Search ports...", + "allProtocols": "All", "noData": "No listening ports data" }, "firewall": { @@ -1389,18 +1536,99 @@ "inactive": "Inactive", "policy": "Policy", "rules": "rules", + "noRules": "No rules in this chain", "noData": "No firewall data available", "action": "Action", "protocol": "Proto", "port": "Port", "source": "Source", - "anywhere": "Anywhere" + "anywhere": "Anywhere", + "chains": "chains" }, "loadAvg": "Load Avg", "swap": "Swap", "architecture": "Architecture", "refresh": "Refresh", - "retry": "Retry" + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { "tagline": "Self-hosted SSH and remote desktop management", @@ -1434,6 +1662,12 @@ "loginWithExternal": "Login with External Provider", "loginWithExternalDesc": "Login using your configured external identity provider", "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", "resetPasswordButton": "Reset Password", "sendResetCode": "Send Reset Code", "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", @@ -1539,6 +1773,7 @@ "github": "GitHub", "support": "Support", "discord": "Discord", + "docs": "Docs", "serverOverview": "Server Overview", "version": "Version", "upToDate": "Up to Date", @@ -1600,6 +1835,22 @@ "empty": "Empty", "clear": "Clear" }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." + }, "networkGraph": { "addHost": "Add Host", "addGroup": "Add Group", @@ -1645,7 +1896,8 @@ "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Server Stats", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "noNodes": "No nodes yet" }, "docker": { @@ -1748,11 +2000,57 @@ "admin": { "sectionGeneral": "General", "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", "sectionUsers": "Users", "sectionSessions": "Sessions", "sectionRoles": "Roles", "sectionDatabase": "Database", "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", "allowRegistration": "Allow User Registration", "allowRegistrationDesc": "Let new users self-register", "allowPasswordLogin": "Allow Password Login", @@ -1761,6 +2059,9 @@ "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", "allowPasswordReset": "Allow Password Reset", "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", "sessionTimeout": "Session Timeout", "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", @@ -1771,8 +2072,13 @@ "logLevel": "Log Level", "enableGuacamole": "Enable Guacamole", "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", "oidcClientId": "Client ID", "oidcClientSecret": "Client Secret", "oidcAuthUrl": "Authorization URL", @@ -1784,6 +2090,10 @@ "oidcUserinfoUrl": "Override Userinfo URL", "oidcAllowedUsers": "Allowed Users", "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", "removeOidc": "Remove", "usersCount": "{{count}} users", "createUser": "Create", @@ -1806,6 +2116,7 @@ "import": "Import", "importing": "Importing...", "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", "newApiKey": "New API Key", "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", "apiKeyName": "Name", @@ -1866,17 +2177,21 @@ "editUserAuthType": "Auth Type", "editUserAdminStatus": "Admin Status", "editUserUserId": "User ID", - "linkAccountTitle": "Link OIDC to Password Account", + "linkAccountTitle": "Link Accounts", "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", "linkAccountWarningTitle": "This will:", "linkAccountEffect1": "Delete the OIDC-only account", "linkAccountEffect2": "Add OIDC login to the target account", "linkAccountEffect3": "Allow both OIDC and password login", - "linkAccountTargetUsername": "Target Username", + "linkAccountTargetUsername": "Local Account Username", "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", "linkAccounts": "Link Accounts", - "linkAccountSuccess": "OIDC account linked to \"{{username}}\"", - "linkAccountFailed": "Failed to link OIDC account", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", "linkAccountInProgress": "Linking...", "saving": "Saving...", "updateRegistrationFailed": "Failed to update registration setting", @@ -1892,6 +2207,8 @@ "guacamoleSaved": "Guacamole settings saved", "guacamoleSaveFailed": "Failed to save Guacamole settings", "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", "logLevelUpdateFailed": "Failed to update log level", "oidcSaved": "OIDC configuration saved", "oidcSaveFailed": "Failed to save OIDC config", @@ -1944,7 +2261,7 @@ "clearAll": "Clear All", "noHistoryEntries": "No history entries", "trackingDisabled": "History tracking is disabled", - "trackingDisabledHint": "Enable it in your profile settings to record commands." + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { "keyRecordingTitle": "Key Recording", @@ -1978,6 +2295,7 @@ "assignToPane": "Assign to pane" }, "snippets": { + "title": "Snippets", "createSnippetTitle": "Create Snippet", "createSnippetDescription": "Create a new command snippet for quick execution", "nameLabel": "Name", @@ -2049,6 +2367,12 @@ "reorderFailed": "Failed to save snippet order" }, "userProfile": { + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", "sectionAccount": "Account", "sectionAppearance": "Appearance", "sectionSecurity": "Security", @@ -2078,8 +2402,6 @@ "commandAutocompleteDesc": "Show autocomplete while typing", "historyTracking": "History Tracking", "historyTrackingDesc": "Track terminal commands", - "syntaxHighlighting": "Syntax Highlighting", - "syntaxHighlightingDesc": "Highlight terminal output", "commandPalette": "Command Palette", "commandPaletteDesc": "Enable keyboard shortcut", "reopenTabsOnLogin": "Reopen Tabs on Login", @@ -2093,6 +2415,8 @@ "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", "settingsSnippets": "Snippets", "foldersCollapsed": "Folders Collapsed", "foldersCollapsedDesc": "Collapse folders by default", @@ -2181,5 +2505,81 @@ "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/af_ZA.json b/src/ui/locales/translated/af_ZA.json index f82fcab2..9bc5af32 100644 --- a/src/ui/locales/translated/af_ZA.json +++ b/src/ui/locales/translated/af_ZA.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Lêers", - "folder": "Vouer", - "password": "Wagwoord", - "key": "Sleutel", - "sshPrivateKey": "SSH Privaat Sleutel", - "upload": "Oplaai", - "keyPassword": "Sleutelwagwoord", - "sshKey": "SSH-sleutel", - "uploadPrivateKeyFile": "Laai privaat sleutellêer op", - "searchCredentials": "Soek geloofsbriewe...", - "addCredential": "Voeg geloofsbriewe by", - "caCertificate": "CA-sertifikaat (-cert.pub)", - "caCertificateDescription": "Opsioneel: Laai die CA-getekende sertifikaatlêer op of plak dit (bv. id_ed25519-cert.pub). Vereis wanneer jou SSH-bediener sertifikaatgebaseerde magtiging gebruik.", - "uploadCertFile": "Laai -cert.pub-lêer op", - "clearCert": "Duidelik", - "certLoaded": "Sertifikaat gelaai", - "certPublicKeyLabel": "CA-sertifikaat", - "certTypeLabel": "Sertifikaattipe", - "pasteOrUploadCert": "Plak of laai 'n -cert.pub-sertifikaat op...", - "hasCaCert": "Het CA-sertifikaat", - "noCaCert": "Geen CA-sertifikaat nie" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Kon nie waarskuwings laai nie", - "failedToDismissAlert": "Kon nie waarskuwing toemaak nie" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Bedienerkonfigurasie", - "description": "Konfigureer die Termix-bediener-URL om aan jou backend-dienste te koppel", - "serverUrl": "Bediener-URL", - "enterServerUrl": "Voer asseblief 'n bediener-URL in", - "saveFailed": "Kon nie konfigurasie stoor nie", - "saveError": "Fout met die stoor van die konfigurasie", - "saving": "Stoor...", - "saveConfig": "Stoor Konfigurasie", - "helpText": "Voer die URL in waar jou Termix-bediener loop (bv. http://localhost:30001 of https://your-server.com)", - "changeServer": "Verander bediener", - "mustIncludeProtocol": "Bediener-URL moet met http:// of https:// begin", - "allowInvalidCertificate": "Laat ongeldige sertifikaat toe", - "allowInvalidCertificateDesc": "Gebruik slegs vir vertroude selfgehoste bedieners met selfgetekende of IP-adres sertifikate.", - "useEmbedded": "Gebruik plaaslike bediener", - "embeddedDesc": "Begin Termix met die ingeboude plaaslike bediener (geen afstandbediener nodig nie)", - "embeddedConnecting": "Verbind met plaaslike bediener...", - "embeddedNotReady": "Die plaaslike bediener is nog nie gereed nie. Wag asseblief 'n oomblik en probeer weer.", - "localServer": "Plaaslike bediener" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Weergawekontrolefout", - "checkFailed": "Kon nie vir opdaterings kyk nie", - "upToDate": "Toepassing is op datum", - "currentVersion": "Jy gebruik weergawe {{version}}", - "updateAvailable": "Opdatering beskikbaar", - "newVersionAvailable": "'n Nuwe weergawe is beskikbaar! Jy gebruik {{current}}, maar {{latest}} is beskikbaar.", - "betaVersion": "Beta-weergawe", - "betaVersionDesc": "Jy gebruik {{current}}, wat nuwer is as die nuutste stabiele weergawe {{latest}}.", - "releasedOn": "Vrygestel op {{date}}", - "downloadUpdate": "Laai opdatering af", - "checking": "Kontroleer tans vir opdaterings...", - "checkUpdates": "Kyk vir opdaterings", - "checkingUpdates": "Kontroleer tans vir opdaterings...", - "updateRequired": "Opdatering Vereis" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Maak toe", - "minimize": "Minimaliseer", - "online": "Aanlyn", - "offline": "Vanlyn", - "continue": "Gaan voort", - "maintenance": "Onderhoud", - "degraded": "Gedegradeer", - "error": "Fout", - "warning": "Waarskuwing", - "unsavedChanges": "Ongestoorde veranderinge", - "dismiss": "Maak toe", - "loading": "Laai tans...", - "optional": "Opsioneel", - "connect": "Verbind", - "copied": "Gekopieer", - "connecting": "Verbind...", - "updateAvailable": "Opdatering beskikbaar", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Maak oop in Nuwe Oortjie", - "noReleases": "Geen vrystellings nie", - "updatesAndReleases": "Opdaterings en vrystellings", - "newVersionAvailable": "'n Nuwe weergawe ({{version}}) is beskikbaar.", - "failedToFetchUpdateInfo": "Kon nie opdateringsinligting haal nie", - "preRelease": "Voorvrystelling", - "noReleasesFound": "Geen vrystellings gevind nie.", - "cancel": "Kanselleer", - "username": "Gebruikersnaam", - "login": "Aanmeld", - "register": "Registreer", - "password": "Wagwoord", - "confirmPassword": "Bevestig wagwoord", - "back": "Terug", - "save": "Stoor", - "saving": "Stoor...", - "delete": "Vee uit", - "rename": "Hernoem", - "edit": "Wysig", - "add": "Voeg by", - "confirm": "Bevestig", - "no": "Nee", - "or": "OF", - "next": "Volgende", - "previous": "Vorige", - "refresh": "Verfris", - "language": "Taal", - "checking": "Kontroleer tans...", - "checkingDatabase": "Kontroleer databasisverbinding...", - "checkingAuthentication": "Kontroleer tans verifikasie...", - "backendReconnected": "Bedienerverbinding herstel", - "connectionDegraded": "Bedienerverbinding verloor, herstel…", - "reload": "Herlaai", - "remove": "Verwyder", - "create": "Skep", - "update": "Opdatering", - "copy": "Kopieer", - "copyFailed": "Kon nie na knipbord kopieer nie", - "maximize": "Maksimeer", - "restore": "Herstel", - "of": "van" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Tuis", - "terminal": "Terminaal", + "home": "Home", + "terminal": "Terminal", "docker": "Docker", - "tunnels": "Tonnels", - "fileManager": "Lêerbestuurder", - "serverStats": "Bedienerstatistieke", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", - "userProfile": "Gebruikersprofiel", - "splitScreen": "Gesplete skerm", - "confirmClose": "Sluit hierdie aktiewe sessie?", - "close": "Maak toe", - "cancel": "Kanselleer", - "sshManager": "SSH-bestuurder", - "cannotSplitTab": "Kan nie hierdie oortjie verdeel nie", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopieer Wagwoord", - "copySudoPassword": "Kopieer Sudo-wagwoord", - "passwordCopied": "Wagwoord gekopieer na knipbord", - "noPasswordAvailable": "Geen wagwoord beskikbaar nie", - "failedToCopyPassword": "Kon nie wagwoord kopieer nie", - "refreshTab": "Verfris verbinding", - "openFileManager": "Maak Lêerbestuurder oop", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", "dashboard": "Dashboard", - "networkGraph": "Netwerkgrafiek", - "quickConnect": "Vinnige verbinding", - "sshTools": "SSH-gereedskap", - "history": "Geskiedenis", - "hosts": "Gashere", - "snippets": "Brokkies", - "hostManager": "Gasheerbestuurder", - "credentials": "Geloofsbriewe", - "connections": "Verbindings", - "roleAdministrator": "Administrateur", - "roleUser": "Gebruiker" + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Gashere", - "noHosts": "Geen SSH-gashere nie", - "retry": "Probeer weer", - "refresh": "Verfris", - "optional": "Opsioneel", - "downloadSample": "Laai voorbeeld af", - "failedToDeleteHost": "Kon nie {{name}} uitvee nie", - "importSkipExisting": "Invoer (slaan bestaande oor)", - "connectionDetails": "Verbindingsbesonderhede", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Afstandslessenaar", - "port": "Hawe", - "username": "Gebruikersnaam", - "folder": "Vouer", - "tags": "Etikette", - "pin": "Speld vas", - "addHost": "Voeg gasheer by", - "editHost": "Wysig gasheer", - "cloneHost": "Kloon gasheer", - "enableTerminal": "Aktiveer Terminaal", - "enableTunnel": "Aktiveer Tonnel", - "enableFileManager": "Aktiveer Lêerbestuurder", - "enableDocker": "Aktiveer Docker", - "defaultPath": "Standaardpad", - "connection": "Verbinding", - "upload": "Oplaai", - "authentication": "Verifikasie", - "password": "Wagwoord", - "key": "Sleutel", - "credential": "Geloofsbrief", - "none": "Geen", - "sshPrivateKey": "SSH Privaat Sleutel", - "keyType": "Sleuteltipe", - "uploadFile": "Laai lêer op", - "tabGeneral": "Algemeen", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tonnels", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Lêers", - "tabStats": "Statistiek", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Deling", - "tabAuthentication": "Verifikasie", - "terminal": "Terminaal", - "tunnel": "Tonnel", - "fileManager": "Lêerbestuurder", - "serverStats": "Bedienerstatistieke", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Lêer \"{{oldName}}\" is suksesvol hernoem na \"{{newName}}\"", - "failedToRenameFolder": "Kon nie die vouer hernoem nie", - "movedToFolder": "Geskuif na \"{{folder}}\"", - "editHostTooltip": "Wysig gasheer", - "statusChecks": "Statuskontroles", - "metricsCollection": "Metrieke-versameling", - "metricsInterval": "Metrieke Versamelingsinterval", - "metricsIntervalDesc": "Hoe gereeld om bedienerstatistieke in te samel (5s - 1u)", - "behavior": "Gedrag", - "themePreview": "Temavoorskou", - "theme": "Tema", - "fontFamily": "Lettertipefamilie", - "fontSize": "Lettergrootte", - "letterSpacing": "Letterspasiëring", - "lineHeight": "Lynhoogte", - "cursorStyle": "Wyserstyl", - "cursorBlink": "Wyserknip", - "scrollbackBuffer": "Terugrolbuffer", - "bellStyle": "Klokstyl", - "rightClickSelectsWord": "Regskliek Kies Woord", - "fastScrollModifier": "Vinnige Blaai-wysiger", - "fastScrollSensitivity": "Vinnige blaai-sensitiwiteit", - "sshAgentForwarding": "SSH Agent Aanstuur", - "backspaceMode": "Terugspasie-modus", - "startupSnippet": "Opstart-brokkie", - "selectSnippet": "Kies uittreksel", - "forceKeyboardInteractive": "Forseer sleutelbord-interaktief", - "overrideCredentialUsername": "Oorskryf geloofsbriewe gebruikersnaam", - "overrideCredentialUsernameDesc": "Gebruik die gebruikersnaam wat hierbo gespesifiseer is in plaas van die gebruikersnaam van die geloofsbriewe", - "jumpHostChain": "Springgasheerketting", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Voeg poort by", - "addProxyNode": "Voeg Node by", - "proxyNode": "Proxy-knooppunt", - "proxyType": "Proxy Tipe", - "quickActions": "Vinnige Aksies", - "sudoPasswordAutoFill": "Sudo Wagwoord Outomatiese Vul", - "sudoPassword": "Sudo-wagwoord", - "keepaliveInterval": "Lewendige interval (ms)", - "moshCommand": "MOSH-opdrag", - "environmentVariables": "Omgewingsveranderlikes", - "addVariable": "Voeg veranderlike by", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Kopieer terminaal-URL", - "copyFileManagerUrl": "Kopieer Lêerbestuurder URL", - "copyRemoteDesktopUrl": "Kopieer Afstandslessenaar URL", - "failedToConnect": "Kon nie aan konsole koppel nie", - "connect": "Verbind", - "disconnect": "Ontkoppel", - "start": "Begin", - "enableStatusCheck": "Aktiveer Statuskontrole", - "enableMetrics": "Aktiveer Metrieke", - "bulkUpdateFailed": "Grootmaatopdatering het misluk", - "selectAll": "Kies Alles", - "deselectAll": "Deselekteer alles", - "protocols": "Protokolle", - "secureShell": "Veilige Dop", - "virtualNetwork": "Virtuele Netwerk", - "unencryptedShell": "Ongeënkripteerde dop", - "addressIp": "Adres / IP", - "friendlyName": "Vriendelike Naam", - "folderAndAdvanced": "Vouer en Gevorderd", - "privateNotes": "Privaat Notas", - "privateNotesPlaceholder": "Besonderhede oor hierdie bediener...", - "pinToTop": "Speld vas aan bokant", - "pinToTopDesc": "Wys hierdie gasheer altyd bo-aan die lys", - "portKnockingSequence": "Port Knocking Sekwensie", - "addKnockBtn": "Voeg Klop by", - "noPortKnocking": "Geen poortklopping gekonfigureer nie.", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", "knockPort": "Knock Port", - "protocol": "Protokol", - "delayAfterMs": "Vertraging na (ms)", - "useSocks5Proxy": "Gebruik SOCKS5 Proxy", - "useSocks5ProxyDesc": "Roeteer verbinding deur 'n proxy-bediener", - "proxyHost": "Proxy-gasheer", - "proxyPort": "Proxy-poort", - "proxyUsername": "Proxy-gebruikersnaam", - "proxyPassword": "Proxy-wagwoord", - "proxySingleMode": "Enkele Proxy", - "proxyChainMode": "Proxy-ketting", - "you": "Jy", - "jumpHostChainLabel": "Springgasheerketting", - "addJumpBtn": "Voeg Spring by", - "noJumpHosts": "Geen springgashere gekonfigureer nie.", - "selectAServer": "Kies 'n bediener...", - "sshPort": "SSH-poort", - "authMethod": "Magtigingsmetode", - "storedCredential": "Gestoorde geloofsbriewe", - "selectACredential": "Kies 'n geloofsbrief...", - "keyTypeLabel": "Sleuteltipe", - "keyTypeAuto": "Outomatiese opsporing", - "keyPasteTab": "Plak", - "keyUploadTab": "Oplaai", - "keyFileLoaded": "Sleutellêer gelaai", - "keyUploadClick": "Klik om .pem / .key / .ppk op te laai", - "clearKey": "Vee sleutel uit", - "keySaved": "SSH-sleutel gestoor", - "keyReplaceNotice": "plak 'n nuwe sleutel hieronder om dit te vervang", - "keyPassphraseSaved": "Wagwoordfrase gestoor, tik om te verander", - "replaceKey": "Vervang sleutel", - "forceKeyboardInteractiveLabel": "Forseer Sleutelbord Interaktief", - "forceKeyboardInteractiveShortDesc": "Forseer handmatige wagwoordinvoer selfs al is sleutels teenwoordig", - "terminalAppearance": "Terminaalvoorkoms", - "colorTheme": "Kleurtema", - "fontFamilyLabel": "Lettertipefamilie", - "fontSizeLabel": "Lettergrootte", - "cursorStyleLabel": "Wyserstyl", - "letterSpacingPx": "Letterspasiëring (px)", - "lineHeightLabel": "Lynhoogte", - "bellStyleLabel": "Klokstyl", - "backspaceModeLabel": "Terugspasie-modus", - "cursorBlinking": "Wysertjie flikker", - "cursorBlinkingDesc": "Aktiveer flikkerende animasie vir die terminaalwyser", - "rightClickSelectsWordLabel": "Regskliek Kies Woord", - "rightClickSelectsWordShortDesc": "Kies die woord onder die wyser met die regskliek", - "behaviorAndAdvanced": "Gedrag en Gevorderd", - "scrollbackBufferLabel": "Terugrolbuffer", - "scrollbackMaxLines": "Maksimum aantal lyne wat in die geskiedenis gehou word", - "sshAgentForwardingLabel": "SSH Agent Aanstuur", - "sshAgentForwardingShortDesc": "Gee jou plaaslike SSH-sleutels aan hierdie gasheer deur", - "enableAutoMosh": "Aktiveer Auto-Mosh", - "enableAutoMoshDesc": "Verkies Mosh bo SSH indien beskikbaar", - "enableAutoTmux": "Aktiveer Auto-Tmux", - "enableAutoTmuxDesc": "Begin of koppel outomaties aan tmux-sessie", - "sudoPasswordAutoFillLabel": "Sudo Wagwoord Outomatiese Vul", - "sudoPasswordAutoFillShortDesc": "Verskaf outomaties 'n sudo-wagwoord wanneer gevra word", - "sudoPasswordLabel": "Sudo-wagwoord", - "environmentVariablesLabel": "Omgewingsveranderlikes", - "addVariableBtn": "Voeg veranderlike by", - "noEnvVars": "Geen omgewingveranderlikes gekonfigureer nie.", - "fastScrollModifierLabel": "Vinnige Blaai-wysiger", - "fastScrollSensitivityLabel": "Vinnige blaai-sensitiwiteit", - "moshCommandLabel": "Mosh-opdrag", - "startupSnippetLabel": "Opstart-brokkie", - "keepaliveIntervalLabel": "Lewendige interval (sekondes)", - "maxKeepaliveMisses": "Max Keepalive mis", - "tunnelSettings": "Tonnelinstellings", - "enableTunneling": "Aktiveer Tonnelbou", - "enableTunnelingDesc": "Aktiveer SSH-tonnelfunksionaliteit vir hierdie gasheer", - "serverTunnelsSection": "Bedienertonnels", - "addTunnelBtn": "Voeg tonnel by", - "noTunnelsConfigured": "Geen tonnels gekonfigureer nie.", - "tunnelLabel": "Tonnel {{number}}", - "tunnelType": "Tonnel Tipe", - "tunnelModeLocalDesc": "Stuur 'n plaaslike poort aan na 'n poort op die afstandbediener (of 'n gasheer wat daarvandaan bereik kan word).", - "tunnelModeRemoteDesc": "Stuur 'n poort op die afgeleë bediener terug na 'n plaaslike poort op jou masjien.", - "tunnelModeDynamicDesc": "Skep 'n SOCKS5-instaanbediener op 'n plaaslike poort vir dinamiese poortaanstuuring.", - "sameHost": "Hierdie gasheer (direkte tonnel)", - "endpointHost": "Eindpuntgasheer", - "endpointPort": "Eindpuntpoort", - "bindHost": "Bind gasheer", - "sourcePort": "Bronpoort", - "maxRetries": "Maksimum herprobeer", - "retryIntervalS": "Herprobeerinterval (s)", - "autoStartLabel": "Outomatiese begin", - "autoStartDesc": "Koppel outomaties hierdie tonnel wanneer die gasheer gelaai is", - "tunnelConnecting": "Tonnel verbind...", - "tunnelDisconnected": "Tonnel ontkoppel", - "failedToConnectTunnel": "Kon nie koppel nie", - "failedToDisconnectTunnel": "Kon nie ontkoppel nie", - "dockerIntegration": "Docker-integrasie", - "enableDockerMonitor": "Aktiveer Docker", - "enableDockerMonitorDesc": "Monitor en bestuur houers op hierdie gasheer via Docker", - "enableFileManagerMonitor": "Aktiveer Lêerbestuurder", - "enableFileManagerMonitorDesc": "Blaai deur en bestuur lêers op hierdie gasheer via SFTP", - "defaultPathLabel": "Standaardpad", - "fileManagerPathHint": "Die gids om oop te maak wanneer die lêerbestuurder vir hierdie gasheer begin.", - "statusChecksLabel": "Statuskontroles", - "enableStatusChecks": "Aktiveer statuskontroles", - "enableStatusChecksDesc": "Ping hierdie gasheer gereeld om beskikbaarheid te verifieer", - "useGlobalInterval": "Gebruik globale interval", - "useGlobalIntervalDesc": "Oorskryf met die bedienerwye statuskontrole-interval", - "checkIntervalS": "Kontrole-interval (s)", - "checkIntervalDesc": "Sekondes tussen elke konnektiwiteitsping", - "metricsCollectionLabel": "Metrieke-versameling", - "enableMetricsLabel": "Aktiveer Metrieke", - "enableMetricsDesc": "Versamel SVE-, RAM-, skyf- en netwerkgebruik van hierdie gasheer", - "useGlobalMetrics": "Gebruik globale interval", - "useGlobalMetricsDesc": "Oorskryf met die bedienerwye metrieke-interval", - "metricsIntervalS": "Metrieke Interval (s)", - "metricsIntervalDesc2": "Sekondes tussen metrieke momentopnames", - "visibleWidgets": "Sigbare Widgets", - "cpuUsageLabel": "SVE-gebruik", - "cpuUsageDesc": "SVE-persentasie, laaigemiddeldes, vonklyngrafiek", - "memoryLabel": "Geheuegebruik", - "memoryDesc": "RAM-gebruik, ruil, kasgeheue", - "storageLabel": "Skyfgebruik", - "storageDesc": "Skyfgebruik per monteerpunt", - "networkLabel": "Netwerkkoppelvlakke", - "networkDesc": "Koppelvlaklys en bandwydte", - "uptimeLabel": "Optyd", - "uptimeDesc": "Stelsel-opstarttyd en opstarttyd", - "systemInfoLabel": "Stelselinligting", - "systemInfoDesc": "OS, kern, gasheernaam, argitektuur", - "recentLoginsLabel": "Onlangse Aanmeldings", - "recentLoginsDesc": "Suksesvolle en mislukte aanmeldgebeurtenisse", - "topProcessesLabel": "Topprosesse", - "topProcessesDesc": "PID, SVE%, MEM%, opdrag", - "listeningPortsLabel": "Luisterpoorte", - "listeningPortsDesc": "Maak poorte oop met proses en toestand", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", "firewallDesc": "Firewall, AppArmor, SELinux status", - "quickActionsLabel": "Vinnige Aksies", - "quickActionsToolbar": "Vinnige aksies verskyn as knoppies in die Bedienerstatistieke-nutsbalk vir die uitvoering van opdragte met een klik.", - "noQuickActions": "Geen vinnige aksies nog nie.", - "buttonLabel": "Knoppie-etiket", - "selectSnippetPlaceholder": "Kies uittreksel...", - "addActionBtn": "Voeg Aksie by", - "hostSharedSuccessfully": "Gasheer suksesvol gedeel", - "failedToShareHost": "Kon nie gasheer deel nie", - "accessRevoked": "Toegang herroep", - "failedToRevokeAccess": "Kon nie toegang herroep nie", - "cancelBtn": "Kanselleer", - "savingBtn": "Stoor...", - "addHostBtn": "Voeg gasheer by", - "hostUpdated": "Gasheer opgedateer", - "hostCreated": "Gasheer geskep", - "failedToSave": "Kon nie gasheer stoor nie", - "credentialUpdated": "Geloofsbrief opgedateer", - "credentialCreated": "Geloofsbrief geskep", - "failedToSaveCredential": "Kon nie geloofsbriewe stoor nie", - "backToHosts": "Terug na gashere", - "backToCredentials": "Terug na geloofsbriewe", - "pinned": "Vasgepen", - "noHostsFound": "Geen gashere gevind nie", - "tryDifferentTerm": "Probeer 'n ander term", - "addFirstHost": "Voeg jou eerste gasheer by om te begin", - "noCredentialsFound": "Geen geloofsbriewe gevind nie", - "addCredentialBtn": "Voeg geloofsbriewe by", - "updateCredentialBtn": "Opdatering van geloofsbriewe", - "features": "Kenmerke", - "noFolder": "(Geen vouer nie)", - "deleteSelected": "Vee uit", - "exitSelection": "Verlaat seleksie", - "importSkip": "Invoer (slaan bestaande oor)", - "importOverwrite": "Invoer (oorskryf)", - "collapseBtn": "Ineenstorting", - "importExportBtn": "Invoer / Uitvoer", - "hostStatusesRefreshed": "Gasheerstatusse is verfris", - "failedToRefreshHosts": "Kon nie gashere verfris nie", - "movedHostTo": "Geskuif {{host}} na \"{{folder}}\"", - "failedToMoveHost": "Kon nie gasheer skuif nie", - "folderRenamedTo": "Vouer hernoem na \"{{name}}\"", - "deletedFolder": "Verwyderde vouer \"{{name}}\"", - "failedToDeleteFolder": "Kon nie vouer uitvee nie", - "deleteAllInFolder": "Vee alle gashere in \"{{name}}\" uit? Dit kan nie ongedaan gemaak word nie.", - "deletedHost": "Verwyder {{name}}", - "copiedToClipboard": "Na knipbord gekopieer", - "terminalUrlCopied": "Terminaal-URL gekopieer", - "fileManagerUrlCopied": "Lêerbestuurder-URL gekopieer", - "tunnelUrlCopied": "Tonnel-URL gekopieer", - "dockerUrlCopied": "Docker-URL gekopieer", - "serverStatsUrlCopied": "Bedienerstatistieke-URL gekopieer", - "rdpUrlCopied": "RDP-URL gekopieer", - "vncUrlCopied": "VNC URL gekopieer", - "telnetUrlCopied": "Telnet-URL gekopieer", - "remoteDesktopUrlCopied": "URL vir afstandwerkskerm gekopieer", - "expandActions": "Vou aksies uit", - "collapseActions": "Vou aksies in", - "wakeOnLanAction": "Wakker word op LAN", - "wakeOnLanSuccess": "Magiese pakkie gestuur na {{name}}", - "wakeOnLanError": "Kon nie magiese pakkie stuur nie", - "cloneHostAction": "Kloon gasheer", - "copyAddress": "Kopieer Adres", - "copyLink": "Kopieer skakel", - "copyTerminalUrlAction": "Kopieer terminaal-URL", - "copyFileManagerUrlAction": "Kopieer Lêerbestuurder URL", - "copyTunnelUrlAction": "Kopieer Tonnel URL", - "copyDockerUrlAction": "Kopieer Docker URL", - "copyServerStatsUrlAction": "Kopieer bedienerstatistieke-URL", - "copyRdpUrlAction": "Kopieer RDP-URL", - "copyVncUrlAction": "Kopieer VNC URL", - "copyTelnetUrlAction": "Kopieer Telnet-URL", - "copyRemoteDesktopUrlAction": "Kopieer Afstandslessenaar URL", - "deleteCredentialConfirm": "Vee geloofsbriewe \"{{name}} \" uit?", - "deletedCredential": "Verwyder {{name}}", - "deploySSHKeyTitle": "Implementeer SSH-sleutel", - "deployingBtn": "Implementeer tans...", - "deployBtn": "Ontplooi", - "failedToDeployKey": "Kon nie sleutel ontplooi nie", - "deleteHostsConfirm": "Vee {{count}} gasheer{{plural}}uit? Dit kan nie ongedaan gemaak word nie.", - "movedToRoot": "Na wortel geskuif", - "failedToMoveHosts": "Kon nie gashere skuif nie", - "enableTerminalFeature": "Aktiveer Terminaal", - "disableTerminalFeature": "Deaktiveer terminaal", - "enableFilesFeature": "Aktiveer lêers", - "disableFilesFeature": "Deaktiveer lêers", - "enableTunnelsFeature": "Aktiveer tonnels", - "disableTunnelsFeature": "Deaktiveer tonnels", - "enableDockerFeature": "Aktiveer Docker", - "disableDockerFeature": "Deaktiveer Docker", - "addTagsPlaceholder": "Voeg etikette by...", - "authDetails": "Verifikasiebesonderhede", - "credType": "Tipe", - "generateKeyPairDesc": "Genereer 'n nuwe sleutelpaar, beide private en publieke sleutels sal outomaties ingevul word.", - "generatingKey": "Genereer tans...", - "generateLabel": "Genereer {{label}}", - "uploadFileBtn": "Laai lêer op", - "keyPassphraseOptional": "Sleutelwagwoordfrase (Opsioneel)", - "sshPublicKeyOptional": "SSH Publieke Sleutel (Opsioneel)", - "publicKeyGenerated": "Publieke sleutel gegenereer", - "failedToGeneratePublicKey": "Kon nie publieke sleutel aflei nie", - "publicKeyCopied": "Publieke sleutel gekopieer", - "keyPairGenerated": "{{label}} sleutelpaar gegenereer", - "failedToGenerateKeyPair": "Kon nie sleutelpaar genereer nie", - "searchHostsPlaceholder": "Soek gashere, adresse, etikette…", - "searchCredentialsPlaceholder": "Soekbesonderhede…", - "refreshBtn": "Verfris", - "addTag": "Voeg etikette by...", - "deleteConfirmBtn": "Vee uit", - "tunnelRequirementsText": "Die SSH-bediener moet GatewayPorts ja, AllowTcpForwarding ja, en PermitRootLogin ja gestel hê in /etc/ssh/sshd_config.", - "deleteHostConfirm": "Vee \"{{name}} \" uit?", - "enableAtLeastOneProtocol": "Aktiveer ten minste een protokol hierbo om verifikasie- en verbindingsinstellings te konfigureer.", - "keyPassphrase": "Sleutelwagwoordfrase", - "connectBtn": "Verbind", - "disconnectBtn": "Ontkoppel", - "basicInformation": "Basiese Inligting", - "authDetailsSection": "Verifikasiebesonderhede", - "credTypeLabel": "Tipe", - "hostsTab": "Gashere", - "credentialsTab": "Geloofsbriewe", - "selectMultiple": "Kies veelvuldige", - "selectHosts": "Kies gashere", - "connectionLabel": "Verbinding", - "authenticationLabel": "Verifikasie", - "generateKeyPairTitle": "Genereer sleutelpaar", - "generateKeyPairDescription": "Genereer 'n nuwe sleutelpaar, beide private en publieke sleutels sal outomaties ingevul word.", - "generateFromPrivateKey": "Genereer vanaf Privaat Sleutel", - "refreshBtn2": "Verfris", - "exitSelectionTitle": "Verlaat seleksie", - "exportAll": "Voer alles uit", - "addHostBtn2": "Voeg gasheer by", - "addCredentialBtn2": "Voeg geloofsbriewe by", - "checkingHostStatuses": "Kontroleer gasheerstatusse...", - "pinnedSection": "Vasgepen", - "hostsExported": "Gashere suksesvol uitgevoer", - "exportFailed": "Kon nie gashere uitvoer nie", - "sampleDownloaded": "Voorbeeldlêer afgelaai", - "failedToDeleteCredential2": "Kon nie geloofsbriewe verwyder nie", - "noFolderOption": "(Geen vouer nie)", - "nSelected": "{{count}} gekies", - "featuresMenu": "Kenmerke", - "moveMenu": "Beweeg", - "cancelSelection": "Kanselleer", - "deployDialogDesc": "Ontplooi {{name}} na 'n gasheer se authorized_keys.", - "targetHostLabel": "Teikengasheer", - "selectHostOption": "Kies 'n gasheer...", - "keyDeployedSuccess": "Sleutel suksesvol ontplooi", - "failedToDeployKey2": "Kon nie sleutel ontplooi nie", - "deletedCount": "Verwyderde {{count}} gashere", - "failedToDeleteCount": "Kon nie {{count}} gashere verwyder nie", - "duplicatedHost": "Gedupliseerde \"{{name}}\"", - "failedToDuplicateHost": "Kon nie gasheer dupliseer nie", - "updatedCount": "Opgedateerde {{count}} gashere", - "friendlyNameLabel": "Vriendelike Naam", - "descriptionLabel": "Beskrywing", - "loadingHost": "Laai gasheer...", - "loadingHosts": "Laai gashere...", - "loadingCredentials": "Laai aanmeldbewyse...", - "noHostsYet": "Geen gashere nog nie", - "noHostsMatchSearch": "Geen gashere stem ooreen met jou soektog nie", - "hostNotFound": "Gasheer nie gevind nie", - "searchHosts": "Soek gashere...", - "sortHosts": "Sorteer gashere", - "sortDefault": "Standaardbestelling", - "sortNameAsc": "Naam (A → Z)", - "sortNameDesc": "Naam (Z → A)", - "sortIpAsc": "IP-adres (opstygend)", - "sortIpDesc": "IP-adres (beskrywing)", - "sortOnlineFirst": "Aanlyn Eerste", - "sortOfflineFirst": "Vanlyn Eerste", - "sortPinnedFirst": "Eerste vasgepen", - "filterHosts": "Filter gashere", - "filterClearAll": "Vee filters uit", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "Aanlyn", - "filterOffline": "Vanlyn", - "filterPinned": "Vasgepen", - "filterAuthGroup": "Magtigingsoort", - "filterAuthPassword": "Wagwoord", - "filterAuthKey": "SSH-sleutel", - "filterAuthCredential": "Geloofsbrief", - "filterAuthNone": "Geen", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokol", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Kenmerke", - "filterFeatureTerminal": "Terminaal", - "filterFeatureFileManager": "Lêerbestuurder", - "filterFeatureTunnel": "Tonnel", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etikette", - "shareHost": "Deel gasheer", - "shareHostTitle": "Deel: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Hierdie gasheer moet 'n geloofsbrief gebruik om deling te aktiveer. Wysig die gasheer en ken eers 'n geloofsbrief toe." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Verbinding", - "authentication": "Verifikasie", - "connectionSettings": "Verbindingsinstellings", - "displaySettings": "Skerminstellings", - "audioSettings": "Oudio-instellings", - "rdpPerformance": "RDP-prestasie", - "deviceRedirection": "Toestelherleiding", - "session": "Sessie", - "gateway": "Poort", - "remoteApp": "Afstandsprogram", - "clipboard": "Klembord", - "sessionRecording": "Sessie-opname", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC-instellings", - "terminalSettings": "Terminaalinstellings", - "rdpPort": "RDP-poort", - "username": "Gebruikersnaam", - "password": "Wagwoord", - "domain": "Domein", - "securityMode": "Sekuriteitsmodus", - "colorDepth": "Kleurdiepte", - "width": "Breedte", - "height": "Hoogte", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Vergrootingsmetode", - "clientName": "Kliëntnaam", - "initialProgram": "Aanvanklike Program", - "serverLayout": "Bedieneruitleg", - "timezone": "Tydsone", - "gatewayHostname": "Gateway-gasheernaam", - "gatewayPort": "Poortpoort", - "gatewayUsername": "Gateway-gebruikersnaam", - "gatewayPassword": "Gateway-wagwoord", - "gatewayDomain": "Gateway-domein", - "remoteAppProgram": "RemoteApp-program", - "workingDirectory": "Werkgids", - "arguments": "Argumente", - "normalizeLineEndings": "Normaliseer Lyn Eindigings", - "recordingPath": "Opnamepad", - "recordingName": "Opnamenaam", - "macAddress": "MAC-adres", - "broadcastAddress": "Uitsaaiadres", - "udpPort": "UDP-poort", - "waitTimeS": "Wagtyd (s)", - "driveName": "Skyfnaam", - "drivePath": "Rypad", - "ignoreCertificate": "Ignoreer Sertifikaat", - "ignoreCertificateDesc": "Laat verbindings na gashere met selfgetekende sertifikate toe", - "forceLossless": "Forseer verliesloos", - "forceLosslessDesc": "Forseer verlieslose beeldkodering (hoër gehalte, meer bandwydte)", - "disableAudio": "Deaktiveer oudio", - "disableAudioDesc": "Demp alle klank van die afstandsessie", - "enableAudioInput": "Aktiveer oudio-invoer (mikrofoon)", - "enableAudioInputDesc": "Stuur plaaslike mikrofoon na die afgeleë sessie aan", - "wallpaper": "Agtergrond", - "wallpaperDesc": "Wys lessenaar agtergrond (deaktivering verbeter werkverrigting)", - "theming": "Temavorming", - "themingDesc": "Aktiveer visuele temas en style", - "fontSmoothing": "Lettertipe-gladmaak", - "fontSmoothingDesc": "Aktiveer ClearType-lettertipe-weergawe", - "fullWindowDrag": "Volle Venster Sleep", - "fullWindowDragDesc": "Wys vensterinhoud terwyl jy sleep", - "desktopComposition": "Komposisie op die lessenaar", - "desktopCompositionDesc": "Aktiveer Aero-glaseffekte", - "menuAnimations": "Menu Animasies", - "menuAnimationsDesc": "Aktiveer menu-vervaag- en skuifanimasies", - "disableBitmapCaching": "Deaktiveer Bitmap-kasgeheue", - "disableBitmapCachingDesc": "Skakel bitmap-kasgeheue af (kan help met foute)", - "disableOffscreenCaching": "Deaktiveer buiteskerm-kasgeheue", - "disableOffscreenCachingDesc": "Skakel buiteskerm-kasgeheue af", - "disableGlyphCaching": "Deaktiveer glifkasgeheue", - "disableGlyphCachingDesc": "Skakel glifkasgeheue af", - "enableGfx": "Aktiveer GFX", - "enableGfxDesc": "Gebruik RemoteFX grafiese pyplyn", - "enablePrinting": "Aktiveer drukwerk", - "enablePrintingDesc": "Herlei plaaslike drukkers na die afgeleë sessie", - "enableDriveRedirection": "Aktiveer dryfherleiding", - "enableDriveRedirectionDesc": "Karaktereer 'n plaaslike vouer as 'n skyf in die afstandsessie", - "createDrivePath": "Skep 'n rylaanpad", - "createDrivePathDesc": "Skep die lêer outomaties as dit nie bestaan nie", - "disableDownload": "Deaktiveer aflaai", - "disableDownloadDesc": "Voorkom die aflaai van lêers vanaf die afgeleë sessie", - "disableUpload": "Deaktiveer oplaai", - "disableUploadDesc": "Voorkom die oplaai van lêers na die afgeleë sessie", - "enableTouch": "Aktiveer Aanraking", - "enableTouchDesc": "Aktiveer aanstuur van raakinvoer", - "consoleSession": "Konsolesessie", - "consoleSessionDesc": "Koppel aan die konsole (sessie 0) in plaas van 'n nuwe sessie", - "sendWolPacket": "Stuur WOL-pakket", - "sendWolPacketDesc": "Stuur 'n magiese pakket om hierdie gasheer wakker te maak voordat jy verbind", - "disableCopy": "Deaktiveer Kopieer", - "disableCopyDesc": "Voorkom die kopiëring van teks vanaf die afgeleë sessie", - "disablePaste": "Deaktiveer plak", - "disablePasteDesc": "Voorkom die plak van teks in die afgeleë sessie", - "createPathIfMissing": "Skep pad indien ontbreek", - "createPathIfMissingDesc": "Skep outomaties die opnamegids", - "excludeOutput": "Sluit Uitvoer Uit", - "excludeOutputDesc": "Moenie skermuitvoer opneem nie (slegs metadata)", - "excludeMouse": "Sluit Muis uit", - "excludeMouseDesc": "Moenie muisbewegings opneem nie", - "includeKeystrokes": "Sluit toetsaanslagen in", - "includeKeystrokesDesc": "Neem rou toetsaanslagen op benewens skermuitvoer", - "vncPort": "VNC-poort", - "vncPassword": "VNC-wagwoord", - "vncUsernameOptional": "Gebruikersnaam (opsioneel)", - "vncLeaveBlank": "Los leeg indien nie nodig nie", - "cursorMode": "Wysermodus", - "swapRedBlue": "Ruil Rooi/Blou", - "swapRedBlueDesc": "Ruil die rooi en blou kleurkanale om (maak sommige kleurprobleme reg)", - "readOnly": "Leesalleen", - "readOnlyDesc": "Bekyk die afstandskerm sonder om enige invoer te stuur", - "telnetPort": "Telnet-poort", - "terminalType": "Terminaaltipe", - "fontName": "Lettertipe Naam", - "fontSize": "Lettergrootte", - "colorScheme": "Kleurskema", - "backspaceKey": "Terugspasie-sleutel", - "saveHostFirst": "Stoor eers die gasheer.", - "sharingOptionsAfterSave": "Deelopsies is beskikbaar nadat die gasheer gestoor is.", - "sharingLoadError": "Kon nie deeldata laai nie. Gaan jou verbinding na en probeer weer.", - "shareHostSection": "Deel gasheer", - "shareWithUser": "Deel met Gebruiker", - "shareWithRole": "Deel met Rol", - "selectUser": "Kies Gebruiker", - "selectRole": "Kies Rol", - "selectUserOption": "Kies 'n gebruiker...", - "selectRoleOption": "Kies 'n rol...", - "permissionLevel": "Toestemmingsvlak", - "expiresInHours": "Verval oor (ure)", - "noExpiryPlaceholder": "Los leeg vir geen vervaldatum", - "shareBtn": "Deel", - "currentAccess": "Huidige Toegang", - "typeHeader": "Tipe", - "targetHeader": "Teiken", - "permissionHeader": "Toestemming", - "grantedByHeader": "Toegeken deur", - "expiresHeader": "Vervaldatum", - "noAccessEntries": "Geen toegangsinskrywings nog nie.", - "expiredLabel": "Verstryk", - "neverLabel": "Nooit", - "revokeBtn": "Herroep", - "cancelBtn": "Kanselleer", - "savingBtn": "Stoor...", - "updateHostBtn": "Opdateer gasheer", - "addHostBtn": "Voeg gasheer by" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Soek gashere, opdragte of instellings...", - "quickActions": "Vinnige Aksies", - "hostManager": "Gasheerbestuurder", - "hostManagerDesc": "Bestuur, voeg by of wysig gashere", - "addNewHost": "Voeg nuwe gasheer by", - "addNewHostDesc": "Registreer 'n nuwe gasheer", - "adminSettings": "Admin-instellings", - "adminSettingsDesc": "Konfigureer stelselvoorkeure en gebruikers", - "userProfile": "Gebruikersprofiel", - "userProfileDesc": "Bestuur jou rekening en voorkeure", - "addCredential": "Voeg geloofsbriewe by", - "addCredentialDesc": "Stoor SSH-sleutels of wagwoorde", - "recentActivity": "Onlangse Aktiwiteit", - "serversAndHosts": "Bedieners en gashere", - "noHostsFound": "Geen gashere gevind wat ooreenstem met \"{{search}}\"", - "links": "Skakels", - "navigate": "Navigeer", - "select": "Kies", - "toggleWith": "Wissel met" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Ruit {{index}} - leeg", - "noTabAssigned": "Geen oortjie toegeken nie", - "focusedPane": "Aktiewe paneel" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Geen verbindings nie", - "noConnectionsDesc": "Maak 'n terminaal, lêerbestuurder of afstandlessenaar oop om verbindings hier te sien", - "connectedFor": "Gekoppel vir {{duration}}", - "connected": "Verbonde", - "disconnected": "Ontkoppel", - "closeTab": "Maak oortjie toe", - "closeConnection": "Maak verbinding toe", - "forgetTab": "Vergeet", - "removeBackground": "Verwyder", - "reconnect": "Herkoppel", - "reopenTab": "Heropen", - "sectionOpen": "Oop", - "sectionBackground": "Agtergrond", - "backgroundDesc": "Sessies duur vir 30 minute na ontkoppeling voort en kan weer gekoppel word.", - "persisted": "Het in die agtergrond voortgeduur", - "expiresIn": "Verval oor {{duration}}", - "search": "Soek verbindings...", - "noSearchResults": "Geen verbindings stem ooreen met jou soektog nie" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Verbind met {{type}} sessie...", - "connectionError": "Verbindingsfout", - "connectionFailed": "Verbinding het misluk", - "failedToConnect": "Kon nie verbindingstoken kry nie", - "hostNotFound": "Gasheer nie gevind nie", - "noHostSelected": "Geen gasheer gekies nie", - "reconnect": "Herkoppel", - "retry": "Probeer weer", - "guacdUnavailable": "Diens vir afstandwerkskerm (guacd) is nie beskikbaar nie. Maak asseblief seker dat guacd loop, toeganklik is en behoorlik in administrateurinstellings gekonfigureer is.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Sluitskerm)", - "winKey": "Windows-sleutel", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Skof", - "win": "Wen", - "stickyActive": "{{key}} (vasgeklem - klik om los te maak)", - "stickyInactive": "{{key}} (klik om te sluit)", - "esc": "Ontsnapping", - "tab": "Oortjie", - "home": "Tuis", - "end": "Einde", - "pageUp": "Bladsy op", - "pageDown": "Bladsy af", - "arrowUp": "Pyltjie Op", - "arrowDown": "Pyltjie Af", - "arrowLeft": "Pyltjie links", - "arrowRight": "Pyltjie Regs", - "fnToggle": "Funksiesleutels", - "reconnect": "Herkoppel Sessie", - "collapse": "Vou nutsbalk in", - "expand": "Vou die nutsbalk uit", - "dragHandle": "Sleep om te herposisioneer" + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Koppel aan gasheer", - "clear": "Duidelik", - "paste": "Plak", - "reconnect": "Herkoppel", - "connectionLost": "Verbinding verloor", - "connected": "Verbonde", - "clipboardWriteFailed": "Kon nie na knipbord kopieer nie. Maak seker dat die bladsy oor HTTPS of localhost bedien word.", - "clipboardReadFailed": "Kon nie vanaf knipbord lees nie. Maak seker dat knipbordtoestemmings toegestaan is.", - "clipboardHttpWarning": "Plak vereis HTTPS. Gebruik Ctrl+Shift+V of bedien Termix oor HTTPS.", - "unknownError": "Onbekende fout het voorgekom", - "websocketError": "WebSocket-verbindingsfout", - "connecting": "Verbind...", - "noHostSelected": "Geen gasheer gekies nie", - "reconnecting": "Herverbind... ({{attempt}}/{{max}})", - "reconnected": "Herverbind suksesvol", - "tmuxSessionCreated": "tmux-sessie geskep: {{name}}", - "tmuxSessionAttached": "tmux-sessie aangeheg: {{name}}", - "tmuxUnavailable": "tmux is nie op die afgeleë gasheer geïnstalleer nie, en val terug na die standaard dop.", - "tmuxSessionPickerTitle": "tmux-sessies", - "tmuxSessionPickerDesc": "Bestaande tmux-sessies is op hierdie gasheer gevind. Kies een om weer aan te heg of skep 'n nuwe sessie.", - "tmuxWindows": "Vensters", - "tmuxWindowCount": "{{count}} venster", - "tmuxAttached": "Aangehegte kliënte", - "tmuxAttachedCount": "{{count}} aangeheg", - "tmuxLastActivity": "Laaste aktiwiteit", - "tmuxTimeJustNow": "nou net", - "tmuxTimeMinutes": "{{count}}m gelede", - "tmuxTimeHours": "{{count}}uur gelede", - "tmuxTimeDays": "{{count}}dae gelede", - "tmuxCreateNew": "Begin nuwe sessie", - "tmuxCopyHint": "Pas seleksie aan en druk Enter om na knipbord te kopieer", - "tmuxDetach": "Ontkoppel van tmux-sessie", - "tmuxDetached": "Losgemaak van tmux-sessie", - "maxReconnectAttemptsReached": "Maksimum herverbindingspogings bereik", - "closeTab": "Maak toe", - "connectionTimeout": "Verbindingstydverstryking", - "terminalTitle": "Terminaal - {{host}}", - "terminalWithPath": "Terminaal - {{host}}:{{path}}", - "runTitle": "Hardloop {{command}} - {{host}}", - "totpRequired": "Tweefaktor-verifikasie vereis", - "totpCodeLabel": "Verifikasiekode", - "totpVerify": "Verifieer", - "warpgateAuthRequired": "Warpgate-verifikasie vereis", - "warpgateSecurityKey": "Sekuriteitsleutel", - "warpgateAuthUrl": "Verifikasie-URL", - "warpgateOpenBrowser": "Maak oop in blaaier", - "warpgateContinue": "Ek het verifikasie voltooi", - "opksshAuthRequired": "OPKSSH-verifikasie vereis", - "opksshAuthDescription": "Voltooi verifikasie in jou blaaier om voort te gaan. Hierdie sessie sal vir 24 uur geldig bly.", - "opksshOpenBrowser": "Maak blaaier oop om te verifieer", - "opksshWaitingForAuth": "Wag vir verifikasie in blaaier...", - "opksshAuthenticating": "Verwerk verifikasie...", - "opksshTimeout": "Verifikasie het verstryk. Probeer asseblief weer.", - "opksshAuthFailed": "Verifikasie het misluk. Kontroleer asseblief jou geloofsbriewe en probeer weer.", - "opksshSignInWith": "Teken aan met {{provider}}", - "sudoPasswordPopupTitle": "Wagwoord invoeg?", - "websocketAbnormalClose": "Verbinding het onverwags gesluit. Dit kan wees as gevolg van 'n omgekeerde proxy- of SSL-konfigurasieprobleem. Gaan asseblief bedienerlogboeke na.", - "connectionLogTitle": "Verbindingslogboek", - "connectionLogCopy": "Kopieer logs na knipbord", - "connectionLogEmpty": "Geen verbindingslogboeke nog nie", - "connectionLogWaiting": "Wag vir verbindingslogboeke...", - "connectionLogCopied": "Verbindingslogboeke na knipbord gekopieer", - "connectionLogCopyFailed": "Kon nie logs na knipbord kopieer nie", - "connectionRejected": "Verbinding deur bediener verwerp. Kontroleer asseblief u verifikasie en netwerkkonfigurasie.", - "hostKeyRejected": "SSH-gasheersleutelverifikasie verwerp. Verbinding gekanselleer.", - "sessionTakenOver": "Sessie is in 'n ander oortjie oopgemaak. Herverbind tans..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Geen gasheer gekies nie", - "initializingEditor": "Inisialiseer redigeerder...", - "file": "Lêer", - "folder": "Vouer", - "uploadFile": "Laai lêer op", - "downloadFile": "Laai af", - "extractArchive": "Uittrekselargief", - "extractingArchive": "Onttrek tans {{name}}...", - "archiveExtractedSuccessfully": "{{name}} suksesvol onttrek", - "extractFailed": "Uittreksel het misluk", - "compressFile": "Komprimeer lêer", - "compressFiles": "Komprimeer lêers", - "compressFilesDesc": "Kompresseer {{count}} items in 'n argief", - "archiveName": "Argiefnaam", - "enterArchiveName": "Voer argiefnaam in...", - "compressionFormat": "Kompressieformaat", - "selectedFiles": "Geselekteerde lêers", - "andMoreFiles": "en {{count}} meer...", - "compress": "Kompressie", - "compressingFiles": "Komprimering van {{count}} items in {{name}}...", - "filesCompressedSuccessfully": "{{name}} suksesvol geskep", - "compressFailed": "Kompressie het misluk", - "edit": "Wysig", - "preview": "Voorskou", - "previous": "Vorige", - "next": "Volgende", - "pageXOfY": "Bladsy {{current}} van {{total}}", - "zoomOut": "Uitzoom", - "zoomIn": "Inzoem", - "newFile": "Nuwe Lêer", - "newFolder": "Nuwe vouer", - "rename": "Hernoem", - "uploading": "Laai tans op...", - "uploadingFile": "Laai tans {{name}} op ...", - "fileName": "Lêernaam", - "folderName": "Vouernaam", - "fileUploadedSuccessfully": "Lêer \"{{name}}\" is suksesvol opgelaai", - "failedToUploadFile": "Kon nie lêer oplaai nie", - "fileDownloadedSuccessfully": "Lêer \"{{name}}\" is suksesvol afgelaai", - "failedToDownloadFile": "Kon nie lêer aflaai nie", - "fileCreatedSuccessfully": "Lêer \"{{name}}\" suksesvol geskep", - "folderCreatedSuccessfully": "Lêer \"{{name}}\" suksesvol geskep", - "failedToCreateItem": "Kon nie item skep nie", - "operationFailed": "{{operation}} -bewerking het misluk vir {{name}}: {{error}}", - "failedToResolveSymlink": "Kon nie simskakel oplos nie", - "itemsDeletedSuccessfully": "{{count}} items is suksesvol verwyder", - "failedToDeleteItems": "Kon nie items uitvee nie", - "sudoPasswordRequired": "Administrateurwagwoord benodig", - "enterSudoPassword": "Voer die sudo-wagwoord in om hierdie operasie voort te sit", - "sudoPassword": "Sudo-wagwoord", - "sudoOperationFailed": "Sudo-bewerking het misluk", - "sudoAuthFailed": "Sudo-verifikasie het misluk", - "dragFilesToUpload": "Plaas lêers hier om op te laai", - "emptyFolder": "Hierdie vouer is leeg", - "searchFiles": "Soek lêers...", - "upload": "Oplaai", - "selectHostToStart": "Kies 'n gasheer om lêerbestuur te begin", - "sshRequiredForFileManager": "Lêerbestuurder benodig SSH. Hierdie gasheer het nie SSH geaktiveer nie.", - "failedToConnect": "Kon nie aan SSH koppel nie", - "failedToLoadDirectory": "Kon nie gids laai nie", - "noSSHConnection": "Geen SSH-verbinding beskikbaar nie", - "copy": "Kopieer", - "cut": "Sny", - "paste": "Plak", - "copyPath": "Kopieer Pad", - "copyPaths": "Kopieer paaie", - "delete": "Vee uit", - "properties": "Eienskappe", - "refresh": "Verfris", - "downloadFiles": "Laai {{count}} lêers na die blaaier af", - "copyFiles": "Kopieer {{count}} items", - "cutFiles": "Sny {{count}} items", - "deleteFiles": "Vee {{count}} items uit", - "filesCopiedToClipboard": "{{count}} items gekopieer na knipbord", - "filesCutToClipboard": "{{count}} items is na knipbord gesny", - "pathCopiedToClipboard": "Pad gekopieer na knipbord", - "pathsCopiedToClipboard": "{{count}} paaie gekopieer na knipbord", - "failedToCopyPath": "Kon nie pad na knipbord kopieer nie", - "movedItems": "Het {{count}} items geskuif", - "failedToDeleteItem": "Kon nie item verwyder nie", - "itemRenamedSuccessfully": "{{type}} suksesvol hernoem", - "failedToRenameItem": "Kon nie item hernoem nie", - "download": "Laai af", - "permissions": "Toestemmings", - "size": "Grootte", - "modified": "Gewysig", - "path": "Pad", - "confirmDelete": "Is jy seker jy wil {{name}} verwyder?", - "permissionDenied": "Toestemming geweier", - "serverError": "Bedienerfout", - "fileSavedSuccessfully": "Lêer suksesvol gestoor", - "failedToSaveFile": "Kon nie lêer stoor nie", - "confirmDeleteSingleItem": "Is jy seker jy wil \"{{name}} \" permanent verwyder?", - "confirmDeleteMultipleItems": "Is jy seker jy wil {{count}} items permanent verwyder?", - "confirmDeleteMultipleItemsWithFolders": "Is jy seker jy wil {{count}} items permanent verwyder? Dit sluit gidse en hul inhoud in.", - "confirmDeleteFolder": "Is jy seker jy wil die vouer \"{{name}}\" en al sy inhoud permanent verwyder?", - "permanentDeleteWarning": "Hierdie aksie kan nie ongedaan gemaak word nie. Die item(s) sal permanent van die bediener verwyder word.", - "recent": "Onlangs", - "pinned": "Vasgepen", - "folderShortcuts": "Vouerkortpaaie", - "failedToReconnectSSH": "Kon nie SSH-sessie herkoppel nie", - "openTerminalHere": "Maak Terminaal Hier Oop", - "run": "Hardloop", - "openTerminalInFolder": "Maak Terminaal in Hierdie Vouer Oop", - "openTerminalInFileLocation": "Maak Terminaal oop by Lêerligging", - "runningFile": "Hardloop - {{file}}", - "onlyRunExecutableFiles": "Kan slegs uitvoerbare lêers uitvoer", - "directories": "Gidse", - "removedFromRecentFiles": "\"{{name}}\" is uit onlangse lêers verwyder", - "removeFailed": "Verwydering het misluk", - "unpinnedSuccessfully": "\"{{name}}\" suksesvol ontspeld", - "unpinFailed": "Ontspeld het misluk", - "removedShortcut": "Kortpad \"{{name}} \" verwyder", - "removeShortcutFailed": "Verwydering van kortpad het misluk", - "clearedAllRecentFiles": "Alle onlangse lêers is uitgevee", - "clearFailed": "Vee uit misluk", - "removeFromRecentFiles": "Verwyder uit onlangse lêers", - "clearAllRecentFiles": "Vee alle onlangse lêers uit", - "unpinFile": "Ontspeld lêer", - "removeShortcut": "Verwyder kortpad", - "pinFile": "Speld lêer vas", - "addToShortcuts": "Voeg by kortpaaie", - "pasteFailed": "Plak het misluk", - "noUndoableActions": "Geen ongedaanbare aksies nie", - "undoCopySuccess": "Kopieerbewerking ongedaan gemaak: Gekopieerde lêers is verwyder {{count}}", - "undoCopyFailedDelete": "Ongedaan maak het misluk: Kon geen gekopieerde lêers verwyder nie", - "undoCopyFailedNoInfo": "Ongedaan maak het misluk: Kon nie gekopieerde lêerinligting vind nie", - "undoMoveSuccess": "Ongedaan gemaak met skuifbewerking: {{count}} lêers teruggeskuif na oorspronklike ligging", - "undoMoveFailedMove": "Ongedaan maak het misluk: Kon geen lêers terugskuif nie", - "undoMoveFailedNoInfo": "Ongedaan maak het misluk: Kon nie inligting oor die verskuifde lêer vind nie", - "undoDeleteNotSupported": "Verwyderingsbewerking kan nie ongedaan gemaak word nie: Lêers is permanent van die bediener verwyder", - "undoTypeNotSupported": "Nie-ondersteunde ongedaanmaak-bewerkingtipe", - "undoOperationFailed": "Ongedaanmaak-bewerking het misluk", - "unknownError": "Onbekende fout", - "confirm": "Bevestig", - "find": "Vind...", - "replace": "Vervang", - "downloadInstead": "Laai eerder af", - "keyboardShortcuts": "Sleutelbordkortpaaie", - "searchAndReplace": "Soek en vervang", - "editing": "Redigering", - "search": "Soek", - "findNext": "Vind Volgende", - "findPrevious": "Vind Vorige", - "save": "Stoor", - "selectAll": "Kies Alles", - "undo": "Ongedaan maak", - "redo": "Herdoen", - "moveLineUp": "Skuif lyn op", - "moveLineDown": "Skuif lyn af", - "toggleComment": "Wissel kommentaar", - "autoComplete": "Outomatiese Voltooiing", - "imageLoadError": "Kon nie beeld laai nie", - "startTyping": "Begin tik...", - "unknownSize": "Onbekende grootte", - "fileIsEmpty": "Lêer is leeg", - "largeFileWarning": "Waarskuwing oor groot lêers", - "largeFileWarningDesc": "Hierdie lêer is {{size}} groot, wat werkverrigtingsprobleme kan veroorsaak wanneer dit as teks oopgemaak word.", - "fileNotFoundAndRemoved": "Lêer \"{{name}}\" nie gevind nie en is verwyder uit onlangse/vasgespelde lêers", - "failedToLoadFile": "Kon nie lêer laai nie: {{error}}", - "serverErrorOccurred": "Bedienerfout het voorgekom. Probeer asseblief later weer.", - "autoSaveFailed": "Outomatiese stoor het misluk", - "fileAutoSaved": "Lêer outomaties gestoor", - "moveFileFailed": "Kon nie {{name}} skuif nie", - "moveOperationFailed": "Skuifbewerking het misluk", - "canOnlyCompareFiles": "Kan slegs twee lêers vergelyk", - "comparingFiles": "Vergelyk lêers: {{file1}} en {{file2}}", - "dragFailed": "Sleepbewerking het misluk", - "filePinnedSuccessfully": "Lêer \"{{name}}\" suksesvol vasgepen", - "pinFileFailed": "Kon nie lêer vaspen nie", - "fileUnpinnedSuccessfully": "Lêer \"{{name}}\" suksesvol ontspeld", - "unpinFileFailed": "Kon nie lêer ontspeld nie", - "shortcutAddedSuccessfully": "Vouerkortpad \"{{name}}\" suksesvol bygevoeg", - "addShortcutFailed": "Kon nie kortpad byvoeg nie", - "operationCompletedSuccessfully": "{{operation}} {{count}} items suksesvol", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", "operationCompleted": "{{operation}} {{count}} items", - "downloadFileSuccess": "Lêer {{name}} suksesvol afgelaai", - "downloadFileFailed": "Aflaai het misluk", - "moveTo": "Skuif na {{name}}", - "diffCompareWith": "Verskil vergelyking met {{name}}", - "dragOutsideToDownload": "Sleep buite die venster om af te laai ({{count}} lêers)", - "newFolderDefault": "NuweVouer", - "newFileDefault": "NuweLêer.txt", - "successfullyMovedItems": "Suksesvol {{count}} items na {{target}} geskuif", - "move": "Beweeg", - "searchInFile": "Soek in lêer (Ctrl+F)", - "showKeyboardShortcuts": "Wys sleutelbordkortpaaie", - "startWritingMarkdown": "Begin om jou afslaginhoud te skryf...", - "loadingFileComparison": "Laai lêervergelyking...", - "reload": "Herlaai", - "compare": "Vergelyk", - "sideBySide": "Sy aan Sy", - "inline": "Inlyn", - "fileComparison": "Lêervergelyking: {{file1}} vs {{file2}}", - "fileTooLarge": "Lêer te groot: {{error}}", - "sshConnectionFailed": "SSH-verbinding het misluk. Kontroleer asseblief jou verbinding met {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Kon nie lêer laai nie: {{error}}", - "connecting": "Verbind...", - "connectedSuccessfully": "Suksesvol gekoppel", - "totpVerificationFailed": "TOTP-verifikasie het misluk", - "warpgateVerificationFailed": "Warpgate-verifikasie het misluk", - "authenticationFailed": "Verifikasie het misluk", - "verificationCodePrompt": "Verifikasiekode:", - "changePermissions": "Verander Toestemmings", - "currentPermissions": "Huidige Toestemmings", - "owner": "Eienaar", - "group": "Groep", - "others": "Ander", - "read": "Lees", - "write": "Skryf", - "execute": "Voer uit", - "permissionsChangedSuccessfully": "Toestemmings suksesvol verander", - "failedToChangePermissions": "Kon nie toestemmings verander nie", - "name": "Naam", - "sortByName": "Naam", - "sortByDate": "Datum Gewysig", - "sortBySize": "Grootte", - "ascending": "Stygend", - "descending": "Dalend", - "root": "Wortel", - "new": "Nuut", - "sortBy": "Sorteer volgens", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", "items": "Items", - "selected": "Geselekteerde", - "editor": "Redakteur", - "octal": "Oktaal", - "storage": "Berging", - "disk": "Skyf", - "used": "Gebruik", - "of": "van", - "toggleSidebar": "Wissel sybalk", - "cannotLoadPdf": "Kan nie PDF laai nie", - "pdfLoadError": "Daar was 'n fout tydens die laai van hierdie PDF-lêer.", - "loadingPdf": "Laai PDF...", - "loadingPage": "Laai bladsy..." + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopieer na gasheer…", - "moveToHost": "Skuif na gasheer…", - "copyItemsToHost": "Kopieer {{count}} items om… aan te bied", - "moveItemsToHost": "Skuif {{count}} items om… te huisves", - "noHostsConnected": "Geen ander lêerbestuurder-gashere beskikbaar nie.", - "noHostsConnectedHint": "Voeg nog 'n SSH-gasheer by met Lêerbestuurder geaktiveer in Gasheerbestuurder.", - "selectDestinationHost": "Kies bestemmingsgasheer", - "destinationPath": "Bestemmingspad", - "recentDestinations": "Onlangse bestemmings", - "collapseRecentDestinations": "Vou onlangse bestemmings in", - "expandRecentDestinations": "Vou onlangse bestemmings uit", - "browseFolders": "Blaai deur bestemmingsmappe", - "browseDestination": "Blaai of voer pad in", - "confirmCopy": "Kopieer", - "confirmMove": "Beweeg", - "transferring": "Oordra…", - "compressing": "Kompressering…", - "extracting": "Onttrek…", - "transferringItems": "Oordrag van {{current}} van {{total}} items…", - "transferSuccess": "Oordrag voltooi", - "transferError": "Oordrag het misluk", - "transferPartial": "Oordrag voltooi met {{count}} foute", - "transferPartialHint": "Kon nie oordra nie: {{paths}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", "itemsSummary": "{{count}} items", - "destMustBeDirectory": "Bestemming moet 'n gids wees vir oordragte van verskeie items", - "selectThisFolder": "Kies hierdie vouer", - "browsePathWillBeCreated": "Hierdie vouer bestaan nog nie. Dit sal geskep word wanneer die oordrag begin.", - "browsePathError": "Kon nie hierdie pad op die bestemmingsgasheer oopmaak nie.", - "goUp": "Gaan op", - "copyFolderToHost": "Kopieer lêergids na gasheer…", - "moveFolderToHost": "Skuif vouer na gasheer…", - "hostReady": "Gereed", - "hostConnecting": "Verbind…", - "hostDisconnected": "Nie gekoppel nie", - "hostAuthRequired": "Verifikasie vereis — maak eers Lêerbestuurder op hierdie gasheer oop", - "hostConnectionFailed": "Verbinding het misluk", - "metricsTitle": "Oordragtye", - "metricsPrepare": "Berei bestemming voor: {{duration}}", - "metricsCompress": "Kompresseer op bron: {{duration}}", - "metricsHopSourceRead": "Bron → bediener: {{throughput}}", - "metricsHopDestSftpWrite": "Bediener → bestemming (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Bediener → bestemming (plaaslik): {{throughput}}", - "metricsTransfer": "Van begin tot einde: {{throughput}} ({{duration}})", - "metricsExtract": "Uittreksel op bestemming: {{duration}}", - "metricsSourceDelete": "Verwyder van bron: {{duration}}", - "metricsTotal": "Totaal: {{duration}}", - "progressCompressing": "Kompressering op brongasheer…", - "progressExtracting": "Onttrek op bestemming…", - "progressTransferring": "Oordrag van data…", - "progressReconnecting": "Herverbind…", - "parallelSegmentsLabel": "Parallelle oordragbane", - "parallelSegmentsOption": "{{count}} bane", - "parallelSegmentsHint": "Groot lêers word in 256 MB-stukke verdeel. Verskeie bane gebruik afsonderlike verbindings (soos om verskeie oordragte te begin) vir hoër totale deurset.", - "progressTotalSpeed": "{{speed}} totaal ({{lanes}} bane)", - "progressTransferringItems": "Oordrag van lêers ({{current}} van {{total}})…", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} lêers", - "sourceNotDeletedPartial": "Bronlêers gehou (gedeeltelike oordrag)", - "jumpHostLimitation": "Beide gashere moet vanaf die Termix-bediener bereikbaar wees. Direkte gasheer-tot-gasheer-roetering word nie ondersteun nie.", - "cancel": "Kanselleer", - "methodLabel": "Oordragmetode", - "methodAuto": "Outomaties", - "methodTar": "Teer-argief", - "methodItemSftp": "SFTP per lêer", - "methodAutoHint": "Kies tar- of per-lêer SFTP gebaseer op lêertelling, grootte en saampersbaarheid. Enkele lêers gebruik altyd stroom-SFTP.", - "methodTarHint": "Komprimeer op bron, dra een argief oor, onttrek op bestemming. Vereis tar op beide Unix-gashere.", - "methodItemSftpHint": "Dra elke lêer individueel oor SFTP. Werk op alle gashere, insluitend Windows.", - "methodPreviewLoading": "Berekening van oordragmetode…", - "methodPreviewError": "Kon nie oordragmetode voorskou nie. Die bediener sal steeds 'n metode kies wanneer jy begin.", - "methodPreviewWillUseTar": "Sal gebruik: Tar-argief", - "methodPreviewWillUseItemSftp": "Sal gebruik: SFTP per lêer", - "methodPreviewScanSummary": "{{fileCount}} lêers, {{totalSize}} totaal (geskandeer op brongasheer).", - "methodItemSftpLimitation": "Elke lêer gebruik dieselfde SFTP-stroom as 'n enkellêerkopie, een na die ander. Vordering word oor alle lêers gekombineer, so die balk beweeg stadig tydens groot lêers.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Jy het SFTP per lêer gekies.", - "user_tar": "Jy het teer-argief gekies.", - "tar_unavailable": "Teer is nie beskikbaar op een of albei gashere nie — SFTP per lêer sal eerder gebruik word.", - "windows_host": "'n Windows-gasheer is betrokke — teer word nie gebruik nie.", - "auto_multi_large": "Outomaties: veelvuldige lêers, insluitend 'n groot lêer ({{largestSize}}) met saampersbare data — teer bundels in een oordrag.", - "auto_single_large_in_archive": "Outomaties: een groot lêer ({{largestSize}}) in hierdie stel — SFTP per lêer.", - "auto_many_incompressible": "Outomaties: meestal onsaampersbare data — SFTP per lêer.", - "auto_many_files": "Outomaties: baie lêers ({{fileCount}}) — teer verminder oorhoofse koste per lêer.", - "auto_default": "Outomaties: SFTP per lêer vir hierdie stel." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Kanselleer", - "progressCancelling": "Kanselleer…", - "progressStalled": "Vasgevang", - "resumedHint": "Herverbind met 'n aktiewe oordrag wat in 'n ander venster begin is.", - "transferCancelled": "Oordrag gekanselleer", - "transferCancelledCopyHint": "Gedeeltelike lêers mag dalk op die bestemming bly.", - "transferCancelledMoveHint": "Gedeeltelike lêers mag dalk op die bestemming bly. Bronlêers is nie verwyder nie.", - "cleanupDestFiles": "Maak bestemming skoon", - "cleanupDestFilesSuccess": "Gedeeltelike lêers van die bestemming verwyder", - "cleanupDestFilesPartial": "Sommige gedeeltelike lêers kon nie verwyder word nie", - "cleanupDestFilesNothing": "Niks om op die bestemming skoon te maak nie", - "cleanupDestFilesError": "Opruiming het misluk", - "retryTransfer": "Probeer weer", - "retryTransferError": "Herprobeer het misluk", - "transferFailedRetryHint": "Gedeeltelike data is op die bestemming gehou. Herprobeer sal hervat word wanneer die verbinding terug is." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Geen SSH-tonnels nie", - "createFirstTunnelMessage": "Jy het nog geen SSH-tonnels geskep nie. Konfigureer tonnelverbindings in die Gasheerbestuurder om te begin.", - "connected": "Verbonde", - "disconnected": "Ontkoppel", - "connecting": "Verbind...", - "error": "Fout", - "canceling": "Kanselleer tans...", - "connect": "Verbind", - "disconnect": "Ontkoppel", - "cancel": "Kanselleer", - "port": "Hawe", - "localPort": "Plaaslike Hawe", - "remotePort": "Afstandpoort", - "currentHostPort": "Huidige gasheerpoort", - "endpointPort": "Eindpuntpoort", - "bindIp": "Plaaslike IP", - "endpointSshConfig": "Eindpunt SSH-konfigurasie", - "endpointSshHost": "Eindpunt SSH-gasheer", - "endpointSshHostPlaceholder": "Kies 'n gekonfigureerde gasheer", - "endpointSshHostRequired": "Kies 'n eindpunt SSH-gasheer vir elke kliënttunnel.", - "attempt": "Poging {{current}} van {{max}}", - "nextRetryIn": "Volgende herprobeer oor {{seconds}} sekondes", - "clientTunnels": "Kliënttonnels", - "clientTunnel": "Kliënttonnel", - "addClientTunnel": "Voeg kliënttonnel by", - "noClientTunnels": "Geen kliënttunnels is op hierdie lessenaar gekonfigureer nie.", - "tunnelName": "Tonnelnaam", - "remoteHost": "Afstandgasheer", - "autoStart": "Outomatiese Begin", - "clientAutoStartDesc": "Begin wanneer hierdie rekenaarkliënt oopmaak en gekoppel bly.", - "clientManualStartDesc": "Gebruik Begin en Stop vanaf hierdie ry. Termix sal dit nie outomaties oopmaak nie.", - "clientRemoteServerNote": "Afstandsaanstuuring mag AllowTcpForwarding en GatewayPorts op die eindpunt SSH-bediener vereis. Die afstandspoort sluit wanneer hierdie lessenaar ontkoppel.", - "clientTunnelStarted": "Kliënttonnel het begin", - "clientTunnelStopped": "Kliënttonnel gestop", - "tunnelTestSucceeded": "Tonneltoets het geslaag", - "tunnelTestFailed": "Tonneltoets het misluk", - "localSaved": "Kliënttonnels gestoor", - "localSaveError": "Kon nie plaaslike kliënttonnels stoor nie", - "invalidBindIp": "Plaaslike IP moet 'n geldige IPv4-adres wees.", - "invalidLocalTargetIp": "Die plaaslike teiken-IP moet 'n geldige IPv4-adres wees.", - "invalidLocalPort": "Plaaslike poort moet tussen 1 en 65535 wees.", - "invalidRemotePort": "Afstandpoort moet tussen 1 en 65535 wees.", - "invalidLocalTargetPort": "Die plaaslike teikenpoort moet tussen 1 en 65535 wees.", - "invalidEndpointPort": "Eindpuntpoort moet tussen 1 en 65535 wees.", - "duplicateAutoStartBind": "Slegs een outomatiese begin-kliënttonnel kan {{bind}} gebruik.", - "manualControlError": "Kon nie tonnelstatus opdateer nie.", - "active": "Aktief", - "start": "Begin", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", "stop": "Stop", - "test": "Toets", - "type": "Tonnel Tipe", - "typeLocal": "Plaaslik (-L)", - "typeRemote": "Afstandbeheer (-R)", - "typeDynamic": "Dinamiese (-D)", - "typeServerLocalDesc": "Huidige gasheer na eindpunt.", - "typeServerRemoteDesc": "Eindpunt terug na huidige gasheer.", - "typeClientLocalDesc": "Plaaslike rekenaar na eindpunt.", - "typeClientRemoteDesc": "Eindpunt terug na plaaslike rekenaar.", - "typeClientDynamicDesc": "SOKKE op plaaslike rekenaar.", - "typeDynamicDesc": "Stuur SOCKS5 CONNECT-verkeer deur SSH aan", - "forwardDescriptionServerLocal": "Huidige gasheer {{sourcePort}} → eindpunt {{endpointPort}}.", - "forwardDescriptionServerRemote": "Eindpunt {{endpointPort}} → huidige gasheer {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS op huidige gasheer {{sourcePort}}.", - "forwardDescriptionClientLocal": "Plaaslik {{sourcePort}} → afgeleë {{endpointPort}}.", - "forwardDescriptionClientRemote": "Afstand {{sourcePort}} → plaaslik {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS op plaaslike poort {{sourcePort}}.", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOKKIES via {{endpoint}}", - "autoNameClientLocal": "Plaaslik {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → plaaslik {{localPort}}", - "autoNameClientDynamic": "SOKKIES {{localPort}} via {{endpoint}}", - "route": "Roete:", - "lastStarted": "Laas begin", - "lastTested": "Laas getoets", - "lastError": "Laaste fout", - "maxRetries": "Maksimum herprobeer", - "maxRetriesDescription": "Maksimum aantal herprobeerpogings.", - "retryInterval": "Herprobeerinterval (sekondes)", - "retryIntervalDescription": "Tyd om te wag tussen herpogings.", - "local": "Plaaslik", - "remote": "Afstandsbediening", - "destination": "Bestemming", - "host": "Gasheer", - "mode": "Modus", - "noHostSelected": "Geen gasheer gekies nie", - "working": "Werk..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "SVE", - "memory": "Geheue", - "disk": "Skyf", - "network": "Netwerk", - "uptime": "Optyd", - "processes": "Prosesse", - "available": "Beskikbaar", - "free": "Gratis", - "connecting": "Verbind...", - "connectionFailed": "Kon nie aan bediener koppel nie", - "naCpus": "N/A SVE(s)", - "cpuCores_one": "{{count}} Kern", - "cpuCores_other": "{{count}} Kerne", - "cpuUsage": "SVE-gebruik", - "memoryUsage": "Geheuegebruik", - "diskUsage": "Skyfgebruik", - "failedToFetchHostConfig": "Kon nie gasheerkonfigurasie haal nie", - "serverOffline": "Bediener vanlyn", - "cannotFetchMetrics": "Kan nie statistieke van vanlyn bediener haal nie", - "totpFailed": "TOTP-verifikasie het misluk", - "noneAuthNotSupported": "Bedienerstatistieke ondersteun nie die 'geen'-verifikasietipe nie.", - "load": "Laai", - "systemInfo": "Stelselinligting", - "hostname": "Gasheernaam", - "operatingSystem": "Bedryfstelsel", - "kernel": "Kern", - "seconds": "sekondes", - "networkInterfaces": "Netwerkkoppelvlakke", - "noInterfacesFound": "Geen netwerkkoppelvlakke gevind nie", - "noProcessesFound": "Geen prosesse gevind nie", - "loginStats": "SSH-aanmeldingstatistieke", - "noRecentLoginData": "Geen onlangse aanmelddata nie", - "executingQuickAction": "Voer tans {{name}} uit ...", - "quickActionSuccess": "{{name}} suksesvol voltooi", - "quickActionFailed": "{{name}} het misluk", - "quickActionError": "Kon nie {{name}} uitvoer nie", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Luisterpoorte", - "protocol": "Protokol", - "port": "Hawe", - "address": "Adres", - "process": "Proses", - "noData": "Geen luisterpoorte data nie" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Onaktief", - "policy": "Beleid", - "rules": "reëls", - "noData": "Geen firewalldata beskikbaar nie", - "action": "Aksie", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Hawe", - "source": "Bron", - "anywhere": "Enige plek" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Laai Gem.", - "swap": "Ruil", - "architecture": "Argitektuur", - "refresh": "Verfris", - "retry": "Probeer weer" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Self-gehoste SSH en afstandrekenaarbestuur", - "loginTitle": "Teken aan by Termix", - "registerTitle": "Skep Rekening", - "forgotPassword": "Wagwoord vergeet?", - "rememberMe": "Onthou toestel vir 30 dae (sluit TOTP in)", - "noAccount": "Het jy nie 'n rekening nie?", - "hasAccount": "Het jy reeds 'n rekening?", - "twoFactorAuth": "Twee-faktor-verifikasie", - "enterCode": "Voer verifikasiekode in", - "backupCode": "Of gebruik rugsteunkode", - "verifyCode": "Verifieer Kode", - "redirectingToApp": "Herlei na toepassing...", - "sshAuthenticationRequired": "SSH-verifikasie vereis", - "sshNoKeyboardInteractive": "Sleutelbord-interaktiewe verifikasie nie beskikbaar nie", - "sshAuthenticationFailed": "Verifikasie het misluk", - "sshAuthenticationTimeout": "Verifikasie-tydverstryking", - "sshNoKeyboardInteractiveDescription": "Die bediener ondersteun nie sleutelbord-interaktiewe verifikasie nie. Verskaf asseblief u wagwoord of SSH-sleutel.", - "sshAuthFailedDescription": "Die verskafde aanmeldbesonderhede was verkeerd. Probeer asseblief weer met geldige aanmeldbesonderhede.", - "sshTimeoutDescription": "Die verifikasiepoging het verstryk. Probeer asseblief weer.", - "sshProvideCredentialsDescription": "Verskaf asseblief u SSH-besonderhede om aan hierdie bediener te koppel.", - "sshPasswordDescription": "Voer die wagwoord vir hierdie SSH-verbinding in.", - "sshKeyPasswordDescription": "As jou SSH-sleutel geïnkripteer is, voer die wagwoordfrase hier in.", - "passphraseRequired": "Wagwoord vereis", - "passphraseRequiredDescription": "Die SSH-sleutel is geïnkripteer. Voer asseblief die wagwoordfrase in om dit te ontsluit.", - "back": "Terug", - "firstUser": "Eerste Gebruiker", - "firstUserMessage": "Jy is die eerste gebruiker en sal 'n administrateur gemaak word. Jy kan administrateurinstellings in die kantbalk se gebruikersaftreklys sien. As jy dink dit is 'n fout, kyk na die docker-logboeke of skep 'n GitHub-probleem.", - "external": "Eksterne", - "loginWithExternal": "Teken aan met eksterne verskaffer", - "loginWithExternalDesc": "Meld aan met jou gekonfigureerde eksterne identiteitsverskaffer", - "externalNotSupportedInElectron": "Eksterne verifikasie word nog nie in die Electron-app ondersteun nie. Gebruik asseblief die webweergawe vir OIDC-aanmelding.", - "resetPasswordButton": "Herstel wagwoord", - "sendResetCode": "Stuur Herstelkode", - "resetCodeDesc": "Voer jou gebruikersnaam in om 'n wagwoordherstelkode te ontvang. Die kode sal in die docker-houerlogboeke aangeteken word.", - "resetCode": "Herstel kode", - "verifyCodeButton": "Verifieer Kode", - "enterResetCode": "Voer die 6-syfer-kode van die docker-houerlogboeke vir die gebruiker in:", - "newPassword": "Nuwe Wagwoord", - "confirmNewPassword": "Bevestig wagwoord", - "enterNewPassword": "Voer jou nuwe wagwoord vir die gebruiker in:", - "signUp": "Registreer", - "desktopApp": "Rekenaartoepassing", - "loggingInToDesktopApp": "Aanmelding by die lessenaar-app", - "loadingServer": "Laai bediener...", - "dataLossWarning": "As jy jou wagwoord op hierdie manier terugstel, sal al jou gestoorde SSH-gashere, geloofsbriewe en ander geïnkripteerde data uitgevee word. Hierdie aksie kan nie ongedaan gemaak word nie. Gebruik dit slegs as jy jou wagwoord vergeet het en nie aangemeld is nie.", - "authenticationDisabled": "Verifikasie gedeaktiveer", - "authenticationDisabledDesc": "Alle verifikasiemetodes is tans gedeaktiveer. Kontak asseblief u administrateur.", - "attemptsRemaining": "{{count}} pogings oor" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifieer SSH-gasheersleutel", - "keyChangedWarning": "SSH-gasheersleutel verander", - "firstConnectionTitle": "Eerste keer dat jy met hierdie gasheer verbind", - "firstConnectionDescription": "Die egtheid van hierdie gasheer kan nie vasgestel word nie. Verifieer dat die vingerafdruk ooreenstem met wat jy verwag.", - "keyChangedDescription": "Die gasheersleutel vir hierdie bediener het verander sedert jou laaste verbinding. Dit kan op 'n sekuriteitsprobleem dui.", - "previousKey": "Vorige Sleutel", - "newFingerprint": "Nuwe vingerafdruk", - "fingerprint": "Vingerafdruk", - "verifyInstructions": "As jy hierdie gasheer vertrou, klik Aanvaar om voort te gaan en hierdie vingerafdruk vir toekomstige verbindings te stoor.", - "securityWarning": "Sekuriteitswaarskuwing", - "acceptAndContinue": "Aanvaar en Gaan voort", - "acceptNewKey": "Aanvaar Nuwe Sleutel & Gaan Voort" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Kon nie aan die databasis koppel nie", - "unknownError": "Onbekende fout", - "loginFailed": "Aanmelding het misluk", - "failedPasswordReset": "Kon nie wagwoordherstel begin nie", - "failedVerifyCode": "Kon nie die terugstelkode verifieer nie", - "failedCompleteReset": "Kon nie wagwoordterugstelling voltooi nie", - "invalidTotpCode": "Ongeldige TOTP-kode", - "failedOidcLogin": "Kon nie OIDC-aanmelding begin nie", - "silentSigninOidcUnavailable": "Stille aanmelding is versoek, maar OIDC-aanmelding is nie beskikbaar nie.", - "failedUserInfo": "Kon nie gebruikersinligting kry na aanmelding nie", - "oidcAuthFailed": "OIDC-verifikasie het misluk", - "invalidAuthUrl": "Ongeldige magtigings-URL ontvang van backend", - "requiredField": "Hierdie veld is verpligtend", - "minLength": "Minimum lengte is {{min}}", - "passwordMismatch": "Wagwoorde stem nie ooreen nie", - "passwordLoginDisabled": "Gebruikersnaam/wagwoord-aanmelding is tans gedeaktiveer", - "sessionExpired": "Sessie het verstryk - meld asseblief weer aan", - "totpRateLimited": "Tempo beperk: Te veel TOTP-verifikasiepogings. Probeer asseblief later weer.", - "totpRateLimitedWithTime": "Tempo beperk: Te veel TOTP-verifikasiepogings. Wag asseblief {{time}} sekondes voordat u weer probeer.", - "resetCodeRateLimited": "Tempo beperk: Te veel verifikasiepogings. Probeer asseblief later weer.", - "resetCodeRateLimitedWithTime": "Tempo beperk: Te veel verifikasiepogings. Wag asseblief {{time}} sekondes voordat u weer probeer.", - "authTokenSaveFailed": "Kon nie verifikasietoken stoor nie", - "failedToLoadServer": "Kon nie bediener laai nie" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Nuwe rekeningregistrasie is tans deur 'n administrateur gedeaktiveer. Meld asseblief aan of kontak 'n administrateur.", - "userNotAllowed": "Jou rekening is nie gemagtig om te registreer nie. Kontak asseblief 'n administrateur.", - "databaseConnectionFailed": "Kon nie aan die databasisbediener koppel nie", - "resetCodeSent": "Herstel kode gestuur na Docker-logboeke", - "codeVerified": "Kode suksesvol geverifieer", - "passwordResetSuccess": "Wagwoord herstel suksesvol", - "loginSuccess": "Aanmelding suksesvol", - "registrationSuccess": "Registrasie suksesvol" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Plaaslike lessenaartunnels wat op gekonfigureerde SSH-gashere fokus.", - "c2sTunnelPresets": "Kliënttonnelvoorinstellings", - "c2sTunnelPresetsDesc": "Stoor hierdie lessenaarkliënt se plaaslike tonnellys as 'n benoemde bedienervoorinstelling, of laai 'n voorinstelling terug in hierdie kliënt.", - "c2sTunnelPresetsUnavailable": "Kliënttunnelvoorinstellings is slegs beskikbaar in die rekenaarkliënt.", - "c2sPresetName": "Voorafingestelde Naam", - "c2sPresetNamePlaceholder": "Kliëntvooringestelde naam", - "c2sPresetToLoad": "Voorafinstelling om te laai", - "c2sNoPresetSelected": "Geen voorafinstelling gekies nie", - "c2sNoPresets": "Geen voorinstellings gestoor nie", - "c2sLoadPreset": "Laai", - "c2sCurrentLocalConfig": "{{count}} plaaslike kliënttonnel(s) gekonfigureer op hierdie lessenaar.", - "c2sPresetSyncNote": "Voorinstellings is eksplisiete kiekies; die laai van een vervang hierdie lessenaarkliënt se plaaslike kliënttonnellys.", - "c2sPresetSaved": "Kliënttonnelvoorinstelling gestoor", - "c2sPresetLoaded": "Kliënttonnelvoorinstelling plaaslik gelaai", - "c2sPresetRenamed": "Kliënttonnel-voorinstelling hernoem", - "c2sPresetDeleted": "Kliënttonnel-voorinstelling verwyder", - "c2sPresetLoadError": "Kon nie kliënttonnelvoorinstellings laai nie" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Taal", - "keyPassword": "sleutelwagwoord", - "pastePrivateKey": "Plak jou privaat sleutel hier...", - "localListenerHost": "127.0.0.1 (luister plaaslik)", - "localTargetHost": "127.0.0.1 (teiken op hierdie rekenaar)", - "socksListenerHost": "127.0.0.1 (SOCKS-luisteraar)", - "enterPassword": "Voer jou wagwoord in", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "Dashboard", - "loading": "Laai paneelbord...", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Ondersteuning", - "discord": "Disharmonie", - "serverOverview": "Bediener Oorsig", - "version": "Weergawe", - "upToDate": "Op datum", - "updateAvailable": "Opdatering beskikbaar", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Optyd", - "database": "Databasis", - "healthy": "Gesond", - "error": "Fout", - "totalHosts": "Totale gashere", - "totalTunnels": "Totale tonnels", - "totalCredentials": "Totale geloofsbriewe", - "recentActivity": "Onlangse Aktiwiteit", - "reset": "Herstel", - "loadingRecentActivity": "Laai onlangse aktiwiteit...", - "noRecentActivity": "Geen onlangse aktiwiteit nie", - "quickActions": "Vinnige Aksies", - "addHost": "Voeg gasheer by", - "addCredential": "Voeg geloofsbriewe by", - "adminSettings": "Admin-instellings", - "userProfile": "Gebruikersprofiel", - "serverStats": "Bedienerstatistieke", - "loadingServerStats": "Laai bedienerstatistieke...", - "noServerData": "Geen bedienerdata beskikbaar nie", - "cpu": "SVE", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Pasmaak Dashboard", - "dashboardSettings": "Dashboard-instellings", - "enableDisableCards": "Aktiveer/Deaktiveer Kaarte", - "resetLayout": "Stel terug na verstekwaarde", - "serverOverviewCard": "Bediener Oorsig", - "recentActivityCard": "Onlangse Aktiwiteit", - "networkGraphCard": "Netwerkgrafiek", - "networkGraph": "Netwerkgrafiek", - "quickActionsCard": "Vinnige Aksies", - "serverStatsCard": "Bedienerstatistieke", - "panelMain": "Hoof", - "panelSide": "Kant", - "justNow": "nou net" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABIEL", - "hostsOnline": "Aanlyn gashere", - "activeTunnels": "Aktiewe Tonnels", - "registerNewServer": "Registreer 'n nuwe bediener", - "storeSshKeysOrPasswords": "Stoor SSH-sleutels of wagwoorde", - "manageUsersAndRoles": "Bestuur gebruikers en rolle", - "manageYourAccount": "Bestuur jou rekening", - "hostStatus": "Gasheerstatus", - "noHostsConfigured": "Geen gashere gekonfigureer nie", - "online": "AANLYN", - "offline": "AFLYN", - "onlineLower": "Aanlyn", - "nodes": "{{count}} nodusse", - "add": "Voeg by:", - "commandPalette": "Opdragpalet", - "done": "Klaar", - "editModeInstructions": "Sleep kaarte om te herrangskik · Sleep die kolomverdeler om kolomme se grootte te verander · Sleep die onderste rand van 'n kaart om die hoogte daarvan te verander · Asblik om te verwyder", - "empty": "Leeg", - "clear": "Duidelik" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Voeg gasheer by", - "addGroup": "Voeg Groep by", - "addLink": "Voeg skakel by", - "zoomIn": "Inzoem", - "zoomOut": "Uitzoom", - "resetView": "Herstel aansig", - "selectHost": "Kies gasheer", - "chooseHost": "Kies 'n gasheer...", - "parentGroup": "Ouergroep", - "noGroup": "Geen Groep", - "groupName": "Groepnaam", - "color": "Kleur", - "source": "Bron", - "target": "Teiken", - "moveToGroup": "Skuif na Groep", - "selectGroup": "Kies groep...", - "addConnection": "Voeg verbinding by", - "hostDetails": "Gasheerbesonderhede", - "removeFromGroup": "Verwyder uit groep", - "addHostHere": "Voeg gasheer hier by", - "editGroup": "Wysig Groep", - "delete": "Vee uit", - "add": "Voeg by", - "create": "Skep", - "move": "Beweeg", - "connect": "Verbind", - "createGroup": "Skep Groep", - "selectSourcePlaceholder": "Kies Bron...", - "selectTargetPlaceholder": "Kies Teiken...", - "invalidFile": "Ongeldige lêer", - "hostAlreadyExists": "Gasheer is reeds in die topologie", - "connectionExists": "Verbinding bestaan reeds", - "unknown": "Onbekend", - "name": "Naam", - "ip": "IP-adres", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", "status": "Status", - "failedToAddNode": "Kon nie nodus byvoeg nie", - "sourceDifferentFromTarget": "Bron en teiken moet verskillend wees", - "exportJSON": "Voer JSON uit", - "importJSON": "Voer JSON in", - "terminal": "Terminaal", - "fileManager": "Lêerbestuurder", - "tunnel": "Tonnel", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Bedienerstatistieke", - "noNodes": "Geen nodusse nog nie" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker is nie vir hierdie gasheer geaktiveer nie", - "validating": "Valideer Docker...", - "connecting": "Verbind...", - "error": "Fout", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Kon nie aan Docker koppel nie", - "containerStarted": "Houer {{name}} het begin", - "failedToStartContainer": "Kon nie houer {{name}} begin nie", - "containerStopped": "Houer {{name}} gestop", - "failedToStopContainer": "Kon nie houer {{name}} stop nie", - "containerRestarted": "Houer {{name}} herbegin", - "failedToRestartContainer": "Kon nie houer {{name}} herbegin nie", - "containerPaused": "Houer {{name}} gepouseer", - "containerUnpaused": "Houer {{name}} ontpoos", - "failedToTogglePauseContainer": "Kon nie pousestatus vir houer {{name}} wissel nie", - "containerRemoved": "Houer {{name}} is verwyder", - "failedToRemoveContainer": "Kon nie houer {{name}} verwyder nie", - "image": "Beeld", - "ports": "Hawens", - "noPorts": "Geen poorte nie", - "start": "Begin", - "confirmRemoveContainer": "Is jy seker jy wil die houer '{{name}}' verwyder? Hierdie aksie kan nie ongedaan gemaak word nie.", - "runningContainerWarning": "Waarskuwing: Hierdie houer loop tans. As dit verwyder word, sal die houer eers gestop word.", - "loadingContainers": "Laai houers...", - "manager": "Docker Bestuurder", - "autoRefresh": "Outomatiese Herlaai", - "timestamps": "Tydstempels", - "lines": "Lyne", - "filterLogs": "Filtreer logboeke...", - "refresh": "Verfris", - "download": "Laai af", - "clear": "Duidelik", - "logsDownloaded": "Logboeke suksesvol afgelaai", - "last50": "Laaste 50", - "last100": "Laaste 100", - "last500": "Laaste 500", - "last1000": "Laaste 1000", - "allLogs": "Alle logboeke", - "noLogsMatching": "Geen logboeke wat ooreenstem met \"{{query}}\"", - "noLogsAvailable": "Geen logboeke beskikbaar nie", - "noContainersFound": "Geen houers gevind nie", - "noContainersFoundHint": "Geen Docker-houers is op hierdie gasheer beskikbaar nie", - "searchPlaceholder": "Soek houers...", - "allStatuses": "Alle statusse", - "stateRunning": "Hardloop", - "statePaused": "Onderbreek", - "stateExited": "Uitgegaan", - "stateRestarting": "Herbegin", - "noContainersMatchFilters": "Geen houers stem ooreen met jou filters nie", - "noContainersMatchFiltersHint": "Probeer om jou soek- of filterkriteria aan te pas", - "failedToFetchStats": "Kon nie houerstatistieke haal nie", - "containerNotRunning": "Houer loop nie", - "startContainerToViewStats": "Begin die houer om statistieke te sien", - "loadingStats": "Laai statistieke...", - "errorLoadingStats": "Fout by die laai van statistieke", - "noStatsAvailable": "Geen statistieke beskikbaar nie", - "cpuUsage": "SVE-gebruik", - "current": "Huidige", - "memoryUsage": "Geheuegebruik", - "networkIo": "Netwerk I/O", - "input": "Invoer", - "output": "Uitset", - "blockIo": "Blok I/O", - "read": "Lees", - "write": "Skryf", - "pids": "PID's", - "containerInformation": "Houerinligting", - "name": "Naam", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Staat", - "containerMustBeRunning": "Die houer moet loop om toegang tot die konsole te verkry", - "verificationCodePrompt": "Voer verifikasiekode in", - "totpVerificationFailed": "TOTP-verifikasie het misluk. Probeer asseblief weer.", - "warpgateVerificationFailed": "Warpgate-verifikasie het misluk. Probeer asseblief weer.", - "connectedTo": "Gekoppel aan {{containerName}}", - "disconnected": "Ontkoppel", - "consoleError": "Konsolefout", - "errorMessage": "Fout: {{message}}", - "failedToConnect": "Kon nie aan houer koppel nie", - "console": "Konsole", - "selectShell": "Kies dop", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "sj", - "ash": "as", - "connect": "Verbind", - "disconnect": "Ontkoppel", - "notConnected": "Nie gekoppel nie", - "clickToConnect": "Klik verbind om 'n dopsessie te begin", - "connectingTo": "Verbind met {{containerName}}...", - "containerNotFound": "Houer nie gevind nie", - "backToList": "Terug na lys", - "logs": "Logboeke", - "stats": "Statistiek", - "consoleTab": "Konsole", - "startContainerToAccess": "Begin die houer om toegang tot die konsole te verkry" + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Algemeen", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Gebruikers", - "sectionSessions": "Sessies", - "sectionRoles": "Rolle", - "sectionDatabase": "Databasis", - "sectionApiKeys": "API-sleutels", - "allowRegistration": "Laat gebruikersregistrasie toe", - "allowRegistrationDesc": "Laat nuwe gebruikers hulself registreer", - "allowPasswordLogin": "Laat Wagwoord Aanmelding Toe", - "allowPasswordLoginDesc": "Gebruikersnaam/wagwoord aanmelding", - "oidcAutoProvision": "OIDC Outomatiese Voorsiening", - "oidcAutoProvisionDesc": "Skep outomaties rekeninge vir OIDC-gebruikers, selfs wanneer registrasie gedeaktiveer is.", - "allowPasswordReset": "Laat wagwoordherstel toe", - "allowPasswordResetDesc": "Herstel kode via Docker-logboeke", - "sessionTimeout": "Sessie-tydsbeperking", - "hours": "ure", - "sessionTimeoutRange": "Min 1 uur · Maks 720 uur", - "monitoringDefaults": "Monitering van Standaardwaardes", - "statusCheck": "Statuskontrole", - "metrics": "Metrieke", - "sec": "sek", - "logLevel": "Logvlak", - "enableGuacamole": "Aktiveer Guacamole", - "enableGuacamoleDesc": "RDP/VNC-afstandlessenaar", - "guacdUrl": "guacd-URL", - "oidcDescription": "Konfigureer OpenID Connect vir SSO. Velde gemerk met * is verpligtend.", - "oidcClientId": "Kliënt-ID", - "oidcClientSecret": "Kliëntgeheim", - "oidcAuthUrl": "Magtigings-URL", - "oidcIssuerUrl": "Uitreiker-URL", - "oidcTokenUrl": "Teken-URL", - "oidcUserIdentifier": "Gebruikersidentifiseerderpad", - "oidcDisplayName": "Vertoonnaampad", - "oidcScopes": "Omvangsgebiede", - "oidcUserinfoUrl": "Oorskryf Gebruikersinligting URL", - "oidcAllowedUsers": "Toegelate Gebruikers", - "oidcAllowedUsersDesc": "Een e-pos per reël. Los leeg om alles toe te laat.", - "removeOidc": "Verwyder", - "usersCount": "{{count}} gebruikers", - "createUser": "Skep", - "newRole": "Nuwe Rol", - "roleName": "Naam", - "roleDisplayName": "Vertoonnaam", - "roleDescription": "Beskrywing", - "rolesCount": "{{count}} rolle", - "createRole": "Skep", - "creating": "Skep...", - "exportDatabase": "Uitvoer van databasis", - "exportDatabaseDesc": "Laai 'n rugsteun van alle gashere, geloofsbriewe en instellings af", - "export": "Uitvoer", - "exporting": "Uitvoer tans...", - "importDatabase": "Voer databasis in", - "importDatabaseDesc": "Herstel vanaf 'n .sqlite rugsteunlêer", - "importDatabaseSelected": "Gekies: {{name}}", - "selectFile": "Kies Lêer", - "changeFile": "Verandering", - "import": "Invoer", - "importing": "Voer tans in...", - "apiKeysCount": "{{count}} sleutels", - "newApiKey": "Nuwe API-sleutel", - "apiKeyCreatedWarning": "Sleutel geskep - kopieer dit nou, dit sal nie weer gewys word nie.", - "apiKeyName": "Naam", - "apiKeyUser": "Gebruiker", - "apiKeySelectUser": "Kies 'n gebruiker...", - "apiKeyExpiresAt": "Vervaldatum", - "createKey": "Skep Sleutel", - "apiKeyNoExpiry": "Geen vervaldatum", - "revokedBadge": "HERROEP", - "authTypeDual": "Dubbele Magtiging", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Plaaslik", - "adminStatusAdministrator": "Administrateur", - "adminStatusRegularUser": "Gereelde Gebruiker", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "AANGEPAS", - "youBadge": "JY", - "sessionsActive": "{{count}} aktief", - "sessionActive": "Aktief: {{time}}", - "sessionExpires": "Vervaldatum: {{time}}", - "revokeAll": "Alles", - "revokeAllSessionsSuccess": "Alle sessies vir die gebruiker herroep", - "revokeAllSessionsFailed": "Kon nie sessies herroep nie", - "revokeSessionFailed": "Kon nie sessie herroep nie", - "addRole": "Voeg rol by", - "noCustomRoles": "Geen persoonlike rolle gedefinieer nie", - "removeRoleFailed": "Kon nie rol verwyder nie", - "assignRoleFailed": "Kon nie rol toewys nie", - "deleteRoleFailed": "Kon nie rol uitvee nie", - "userAdminAccess": "Administrateur", - "userAdminAccessDesc": "Volle toegang tot alle administrateurinstellings", - "userRoles": "Rolle", - "revokeAllUserSessions": "Herroep alle sessies", - "revokeAllUserSessionsDesc": "Forseer heraanmelding op alle toestelle", - "revoke": "Herroep", - "deleteUserWarning": "Die verwydering van hierdie gebruiker is permanent.", - "deleteUser": "Vee uit {{username}}", - "deleting": "Vee tans uit...", - "deleteUserFailed": "Kon nie gebruiker verwyder nie", - "deleteUserSuccess": "Gebruiker \"{{username}}\" is uitgevee", - "deleteRoleSuccess": "Rol \"{{name}}\" uitgevee", - "revokeKeySuccess": "Sleutel \"{{name}}\" herroep", - "revokeKeyFailed": "Kon nie sleutel herroep nie", - "copiedToClipboard": "Na knipbord gekopieer", - "done": "Klaar", - "createUserTitle": "Skep Gebruiker", - "createUserDesc": "Skep 'n nuwe plaaslike rekening.", - "createUserUsername": "Gebruikersnaam", - "createUserPassword": "Wagwoord", - "createUserPasswordHint": "Minimum 6 karakters.", - "createUserEnterUsername": "Voer gebruikersnaam in", - "createUserEnterPassword": "Voer wagwoord in", - "createUserSubmit": "Skep Gebruiker", - "editUserTitle": "Bestuur Gebruiker: {{username}}", - "editUserDesc": "Wysig rolle, administrateurstatus, sessies en rekeninginstellings.", - "editUserUsername": "Gebruikersnaam", - "editUserAuthType": "Magtigingsoort", - "editUserAdminStatus": "Adminstatus", - "editUserUserId": "Gebruikers-ID", - "linkAccountTitle": "Koppel OIDC aan wagwoordrekening", - "linkAccountDesc": "Voeg die OIDC-rekening {{username}} saam met 'n bestaande plaaslike rekening.", - "linkAccountWarningTitle": "Dit sal:", - "linkAccountEffect1": "Vee die OIDC-enigste rekening uit", - "linkAccountEffect2": "Voeg OIDC-aanmelding by die teikenrekening", - "linkAccountEffect3": "Laat beide OIDC- en wagwoordaanmelding toe", - "linkAccountTargetUsername": "Teikengebruikersnaam", - "linkAccountTargetPlaceholder": "Voer die plaaslike rekeninggebruikersnaam in om aan te koppel", - "linkAccounts": "Koppel rekeninge", - "linkAccountSuccess": "OIDC-rekening gekoppel aan \"{{username}}\"", - "linkAccountFailed": "Kon nie OIDC-rekening koppel nie", - "linkAccountInProgress": "Skakel tans...", - "saving": "Stoor...", - "updateRegistrationFailed": "Kon nie registrasie-instellings opdateer nie", - "updatePasswordLoginFailed": "Kon nie wagwoord-aanmeldinstelling opdateer nie", - "updateOidcAutoProvisionFailed": "Kon nie OIDC-outovoorsieningsinstelling opdateer nie", - "updatePasswordResetFailed": "Kon nie wagwoordterugstellinginstelling opdateer nie", - "sessionTimeoutRange2": "Sessie-tydsberekening moet tussen 1 en 720 uur wees", - "sessionTimeoutSaved": "Sessie-tydsbeperking gestoor", - "sessionTimeoutSaveFailed": "Kon nie sessie-tydsberekening stoor nie", - "monitoringIntervalInvalid": "Ongeldige intervalwaardes", - "monitoringSaved": "Moniteringsinstellings gestoor", - "monitoringSaveFailed": "Kon nie moniteringsinstellings stoor nie", - "guacamoleSaved": "Guacamole-instellings gestoor", - "guacamoleSaveFailed": "Kon nie Guacamole-instellings stoor nie", - "guacamoleUpdateFailed": "Kon nie Guacamole-instelling opdateer nie", - "logLevelUpdateFailed": "Kon nie logvlak opdateer nie", - "oidcSaved": "OIDC-konfigurasie gestoor", - "oidcSaveFailed": "Kon nie OIDC-konfigurasie stoor nie", - "oidcRemoved": "OIDC-konfigurasie verwyder", - "oidcRemoveFailed": "Kon nie OIDC-konfigurasie verwyder nie", - "createUserRequired": "Gebruikersnaam en wagwoord word vereis", - "createUserPasswordTooShort": "Wagwoord moet ten minste 6 karakters wees", - "createUserSuccess": "Gebruiker \"{{username}}\" geskep", - "createUserFailed": "Kon nie gebruiker skep nie", - "updateAdminStatusFailed": "Kon nie administrateurstatus opdateer nie", - "allSessionsRevoked": "Alle sessies herroep", - "revokeSessionsFailed": "Kon nie sessies herroep nie", - "createRoleRequired": "Naam en vertoonnaam word vereis", - "createRoleSuccess": "Rol \"{{name}}\" geskep", - "createRoleFailed": "Kon nie rol skep nie", - "apiKeyNameRequired": "Sleutelnaam word vereis", - "apiKeyUserRequired": "Gebruikers-ID word vereis", - "apiKeyCreatedSuccess": "API-sleutel \"{{name}}\" geskep", - "apiKeyCreateFailed": "Kon nie API-sleutel skep nie", - "exportSuccess": "Databasis suksesvol uitgevoer", - "exportFailed": "Databasis-uitvoer het misluk", - "importSelectFile": "Kies asseblief eers 'n lêer", - "importCompleted": "Invoer voltooi: {{total}} items ingevoer, {{skipped}} oorgeslaan", - "importFailed": "Invoer het misluk: {{error}}", - "importError": "Databasisinvoer het misluk" + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Gasheer", - "hostPlaceholder": "192.168.1.1 of example.com", - "portLabel": "Hawe", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Gebruikersnaam", - "usernamePlaceholder": "gebruikersnaam", - "authLabel": "Magtiging", - "passwordLabel": "Wagwoord", - "passwordPlaceholder": "wagwoord", - "privateKeyLabel": "Privaat Sleutel", - "privateKeyPlaceholder": "Plak privaat sleutel...", - "credentialLabel": "Geloofsbrief", - "credentialPlaceholder": "Kies 'n gestoorde geloofsbrief", - "connectToTerminal": "Koppel aan terminaal", - "connectToFiles": "Koppel aan lêers" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Geen terminaal gekies nie", - "noTerminalSelectedHint": "Maak 'n SSH-terminaal-oortjie oop om die opdraggeskiedenis te sien", - "searchPlaceholder": "Soekgeskiedenis...", - "clearAll": "Vee alles uit", - "noHistoryEntries": "Geen geskiedenisinskrywings nie", - "trackingDisabled": "Geskiedenisopsporing is gedeaktiveer", - "trackingDisabledHint": "Aktiveer dit in jou profielinstellings om bevele op te neem." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Sleutelopname", - "recordToTerminals": "Neem op na terminale", - "selectAll": "Alles", - "selectNone": "Geen", - "noTerminalTabsOpen": "Geen terminaal-oortjies oop nie", - "selectTerminalsAbove": "Kies terminale hierbo", - "broadcastInputPlaceholder": "Tik hier om toetsaanslagen uit te saai...", - "stopRecording": "Stop opname", - "startRecording": "Begin Opname", - "settingsTitle": "Instellings", - "enableRightClickCopyPaste": "Aktiveer regskliek kopieer/plak" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Uitleg", - "selectLayoutAbove": "Kies 'n uitleg hierbo", - "selectLayoutHint": "Kies hoeveel panele om te vertoon", - "panesTitle": "Ruite", - "openTabsTitle": "Maak oortjies oop", - "dragTabsHint": "Sleep oortjies na die panele hierbo, of gebruik Vinnige Toewysing", - "dropHere": "Laat val hier", - "emptyPane": "Leeg", + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", "dashboard": "Dashboard", - "clearSplitScreen": "Maak die gesplete skerm skoon", - "quickAssign": "Vinnige Toewysing", - "alreadyAssigned": "Ruit {{index}}", - "splitTab": "Verdeel oortjie", - "addToSplit": "Voeg by Split", - "removeFromSplit": "Verwyder van Split", - "assignToPane": "Toewys aan paneel" + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Skep brokkie", - "createSnippetDescription": "Skep 'n nuwe opdragbrokkie vir vinnige uitvoering", - "nameLabel": "Naam", - "namePlaceholder": "bv., Herbegin Nginx", - "descriptionLabel": "Beskrywing", - "descriptionPlaceholder": "Opsionele beskrywing", - "optional": "Opsioneel", - "folderLabel": "Vouer", - "noFolder": "Geen vouer nie (Ongekategoriseerd)", - "commandLabel": "Bevel", - "commandPlaceholder": "bv. sudo systemctl herbegin nginx", - "cancel": "Kanselleer", - "createSnippetButton": "Skep brokkie", - "createFolderTitle": "Skep vouer", - "createFolderDescription": "Organiseer jou brokkies in dopgehou", - "folderNameLabel": "Vouernaam", - "folderNamePlaceholder": "bv. Stelselopdragte, Docker-skripte", - "folderColorLabel": "Vouerkleur", - "folderIconLabel": "Vouer-ikoon", - "previewLabel": "Voorskou", - "folderNameFallback": "Vouernaam", - "createFolderButton": "Skep vouer", - "targetTerminals": "Teikenterminale", - "selectAll": "Alles", - "selectNone": "Geen", - "noTerminalTabsOpen": "Geen terminaal-oortjies oop nie", - "searchPlaceholder": "Soek brokkies...", - "newSnippet": "Nuwe brokkie", - "newFolder": "Nuwe vouer", - "run": "Hardloop", - "noSnippetsInFolder": "Geen brokkies in hierdie vouer nie", - "uncategorized": "Ongekategoriseerd", - "editSnippetTitle": "Wysig brokkie", - "editSnippetDescription": "Dateer hierdie opdragbrokkie op", - "saveSnippetButton": "Stoor veranderinge", - "createSuccess": "Brokkie suksesvol geskep", - "createFailed": "Kon nie brokkie skep nie", - "updateSuccess": "Brokkie suksesvol opgedateer", - "updateFailed": "Kon nie die brokkie opdateer nie", - "deleteFailed": "Kon nie fragment uitvee nie", - "folderCreateSuccess": "Vouer suksesvol geskep", - "folderCreateFailed": "Kon nie vouer skep nie", - "editFolderTitle": "Wysig vouer", - "editFolderDescription": "Hernoem of verander die voorkoms van hierdie vouer", - "saveFolderButton": "Stoor veranderinge", - "editFolder": "Wysig vouer", - "deleteFolder": "Vee vouer uit", - "folderDeleteSuccess": "Lêer \"{{name}}\" is uitgevee", - "folderDeleteFailed": "Kon nie vouer uitvee nie", - "folderEditSuccess": "Vouer suksesvol opgedateer", - "folderEditFailed": "Kon nie vouer opdateer nie", - "confirmRunMessage": "Begin \"{{name}}\"?", - "confirmRunButton": "Hardloop", - "runSuccess": "Het \"{{name}}\" in {{count}} terminaal(e) uitgevoer", - "copySuccess": "\"{{name}}\" na knipbord gekopieer", - "shareTitle": "Deel brokkie", - "shareUser": "Gebruiker", - "shareRole": "Rol", - "selectUser": "Kies 'n gebruiker...", - "selectRole": "Kies 'n rol...", - "shareSuccess": "Brokkie suksesvol gedeel", - "shareFailed": "Kon nie fragment deel nie", - "revokeSuccess": "Toegang herroep", - "revokeFailed": "Kon nie toegang herroep nie", - "currentAccess": "Huidige Toegang", - "shareLoadError": "Kon nie deeldata laai nie", - "loading": "Laai tans...", - "close": "Maak toe", - "reorderFailed": "Kon nie die volgorde van die brokkie stoor nie" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Rekening", - "sectionAppearance": "Voorkoms", - "sectionSecurity": "Sekuriteit", - "sectionApiKeys": "API-sleutels", - "sectionC2sTunnels": "C2S-tonnels", - "usernameLabel": "Gebruikersnaam", - "roleLabel": "Rol", - "roleAdministrator": "Administrateur", - "authMethodLabel": "Magtigingsmetode", - "authMethodLocal": "Plaaslik", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Aan", - "twoFaOff": "Af", - "versionLabel": "Weergawe", - "deleteAccount": "Vee rekening uit", - "deleteAccountDescription": "Vee jou rekening permanent uit", - "deleteButton": "Vee uit", - "deleteAccountPermanent": "Hierdie aksie is permanent en kan nie ongedaan gemaak word nie.", - "deleteAccountWarning": "Alle sessies, gashere, geloofsbriewe en instellings sal permanent uitgevee word.", - "confirmPasswordDeletePlaceholder": "Voer jou wagwoord in om te bevestig", - "languageLabel": "Taal", - "themeLabel": "Tema", - "fontSizeLabel": "Lettergrootte", - "accentColorLabel": "Aksentkleur", - "settingsTerminal": "Terminaal", - "commandAutocomplete": "Opdrag Outomatiese Voltooiing", - "commandAutocompleteDesc": "Wys outovoltooiing terwyl jy tik", - "historyTracking": "Geskiedenisopsporing", - "historyTrackingDesc": "Spoor terminaal opdragte", - "syntaxHighlighting": "Sintaksis-uitlig", - "syntaxHighlightingDesc": "Merk terminaaluitvoer", - "commandPalette": "Opdragpalet", - "commandPaletteDesc": "Aktiveer sleutelbordkortpaaie", - "reopenTabsOnLogin": "Heropen oortjies by aanmelding", - "reopenTabsOnLoginDesc": "Herstel jou oop oortjies wanneer jy aanmeld of die bladsy verfris, selfs vanaf 'n ander toestel", - "confirmTabClose": "Bevestig oortjie-sluiting", - "confirmTabCloseDesc": "Vra voordat terminaal-oortjies gesluit word", - "settingsSidebar": "Sybalk", - "showHostTags": "Wys gasheer-etikette", - "showHostTagsDesc": "Wys etikette in gasheerlys", - "hostTrayOnClick": "Klik om gasheeraksies uit te brei", - "hostTrayOnClickDesc": "Wys altyd verbindingsknoppies; klik om bestuursopsies uit te brei in plaas daarvan om te beweeg", - "pinAppRail": "Speld App-spoor vas", - "pinAppRailDesc": "Hou die linkerkantbalk-apprail altyd uitgebrei in plaas daarvan om uit te brei wanneer jy beweeg", - "settingsSnippets": "Brokkies", - "foldersCollapsed": "Vouers Ingevou", - "foldersCollapsedDesc": "Vou vouers standaard in", - "confirmExecution": "Bevestig Uitvoering", - "confirmExecutionDesc": "Bevestig voordat brokkies uitgevoer word", - "settingsUpdates": "Opdaterings", - "disableUpdateChecks": "Deaktiveer opdateringskontroles", - "disableUpdateChecksDesc": "Hou op om vir opdaterings te kyk", - "totpAuthenticator": "TOTP-verifikasie", - "totpEnabled": "2FA is geaktiveer", - "totpDisabled": "Voeg ekstra aanmeldsekuriteit by", - "disable": "Deaktiveer", - "enable": "Aktiveer", - "setupTotp": "Stel TOTP op", - "qrCode": "QR-kode", - "totpInstructions": "Skandeer QR-kode of voer geheim in jou verifikasie-app in, en voer dan die 6-syferkode in", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifieer", - "changePassword": "Verander wagwoord", - "currentPasswordLabel": "Huidige Wagwoord", - "currentPasswordPlaceholder": "Huidige wagwoord", - "newPasswordLabel": "Nuwe Wagwoord", - "newPasswordPlaceholder": "Nuwe wagwoord", - "confirmPasswordLabel": "Bevestig Nuwe Wagwoord", - "confirmPasswordPlaceholder": "Bevestig nuwe wagwoord", - "updatePassword": "Wagwoord opdateer", - "createApiKeyTitle": "Skep API-sleutel", - "createApiKeyDescription": "Genereer 'n nuwe API-sleutel vir programmatiese toegang.", - "apiKeyNameLabel": "Naam", - "apiKeyNamePlaceholder": "bv. CI-pyplyn", - "expiryDateLabel": "Vervaldatum", - "optional": "opsioneel", - "cancel": "Kanselleer", - "createKey": "Skep Sleutel", - "apiKeyCount": "{{count}} sleutels", - "newKey": "Nuwe Sleutel", - "noApiKeys": "Geen API-sleutels nog nie.", - "apiKeyActive": "Aktief", - "apiKeyUsageHint": "Sluit jou sleutel in die", - "apiKeyUsageHintHeader": "opskrif.", - "apiKeyPermissionsHint": "Sleutels erf die regte van die skeppergebruiker.", - "roleUser": "Gebruiker", - "authMethodDual": "Dubbele Magtiging", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Kon nie TOTP-opstelling begin nie", - "totpEnter6Digits": "Voer 'n 6-syferkode in", - "totpEnabledSuccess": "Tweefaktor-verifikasie geaktiveer", - "totpInvalidCode": "Ongeldige kode, probeer asseblief weer", - "totpDisableInputRequired": "Voer jou TOTP-kode of wagwoord in", - "totpDisabledSuccess": "Tweefaktor-verifikasie gedeaktiveer", - "totpDisableFailed": "Kon nie 2FA deaktiveer nie", - "totpDisableTitle": "Deaktiveer 2FA", - "totpDisablePlaceholder": "Voer TOTP-kode of wagwoord in", - "totpDisableConfirm": "Deaktiveer 2FA", - "totpContinueVerify": "Gaan voort om te verifieer", - "totpVerifyTitle": "Verifieer Kode", - "totpBackupTitle": "Rugsteunkodes", - "totpDownloadBackup": "Laai rugsteunkodes af", - "done": "Klaar", - "secretCopied": "Geheim gekopieer na knipbord", - "apiKeyNameRequired": "Sleutelnaam word vereis", - "apiKeyCreated": "API-sleutel \"{{name}}\" geskep", - "apiKeyCreateFailed": "Kon nie API-sleutel skep nie", - "apiKeyUser": "Gebruiker", - "apiKeyExpires": "Vervaldatum", - "apiKeyRevoked": "API-sleutel \"{{name}}\" herroep", - "apiKeyRevokeFailed": "Kon nie API-sleutel herroep nie", - "passwordFieldsRequired": "Huidige en nuwe wagwoorde word vereis", - "passwordMismatch": "Wagwoorde stem nie ooreen nie", - "passwordTooShort": "Wagwoord moet ten minste 6 karakters wees", - "passwordUpdated": "Wagwoord suksesvol opgedateer", - "passwordUpdateFailed": "Kon nie wagwoord opdateer nie", - "deletePasswordRequired": "Wagwoord word vereis om jou rekening te verwyder", - "deleteFailed": "Kon nie rekening uitvee nie", - "deleting": "Vee tans uit...", - "colorPickerTooltip": "Maak kleurkieser oop", - "themeSystem": "Stelsel", - "themeLight": "Lig", - "themeDark": "Donker", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", - "themeCatppuccin": "Katppuccin", - "themeNord": "Noord", - "themeSolarized": "Gesonaliseer", - "themeTokyoNight": "Tokio Nag", - "themeOneDark": "Een Donker", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ar_SA.json b/src/ui/locales/translated/ar_SA.json index 10abe315..9bc5af32 100644 --- a/src/ui/locales/translated/ar_SA.json +++ b/src/ui/locales/translated/ar_SA.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "مجلدات", - "folder": "مجلد", - "password": "كلمة المرور", - "key": "المفتاح", - "sshPrivateKey": "مفتاح SSH الخاص", - "upload": "تحميل", - "keyPassword": "كلمة المرور الرئيسية", - "sshKey": "مفتاح SSH", - "uploadPrivateKeyFile": "تحميل ملف المفتاح الخاص", - "searchCredentials": "البحث عن بيانات التفويض...", - "addCredential": "إضافة بيانات اعتماد", - "caCertificate": "شهادة CA (-cert.pub)", - "caCertificateDescription": "اختياري: تحميل أو لصق ملف الشهادة الموقعة CA-(على سبيل المثال id_ed25519-cert.pub). مطلوب عندما يستخدم خادم SSH الخاص بك الإذن المستند إلى الشهادة.", - "uploadCertFile": "تحميل ملف-cert.pub", - "clearCert": "مسح", - "certLoaded": "تم تحميل الشهادة", - "certPublicKeyLabel": "شهادة CA", - "certTypeLabel": "نوع الشهادة", - "pasteOrUploadCert": "لصق أو تحميل شهادة -cert.pub ...", - "hasCaCert": "حائز على شهادة CA", - "noCaCert": "لا توجد شهادة CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "فشل تحميل التنبيهات", - "failedToDismissAlert": "فشل في تجاهل التنبيه" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "تكوين الخادم", - "description": "تكوين عنوان URL للخادم Termix للاتصال بخدمات الخلفية", - "serverUrl": "رابط الخادم", - "enterServerUrl": "الرجاء إدخال رابط الخادم", - "saveFailed": "فشل في حفظ التكوين", - "saveError": "خطأ في حفظ الإعدادات", - "saving": "حفظ...", - "saveConfig": "حفظ الإعدادات", - "helpText": "أدخل عنوان URL حيث يعمل خادم Termix الخاص بك (على سبيل المثال: http://localhost:30001 أو https://your-server.com)", - "changeServer": "تغيير الخادم", - "mustIncludeProtocol": "عنوان URL للخادم يجب أن يبدأ مع http:// أو https://", - "allowInvalidCertificate": "السماح بشهادة غير صالحة", - "allowInvalidCertificateDesc": "استخدمه فقط للخوادم الموثوقة ذاتية الاستضافة التي تحتوي على شهادات موقعة ذاتيًا أو شهادات عناوين IP.", - "useEmbedded": "استخدام الخادم المحلي", - "embeddedDesc": "تشغيل ترميكس مع الخادم المحلي المدمج (لا حاجة للخادم البعيد)", - "embeddedConnecting": "جاري الاتصال بالخادم المحلي...", - "embeddedNotReady": "الخادم المحلي غير جاهز بعد. الرجاء الانتظار لحظة وحاول مرة أخرى.", - "localServer": "الخادم المحلي" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "خطأ في التحقق من الإصدار", - "checkFailed": "فشل التحقق من وجود تحديثات", - "upToDate": "التطبيق يصل إلى تاريخ", - "currentVersion": "أنت تقوم بتشغيل الإصدار {{version}}", - "updateAvailable": "تحديث متوفر", - "newVersionAvailable": "يتوفر إصدار جديد! أنت تقوم بتشغيل {{current}}، ولكن {{latest}} متاح.", - "betaVersion": "نسخة بيتا", - "betaVersionDesc": "أنت تقوم بتشغيل {{current}}، وهو أحدث من أحدث إصدار مستقر {{latest}}.", - "releasedOn": "صدر على {{date}}", - "downloadUpdate": "تنزيل التحديث", - "checking": "البحث عن تحديثات...", - "checkUpdates": "التحقق من وجود تحديثات", - "checkingUpdates": "البحث عن تحديثات...", - "updateRequired": "التحديث مطلوب" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "أغلق", + "close": "Close", "minimize": "Minimize", - "online": "متصل", - "offline": "غير متصل", - "continue": "متابعة", - "maintenance": "صيانة", - "degraded": "تدهورت", - "error": "خطأ", - "warning": "تحذير", - "unsavedChanges": "التغييرات غير المحفوظة", - "dismiss": "تجاهل", - "loading": "تحميل...", - "optional": "اختياري", - "connect": "الاتصال", - "copied": "منسوخ", - "connecting": "جاري الاتصال...", - "updateAvailable": "تحديث متوفر", - "appName": "تيريمكس", - "openInNewTab": "فتح في علامة تبويب جديدة", - "noReleases": "لا توجد إصدارات", - "updatesAndReleases": "التحديثات والإصدارات", - "newVersionAvailable": "يتوفر إصدار جديد ({{version}}).", - "failedToFetchUpdateInfo": "فشل في جلب معلومات التحديث", - "preRelease": "الإصدار السابق", - "noReleasesFound": "لم يتم العثور على إصدارات", - "cancel": "إلغاء", - "username": "اسم المستخدم", - "login": "تسجيل الدخول", - "register": "تسجيل", - "password": "كلمة المرور", - "confirmPassword": "تأكيد كلمة المرور", - "back": "الرجوع", - "save": "حفظ", - "saving": "حفظ...", - "delete": "حذف", - "rename": "إعادة تسمية", - "edit": "تحرير", - "add": "إضافة", - "confirm": "تأكيد", - "no": "لا", - "or": "أو", - "next": "التالي", - "previous": "السابق", - "refresh": "تحديث", - "language": "اللغة", - "checking": "يتحقق...", - "checkingDatabase": "التحقق من اتصال قاعدة البيانات...", - "checkingAuthentication": "التحقق من المصادقة...", - "backendReconnected": "تم استعادة اتصال الخادم", - "connectionDegraded": "فقد اتصال الخادم، واسترداد…", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "إزالة", - "create": "إنشاء", - "update": "تحديث", - "copy": "نسخ", - "copyFailed": "فشل النسخ إلى الحافظة", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "إستعادة", - "of": "من" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "المنزل", - "terminal": "المحطة", - "docker": "دوكر", - "tunnels": "أنفاق", - "fileManager": "مدير الملفات", - "serverStats": "إحصائيات الخادم", - "admin": "المشرف", - "userProfile": "الملف الشخصي للمستخدم", - "splitScreen": "تقسيم الشاشة", - "confirmClose": "إغلاق هذه الجلسة النشطة؟", - "close": "أغلق", - "cancel": "إلغاء", - "sshManager": "مدير SSH", - "cannotSplitTab": "لا يمكن تقسيم هذا التبويب", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "نسخ كلمة المرور", - "copySudoPassword": "نسخ كلمة مرور سودو", - "passwordCopied": "تم نسخ كلمة المرور إلى الحافظة", - "noPasswordAvailable": "لا توجد كلمة مرور متاحة", - "failedToCopyPassword": "فشل في نسخ كلمة المرور", - "refreshTab": "تحديث الاتصال", - "openFileManager": "فتح مدير الملفات", - "dashboard": "لوحة التحكم", - "networkGraph": "رسم الشبكة", - "quickConnect": "الاتصال السريع", - "sshTools": "أدوات SSH", - "history": "التاريخ", - "hosts": "المضيفون", - "snippets": "كتل الكود", - "hostManager": "مدير المضيف", - "credentials": "وثائق التفويض", - "connections": "الاتصالات", - "roleAdministrator": "المدير", - "roleUser": "المستخدم" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "المضيفون", - "noHosts": "لا يوجد مضيفين SSH", - "retry": "إعادة المحاولة", - "refresh": "تحديث", - "optional": "اختياري", - "downloadSample": "تحميل عينة", - "failedToDeleteHost": "فشل في حذف {{name}}", - "importSkipExisting": "استيراد (تخطي قائم)", - "connectionDetails": "تفاصيل الاتصال", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "سطح المكتب عن بعد", - "port": "المنفذ", - "username": "اسم المستخدم", - "folder": "مجلد", - "tags": "الوسوم", - "pin": "تثبيت", - "addHost": "إضافة مضيف", - "editHost": "تحرير المضيف", - "cloneHost": "استنساخ المضيف", - "enableTerminal": "تمكين المحطة الطرفية", - "enableTunnel": "تمكين النفق", - "enableFileManager": "تمكين مدير الملفات", - "enableDocker": "تمكين Docker", - "defaultPath": "المسار الافتراضي", - "connection": "اتصال", - "upload": "تحميل", - "authentication": "المصادقة", - "password": "كلمة المرور", - "key": "المفتاح", - "credential": "بيانات", - "none": "لا", - "sshPrivateKey": "مفتاح SSH الخاص", - "keyType": "نوع المفتاح", - "uploadFile": "تحميل ملف", - "tabGeneral": "عام", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "أنفاق", - "tabDocker": "دوكر", - "tabFiles": "الملفات", - "tabStats": "إحصائيات", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "مشاركة", - "tabAuthentication": "المصادقة", - "terminal": "المحطة", - "tunnel": "نفق", - "fileManager": "مدير الملفات", - "serverStats": "إحصائيات الخادم", - "status": "الحالة", - "folderRenamed": "المجلد \"{{oldName}}\" أعيدت تسميته إلى \"{{newName}}\" بنجاح", - "failedToRenameFolder": "فشل في إعادة تسمية المجلد", - "movedToFolder": "نقل إلى \"{{folder}}\"", - "editHostTooltip": "تحرير المضيف", - "statusChecks": "التحقق من الحالة", - "metricsCollection": "مجموعة القياسات", - "metricsInterval": "الفاصل الزمني لمجموعة القياسات", - "metricsIntervalDesc": "كم عدد المرات لجمع إحصائيات الخادم (5s - 1ساعة)", - "behavior": "السلوك", - "themePreview": "معاينة السمة", - "theme": "السمة", - "fontFamily": "عائلة الخط", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "تباعد الحروف", - "lineHeight": "ارتفاع الخط", - "cursorStyle": "نمط المؤشر", - "cursorBlink": "وميض المؤشر", - "scrollbackBuffer": "المخزن المؤقت للتمرير", - "bellStyle": "نمط الجرس", - "rightClickSelectsWord": "النقر الأيمن يحدد الكلمة", - "fastScrollModifier": "تعديل التمرير السريع", - "fastScrollSensitivity": "حساسية التمرير السريع", - "sshAgentForwarding": "إعادة توجيه وكيل SSH", - "backspaceMode": "وضع الفضاء الخلفي", - "startupSnippet": "بدء تشغيل كتلة الكود", - "selectSnippet": "حدد كتلة الكود", - "forceKeyboardInteractive": "تفعيل لوحة المفاتيح", - "overrideCredentialUsername": "تجاوز اسم المستخدم", - "overrideCredentialUsernameDesc": "استخدام اسم المستخدم المحدد أعلاه بدلا من اسم المستخدم للاعتماد", - "jumpHostChain": "اقفز سلسلة المضيف", - "portKnocking": "منصة المنفذ", - "addKnock": "إضافة منفذ", - "addProxyNode": "إضافة عقدة", - "proxyNode": "عقدة الوكيل", - "proxyType": "نوع الوكيل", - "quickActions": "الإجراءات السريعة", - "sudoPasswordAutoFill": "كلمة مرور سودو تلقائية", - "sudoPassword": "كلمة مرور سودو", - "keepaliveInterval": "الفاصل الزمني للحفاظ على (مللي ثانية)", - "moshCommand": "قيادة MOSH", - "environmentVariables": "المتغيرات البيئية", - "addVariable": "إضافة متغير", - "docker": "دوكر", - "copyTerminalUrl": "نسخ رابط المحطة", - "copyFileManagerUrl": "نسخ رابط مدير الملفات", - "copyRemoteDesktopUrl": "نسخ رابط سطح المكتب عن بعد", - "failedToConnect": "فشل الاتصال بوحدة التحكم", - "connect": "الاتصال", - "disconnect": "قطع الاتصال", - "start": "ابدأ", - "enableStatusCheck": "تمكين التحقق من الحالة", - "enableMetrics": "تمكين القياسات", - "bulkUpdateFailed": "فشل التحديث الشامل", - "selectAll": "حدد الكل", - "deselectAll": "إلغاء تحديد الكل", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "قذيفة آمنة", - "virtualNetwork": "الشبكة الظاهرية", - "unencryptedShell": "قذيفة غير مشفرة", - "addressIp": "العنوان / IP", - "friendlyName": "الاسم الودي", - "folderAndAdvanced": "مجلد و متقدم", - "privateNotes": "ملاحظات خاصة", - "privateNotesPlaceholder": "تفاصيل حول هذا الخادم...", - "pinToTop": "تثبيت إلى الأعلى", - "pinToTopDesc": "إظهار هذا المضيف دائماً في الجزء العلوي من القائمة", - "portKnockingSequence": "تسلسل المنفذ", - "addKnockBtn": "إضافة قرطة", - "noPortKnocking": "لم يتم تكوين منفذ الضرب.", - "knockPort": "منفذ القفص", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "التأخير بعد (مللي ثانية)", - "useSocks5Proxy": "استخدام وكيل SOCKS5", - "useSocks5ProxyDesc": "مسار الاتصال من خلال خادم الوكيل", - "proxyHost": "مضيف الوكيل", - "proxyPort": "منفذ الوكيل", - "proxyUsername": "اسم مستخدم البروكسي", - "proxyPassword": "كلمة مرور الوكيل", - "proxySingleMode": "وكيل واحد", - "proxyChainMode": "سلسلة البروكسي", - "you": "أنت", - "jumpHostChainLabel": "اقفز سلسلة المضيف", - "addJumpBtn": "إضافة قفزة", - "noJumpHosts": "لم يتم تكوين مضيف قفز.", - "selectAServer": "اختر خادم...", - "sshPort": "منفذ SSH", - "authMethod": "طريقة المصادقة", - "storedCredential": "بيانات الاعتماد المخزنة", - "selectACredential": "حدد بيانات الاعتماد...", - "keyTypeLabel": "نوع المفتاح", - "keyTypeAuto": "الكشف التلقائي", - "keyPasteTab": "لصق", - "keyUploadTab": "تحميل", - "keyFileLoaded": "تم تحميل ملف المفتاح", - "keyUploadClick": "انقر لتحميل .pem / .key / .ppk", - "clearKey": "مسح المفتاح", - "keySaved": "تم حفظ مفتاح SSH", - "keyReplaceNotice": "لصق مفتاح جديد أدناه لاستبداله", - "keyPassphraseSaved": "تم حفظ كلمة المرور، اكتب للتغيير", - "replaceKey": "استبدال المفتاح", - "forceKeyboardInteractiveLabel": "فرض تفاعل لوحة المفاتيح", - "forceKeyboardInteractiveShortDesc": "فرض إدخال كلمة المرور يدوياً حتى إذا كانت المفاتيح موجودة", - "terminalAppearance": "مظهر المحطة النهائية", - "colorTheme": "لون السمة", - "fontFamilyLabel": "عائلة الخط", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "نمط المؤشر", - "letterSpacingPx": "تباعد الحروف (px)", - "lineHeightLabel": "ارتفاع الخط", - "bellStyleLabel": "نمط الجرس", - "backspaceModeLabel": "وضع الفضاء الخلفي", - "cursorBlinking": "ربط المؤشر", - "cursorBlinkingDesc": "تمكين الرسوم المتحركة الضوئية للمؤشر الطرفي", - "rightClickSelectsWordLabel": "اختيار الكلمة بالزر الأيمن", - "rightClickSelectsWordShortDesc": "حدد الكلمة تحت المؤشر عند النقر بالزر الأيمن", - "behaviorAndAdvanced": "السلوك والمتقدم", - "scrollbackBufferLabel": "المخزن المؤقت للتمرير", - "scrollbackMaxLines": "الحد الأقصى لعدد الخطوط المحفوظة في التاريخ", - "sshAgentForwardingLabel": "إعادة توجيه وكيل SSH", - "sshAgentForwardingShortDesc": "مرر مفاتيح SSH المحلية إلى هذا المضيف", - "enableAutoMosh": "تمكين الموسم التلقائي", - "enableAutoMoshDesc": "تفضيل موش على SSH إذا كان متوفراً", - "enableAutoTmux": "تمكين Tmux التلقائي", - "enableAutoTmuxDesc": "تشغيل تلقائي أو إرفاق جلسة tmux", - "sudoPasswordAutoFillLabel": "كلمة مرور سودو التلقائية", - "sudoPasswordAutoFillShortDesc": "توفير كلمة مرور sudo تلقائياً عند الطلب", - "sudoPasswordLabel": "كلمة مرور سودو", - "environmentVariablesLabel": "المتغيرات البيئية", - "addVariableBtn": "إضافة متغير", - "noEnvVars": "لا توجد متغيرات بيئية مكوّنة.", - "fastScrollModifierLabel": "تعديل التمرير السريع", - "fastScrollSensitivityLabel": "حساسية التمرير السريع", - "moshCommandLabel": "قيادة موش", - "startupSnippetLabel": "بدء تشغيل كتلة الكود", - "keepaliveIntervalLabel": "فترة الإبقاء على الاتصال (بالثواني)", - "maxKeepaliveMisses": "الحد الاقصى للاخطاء", - "tunnelSettings": "إعدادات النفق", - "enableTunneling": "تمكين الاتصال النفقي", - "enableTunnelingDesc": "تمكين وظيفة نفق SSH لهذا المضيف", - "serverTunnelsSection": "أنفاق الخادم", - "addTunnelBtn": "إضافة نفق", - "noTunnelsConfigured": "لم يتم تكوين الأنفاق.", - "tunnelLabel": "النفق {{number}}", - "tunnelType": "نوع النفق", - "tunnelModeLocalDesc": "إعادة توجيه منفذ محلي إلى منفذ على الخادم البعيد (أو يمكن الوصول إلى مضيف منه).", - "tunnelModeRemoteDesc": "إعادة توجيه المنفذ على الخادم البعيد إلى منفذ محلي على جهازك.", - "tunnelModeDynamicDesc": "إنشاء وكيل SOCKS5 على منفذ محلي لنقل الميناء الديناميكي.", - "sameHost": "هذا المضيف (نفق مباشر)", - "endpointHost": "مضيف نقطة النهاية", - "endpointPort": "منفذ نقطة النهاية", - "bindHost": "ربط المضيف", - "sourcePort": "منفذ المصدر", - "maxRetries": "الحد الاقصى لمحاولات", - "retryIntervalS": "الفاصل الزمني لإعادة المحاولة (ات)", - "autoStartLabel": "بدء التشغيل التلقائي", - "autoStartDesc": "توصيل هذا النفق تلقائياً عند تحميل المضيف", - "tunnelConnecting": "اتصال النفق...", - "tunnelDisconnected": "النفق غير متصل", - "failedToConnectTunnel": "فشل في الاتصال", - "failedToDisconnectTunnel": "فشل في قطع الاتصال", - "dockerIntegration": "تكامل Docker", - "enableDockerMonitor": "تمكين Docker", - "enableDockerMonitorDesc": "مراقبة وإدارة الحاويات على هذا المضيف عبر Docker", - "enableFileManagerMonitor": "تمكين مدير الملفات", - "enableFileManagerMonitorDesc": "تصفح وإدارة الملفات على هذا المضيف عبر SFTP", - "defaultPathLabel": "المسار الافتراضي", - "fileManagerPathHint": "الدليل المراد فتحه عند تشغيل مدير الملفات لهذا المضيف.", - "statusChecksLabel": "التحقق من الحالة", - "enableStatusChecks": "تمكين التحقق من الحالة", - "enableStatusChecksDesc": "ربط هذا المضيف بشكل دوري للتحقق من التوافر", - "useGlobalInterval": "استخدام الفاصل الزمني العالمي", - "useGlobalIntervalDesc": "تجاوز الفاصل الزمني للتحقق من الحالة على نطاق الخادم", - "checkIntervalS": "الفاصل الزمني (ات)", - "checkIntervalDesc": "ثواني بين كل اتصال بينغ", - "metricsCollectionLabel": "مجموعة القياسات", - "enableMetricsLabel": "تمكين القياسات", - "enableMetricsDesc": "اجمع المعالج، ذاكرة الوصول العشوائي، القرص واستخدام الشبكة من هذا المضيف", - "useGlobalMetrics": "استخدام الفاصل الزمني العالمي", - "useGlobalMetricsDesc": "تجاوز الفاصل الزمني للقياسات على نطاق الخادم", - "metricsIntervalS": "فاصل القياسات (ات)", - "metricsIntervalDesc2": "ثواني بين لقطات مترية", - "visibleWidgets": "أدوات مرئية", - "cpuUsageLabel": "استخدام المعالج", - "cpuUsageDesc": "المعالج بالمئة ، تحميل المتوسطات ، الرسم البياني للشرارة", - "memoryLabel": "استخدام الذاكرة", - "memoryDesc": "استخدام ذاكرة الوصول العشوائي، مقايضة، تخزين مؤقت", - "storageLabel": "استخدام القرص", - "storageDesc": "استخدام القرص لكل نقطة تركيب", - "networkLabel": "واجهة الشبكة", - "networkDesc": "قائمة الواجهة وعرض النطاق الترددي", - "uptimeLabel": "وقت التحديث", - "uptimeDesc": "النظام مرة اخرى و وقت التشغيل", - "systemInfoLabel": "معلومات النظام", - "systemInfoDesc": "نظام التشغيل، اسم المضيف، العمارة", - "recentLoginsLabel": "تسجيلات الدخول الأخيرة", - "recentLoginsDesc": "أحداث تسجيل الدخول الناجحة والفاشلة", - "topProcessesLabel": "أعلى العمليات", - "topProcessesDesc": "PID, CPU%, MEM%, الأمر", - "listeningPortsLabel": "منافذ الاستماع", - "listeningPortsDesc": "فتح المنافذ مع العملية والولاية", - "firewallLabel": "جدار الحماية", - "firewallDesc": "جدار ناري، AppArmor، حالة SELinux", - "quickActionsLabel": "الإجراءات السريعة", - "quickActionsToolbar": "الإجراءات السريعة تظهر كأزرار في شريط احصائيات الخادم لتنفيذ الأمر بنقرة واحدة.", - "noQuickActions": "لا توجد إجراءات سريعة حتى الآن.", - "buttonLabel": "تسمية الزر", - "selectSnippetPlaceholder": "حدد كتلة الكود...", - "addActionBtn": "إضافة إجراء", - "hostSharedSuccessfully": "تم مشاركة المضيف بنجاح", - "failedToShareHost": "فشل مشاركة المضيف", - "accessRevoked": "تم إلغاء الوصول", - "failedToRevokeAccess": "فشل في إلغاء الوصول", - "cancelBtn": "إلغاء", - "savingBtn": "حفظ...", - "addHostBtn": "إضافة مضيف", - "hostUpdated": "تم تحديث المضيف", - "hostCreated": "تم إنشاء المضيف", - "failedToSave": "فشل في حفظ المضيف", - "credentialUpdated": "تم تحديث بيانات الاعتماد", - "credentialCreated": "تم إنشاء بيانات الاعتماد", - "failedToSaveCredential": "فشل في حفظ بيانات الاعتماد", - "backToHosts": "العودة إلى المضيفين", - "backToCredentials": "العودة إلى وثائق التفويض", - "pinned": "مثبتة", - "noHostsFound": "لم يتم العثور على مضيف", - "tryDifferentTerm": "جرب مصطلحا مختلفا", - "addFirstHost": "أضف أول مضيف لك للبدء", - "noCredentialsFound": "لا توجد بيانات اعتماد", - "addCredentialBtn": "إضافة بيانات اعتماد", - "updateCredentialBtn": "تحديث بيانات الاعتماد", - "features": "الميزات", - "noFolder": "(بدون مجلد)", - "deleteSelected": "حذف", - "exitSelection": "الخروج من الاختيار", - "importSkip": "استيراد (تخطي قائم)", - "importOverwrite": "الاستيراد (الكتابة)", - "collapseBtn": "تصغير", - "importExportBtn": "استيراد / تصدير", - "hostStatusesRefreshed": "تم تحديث حالات المضيف", - "failedToRefreshHosts": "فشل تحديث المضيفين", - "movedHostTo": "نقل {{host}} إلى \"{{folder}}\"", - "failedToMoveHost": "فشل نقل المضيف", - "folderRenamedTo": "تمت إعادة تسمية المجلد إلى \"{{name}}\"", - "deletedFolder": "المجلد المحذوف\"{{name}}\"", - "failedToDeleteFolder": "فشل في حذف المجلد", - "deleteAllInFolder": "حذف جميع المضيفين في \"{{name}}\"؟ لا يمكن التراجع عن ذلك.", - "deletedHost": "محذوف {{name}}", - "copiedToClipboard": "نسخ إلى الحافظة", - "terminalUrlCopied": "تم نسخ الرابط الطرفي", - "fileManagerUrlCopied": "تم نسخ رابط مدير الملفات", - "tunnelUrlCopied": "تم نسخ رابط النفق", - "dockerUrlCopied": "تم نسخ رابط Docker", - "serverStatsUrlCopied": "تم نسخ رابط إحصائيات الخادم", - "rdpUrlCopied": "تم نسخ رابط RDP", - "vncUrlCopied": "تم نسخ رابط VNC", - "telnetUrlCopied": "تم نسخ رابط الهاتف", - "remoteDesktopUrlCopied": "تم نسخ رابط سطح المكتب عن بعد", - "expandActions": "توسيع الإجراءات", - "collapseActions": "إجراءات الانهيار", - "wakeOnLanAction": "تشغيل عبر الشبكة المحلية", - "wakeOnLanSuccess": "تم إرسال حزمة سحرية إلى {{name}}", - "wakeOnLanError": "فشل إرسال الحزمة السحرية", - "cloneHostAction": "استنساخ المضيف", - "copyAddress": "نسخ العنوان", - "copyLink": "نسخ الرابط", - "copyTerminalUrlAction": "نسخ رابط المحطة", - "copyFileManagerUrlAction": "نسخ رابط مدير الملفات", - "copyTunnelUrlAction": "نسخ رابط النفق", - "copyDockerUrlAction": "نسخ رابط Docker", - "copyServerStatsUrlAction": "نسخ رابط إحصائيات الخادم", - "copyRdpUrlAction": "نسخ رابط RDP", - "copyVncUrlAction": "نسخ رابط VNC", - "copyTelnetUrlAction": "نسخ رابط Telnet", - "copyRemoteDesktopUrlAction": "نسخ رابط سطح المكتب عن بعد", - "deleteCredentialConfirm": "حذف بيانات الاعتماد\"{{name}}\"؟", - "deletedCredential": "محذوف {{name}}", - "deploySSHKeyTitle": "نشر مفتاح SSH", - "deployingBtn": "النشر...", - "deployBtn": "نشر", - "failedToDeployKey": "فشل نشر المفتاح", - "deleteHostsConfirm": "حذف مضيف {{count}}{{plural}}؟ لا يمكن التراجع عن ذلك.", - "movedToRoot": "نقل إلى الجذر", - "failedToMoveHosts": "فشل في نقل المضيفين", - "enableTerminalFeature": "تمكين المحطة الطرفية", - "disableTerminalFeature": "تعطيل المحطة الطرفية", - "enableFilesFeature": "تمكين الملفات", - "disableFilesFeature": "تعطيل الملفات", - "enableTunnelsFeature": "تمكين الأنفاق", - "disableTunnelsFeature": "تعطيل الأنفاق", - "enableDockerFeature": "تمكين Docker", - "disableDockerFeature": "تعطيل المرفأ", - "addTagsPlaceholder": "إضافة علامات...", - "authDetails": "تفاصيل المصادقة", - "credType": "نوع", - "generateKeyPairDesc": "إنشاء زوج جديد من المفاتيح، الخاصة والعامة، سيتم ملؤها تلقائياً.", - "generatingKey": "توليد ...", - "generateLabel": "توليد {{label}}", - "uploadFileBtn": "تحميل ملف", - "keyPassphraseOptional": "كلمة المرور الرئيسية (اختياري)", - "sshPublicKeyOptional": "مفتاح SSH العمومي (اختياري)", - "publicKeyGenerated": "تم إنشاء المفتاح العمومي", - "failedToGeneratePublicKey": "فشل في اشتقاق المفتاح العمومي", - "publicKeyCopied": "تم نسخ المفتاح العمومي", - "keyPairGenerated": "تم إنشاء زوج المفتاح {{label}}", - "failedToGenerateKeyPair": "فشل في إنشاء زوج المفاتيح", - "searchHostsPlaceholder": "البحث عن المضيفين والعناوين والعلامات…", - "searchCredentialsPlaceholder": "البحث في بيانات الاعتماد…", - "refreshBtn": "تحديث", - "addTag": "إضافة علامات...", - "deleteConfirmBtn": "حذف", - "tunnelRequirementsText": "يجب أن يحتوي خادم SSH على منافذ بوابة نعم ، وسماحةTcpForarding نعم ، و PermitRootLogin عيّن في /etc/ssh/sshd_config.", - "deleteHostConfirm": "حذف \"{{name}}\"؟", - "enableAtLeastOneProtocol": "تمكين بروتوكول واحد على الأقل أعلاه لتهيئة المصادقة وإعدادات الاتصال.", - "keyPassphrase": "كلمة المرور الرئيسية", - "connectBtn": "الاتصال", - "disconnectBtn": "قطع الاتصال", - "basicInformation": "معلومات أساسية", - "authDetailsSection": "تفاصيل المصادقة", - "credTypeLabel": "نوع", - "hostsTab": "المضيفون", - "credentialsTab": "وثائق التفويض", - "selectMultiple": "حدد عدة", - "selectHosts": "حدد المضيفين", - "connectionLabel": "اتصال", - "authenticationLabel": "المصادقة", - "generateKeyPairTitle": "إنشاء اقتران المفتاح", - "generateKeyPairDescription": "إنشاء زوج جديد من المفاتيح، الخاصة والعامة، سيتم ملؤها تلقائياً.", - "generateFromPrivateKey": "توليد من المفتاح الخاص", - "refreshBtn2": "تحديث", - "exitSelectionTitle": "الخروج من الاختيار", - "exportAll": "تصدير الكل", - "addHostBtn2": "إضافة مضيف", - "addCredentialBtn2": "إضافة بيانات اعتماد", - "checkingHostStatuses": "التحقق من أوضاع المضيف...", - "pinnedSection": "مثبتة", - "hostsExported": "تم تصدير المضيفين بنجاح", - "exportFailed": "فشل تصدير المضيفين", - "sampleDownloaded": "تم تنزيل ملف العينة", - "failedToDeleteCredential2": "فشل في حذف بيانات الاعتماد", - "noFolderOption": "(بدون مجلد)", - "nSelected": "{{count}} تم الاختيار", - "featuresMenu": "الميزات", - "moveMenu": "نقل", - "cancelSelection": "إلغاء", - "deployDialogDesc": "نشر {{name}} في مفتاح_المضيف المصرح به.", - "targetHostLabel": "المضيف المستهدف", - "selectHostOption": "اختر مضيفاً...", - "keyDeployedSuccess": "نشر المفتاح بنجاح", - "failedToDeployKey2": "فشل نشر المفتاح", - "deletedCount": "مضيفين {{count}} محذوفين", - "failedToDeleteCount": "فشل في حذف مضيفين {{count}}", - "duplicatedHost": "مكررة \"{{name}}\"", - "failedToDuplicateHost": "فشل في تكرار المضيف", - "updatedCount": "تم تحديث مستضيفين {{count}}", - "friendlyNameLabel": "الاسم الودي", - "descriptionLabel": "الوصف", - "loadingHost": "جاري تحميل المضيف...", - "loadingHosts": "جاري تحميل المضيفين...", - "loadingCredentials": "جاري تحميل البيانات...", - "noHostsYet": "لا يوجد مضيفين بعد", - "noHostsMatchSearch": "لا يوجد مضيف يطابق بحثك", - "hostNotFound": "لم يتم العثور على المضيف", - "searchHosts": "البحث عن المضيفين...", - "sortHosts": "فرز المضيفين", - "sortDefault": "الترتيب الافتراضي", - "sortNameAsc": "الاسم (من الألف إلى الياء)", - "sortNameDesc": "الاسم (Z → A)", - "sortIpAsc": "عنوان IP (تصاعدي)", - "sortIpDesc": "عنوان IP (الوصف)", - "sortOnlineFirst": "عبر الإنترنت أولاً", - "sortOfflineFirst": "العمل دون اتصال بالإنترنت أولاً", - "sortPinnedFirst": "مثبت أولاً", - "filterHosts": "تصفية المضيفين", - "filterClearAll": "فلاتر شفافة", - "filterStatusGroup": "حالة", - "filterOnline": "متصل", - "filterOffline": "غير متصل بالإنترنت", - "filterPinned": "مثبت", - "filterAuthGroup": "نوع المصادقة", - "filterAuthPassword": "كلمة المرور", - "filterAuthKey": "مفتاح SSH", - "filterAuthCredential": "بيانات الاعتماد", - "filterAuthNone": "لا أحد", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "بروتوكول", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "تيلنت", - "filterFeaturesGroup": "سمات", - "filterFeatureTerminal": "صالة", - "filterFeatureFileManager": "مدير الملفات", - "filterFeatureTunnel": "نفق", - "filterFeatureDocker": "عامل ميناء", - "filterTagsGroup": "الوسوم", - "shareHost": "مشاركة المضيف", - "shareHostTitle": "مشاركة: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "يجب أن يستخدم هذا المضيف بيانات اعتماد لتمكين المشاركة. تحرير المضيف وتعيين بيانات الاعتماد أولاً." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "اتصال", - "authentication": "المصادقة", - "connectionSettings": "إعدادات الاتصال", - "displaySettings": "اعدادات العرض", - "audioSettings": "إعدادات الصوت", - "rdpPerformance": "أداء RDP", - "deviceRedirection": "إعادة توجيه الجهاز", - "session": "الجلسة", - "gateway": "البوابة", - "remoteApp": "التطبيق البعيد", - "clipboard": "الحافظة", - "sessionRecording": "تسجيل الجلسة", - "wakeOnLan": "إيقاظ-الشبكة المحلية", - "vncSettings": "إعدادات VNC", - "terminalSettings": "إعدادات المحطة الطرفية", - "rdpPort": "منفذ RDP", - "username": "اسم المستخدم", - "password": "كلمة المرور", - "domain": "النطاق", - "securityMode": "وضع الأمان", - "colorDepth": "عمق اللون", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "الارتفاع", - "dpi": "إدارة", - "resizeMethod": "طريقة تغيير الحجم", - "clientName": "اسم العميل", - "initialProgram": "البرنامج الأولي", - "serverLayout": "تخطيط الخادم", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "منفذ البوابة", - "gatewayUsername": "اسم مستخدم البوابة", - "gatewayPassword": "كلمة مرور البوابة", - "gatewayDomain": "نطاق البوابة", - "remoteAppProgram": "برنامج التطبيقات البعيدة", - "workingDirectory": "دليل العمل", - "arguments": "حجج", - "normalizeLineEndings": "تطبيع نهاية الخط", - "recordingPath": "مسار التسجيل", - "recordingName": "اسم التسجيل", - "macAddress": "عنوان MAC", - "broadcastAddress": "عنوان البث", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "وقت الانتظار (ات)", - "driveName": "اسم القرص", - "drivePath": "مسار القيادة", - "ignoreCertificate": "تجاهل الشهادة", - "ignoreCertificateDesc": "السماح بالاتصالات للمضيفين الذين لديهم شهادات توقيع ذاتي", - "forceLossless": "إجبار بلا خسارة", - "forceLosslessDesc": "فرض ترميز الصور الخاسرة (جودة أعلى، عرض النطاق الترددي)", - "disableAudio": "تعطيل الصوت", - "disableAudioDesc": "كتم كل الصوت من الجلسة البعيدة", - "enableAudioInput": "تمكين إدخال الصوت (الميكروفون)", - "enableAudioInputDesc": "إعادة توجيه الميكروفون المحلي إلى الجلسة البعيدة", - "wallpaper": "الخلفية", - "wallpaperDesc": "إظهار خلفية سطح المكتب (تعطيل تحسين الأداء)", - "theming": "القالب", - "themingDesc": "تمكين السمات والأنماط المرئية", - "fontSmoothing": "تمزيق الخط", - "fontSmoothingDesc": "تمكين عرض خط CleType", - "fullWindowDrag": "سحب النافذة الكاملة", - "fullWindowDragDesc": "إظهار محتويات النافذة أثناء السحب", - "desktopComposition": "تكوين سطح المكتب", - "desktopCompositionDesc": "تمكين تأثيرات الزجاج الهوائي", - "menuAnimations": "الرسوم المتحركة للقائمة", - "menuAnimationsDesc": "تمكين تلاشي القائمة والرسوم المتحركة للشريحة", - "disableBitmapCaching": "تعطيل التخزين المؤقت لخرائط Bitmap", - "disableBitmapCachingDesc": "إيقاف تشغيل ذاكرة التخزين المؤقت للخرائط (قد تساعد في اللقائق)", - "disableOffscreenCaching": "تعطيل التخزين المؤقت لإغلاق الشاشة", - "disableOffscreenCachingDesc": "إيقاف تشغيل ذاكرة التخزين المؤقت", - "disableGlyphCaching": "تعطيل التخزين المؤقت لـ Glyph", - "disableGlyphCachingDesc": "إيقاف تشغيل ذاكرة التخزين المؤقت", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "استخدام خط أنابيب الرسومات عن بعد", - "enablePrinting": "تمكين الطباعة", - "enablePrintingDesc": "إعادة توجيه الطابعات المحلية إلى الجلسة البعيدة", - "enableDriveRedirection": "تمكين إعادة توجيه القرص", - "enableDriveRedirectionDesc": "خريطة مجلد محلي كمحرك أقراص في الجلسة البعيدة", - "createDrivePath": "إنشاء مسار محرك الأقراص", - "createDrivePathDesc": "إنشاء المجلد تلقائياً إذا كان غير موجود", - "disableDownload": "تعطيل التحميل", - "disableDownloadDesc": "منع تحميل الملفات من الجلسة البعيدة", - "disableUpload": "تعطيل التحميل", - "disableUploadDesc": "منع تحميل الملفات إلى الجلسة البعيدة", - "enableTouch": "تمكين اللمس", - "enableTouchDesc": "تمكين إعادة توجيه الإدخال اللمس", - "consoleSession": "وحدة التحكم", - "consoleSessionDesc": "الاتصال بوحدة التحكم (الجلسة 0) بدلاً من جلسة جديدة", - "sendWolPacket": "إرسال حزمة WOL", - "sendWolPacketDesc": "إرسال حزمة سحرية لإيقاظ هذا المضيف قبل الاتصال", - "disableCopy": "تعطيل النسخ", - "disableCopyDesc": "منع نسخ النص من الجلسة البعيدة", - "disablePaste": "تعطيل اللصق", - "disablePasteDesc": "منع لصق النص في الجلسة البعيدة", - "createPathIfMissing": "إنشاء مسار إذا كان مفقودا", - "createPathIfMissingDesc": "إنشاء دليل التسجيل تلقائياً", - "excludeOutput": "استبعاد المخرجات", - "excludeOutputDesc": "عدم تسجيل إخراج الشاشة (بيانات التعريف فقط)", - "excludeMouse": "استبعاد الفأرة", - "excludeMouseDesc": "لا تسجل حركات الماوس", - "includeKeystrokes": "تضمين لوحة المفاتيح", - "includeKeystrokesDesc": "سجل المفاتيح الخام بالإضافة إلى إخراج الشاشة", - "vncPort": "منفذ VNC", - "vncPassword": "كلمة مرور VNC", - "vncUsernameOptional": "اسم المستخدم (اختياري)", - "vncLeaveBlank": "اتركه فارغاً إذا لم يكن مطلوباً", - "cursorMode": "وضع المؤشر", - "swapRedBlue": "مبادلة Red/Bأزرق", - "swapRedBlueDesc": "مبادلة قنوات اللون الأحمر والأزرق (إصلاح بعض مشاكل الألوان)", - "readOnly": "قراءة فقط", - "readOnlyDesc": "عرض الشاشة البعيدة دون إرسال أي إدخال", - "telnetPort": "منفذ شبكة الاتصال", - "terminalType": "نوع المحطة الطرفية", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "مخطط الألوان", - "backspaceKey": "مفتاح الخلفية", - "saveHostFirst": "حفظ المضيف أولاً.", - "sharingOptionsAfterSave": "خيارات المشاركة متاحة بعد أن يتم حفظ المضيف.", - "sharingLoadError": "فشل تحميل البيانات التشاركية. تحقق من اتصالك وحاول مرة أخرى.", - "shareHostSection": "مشاركة المضيف", - "shareWithUser": "مشاركة مع المستخدم", - "shareWithRole": "مشاركة مع الدور", - "selectUser": "حدد المستخدم", - "selectRole": "اختيار دور", - "selectUserOption": "اختر مستخدم...", - "selectRoleOption": "اختر دور...", - "permissionLevel": "مستوى الإذن", - "expiresInHours": "تنتهي في (ساعات)", - "noExpiryPlaceholder": "اتركه فارغاً لعدم انتهاء الصلاحية", - "shareBtn": "مشاركة", - "currentAccess": "الوصول الحالي", - "typeHeader": "نوع", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "الصلاحية", - "grantedByHeader": "ممنوح من قبل", - "expiresHeader": "تنتهي", - "noAccessEntries": "لا توجد إدخالات الدخول حتى الآن.", - "expiredLabel": "منتهية", - "neverLabel": "لا", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "إلغاء", - "savingBtn": "حفظ...", - "updateHostBtn": "تحديث المضيف", - "addHostBtn": "إضافة مضيف" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "البحث عن المضيفين، الأوامر، أو الإعدادات...", - "quickActions": "الإجراءات السريعة", - "hostManager": "مدير المضيف", - "hostManagerDesc": "إدارة أو إضافة أو تحرير المضيفين", - "addNewHost": "إضافة مضيف جديد", - "addNewHostDesc": "تسجيل مضيف جديد", - "adminSettings": "إعدادات المدير", - "adminSettingsDesc": "تهيئة تفضيلات النظام والمستخدمين", - "userProfile": "الملف الشخصي للمستخدم", - "userProfileDesc": "إدارة الحساب والتفضيلات الخاصة بك", - "addCredential": "إضافة بيانات اعتماد", - "addCredentialDesc": "تخزين مفاتيح SSH أو كلمات المرور", - "recentActivity": "النشاط الأخير", - "serversAndHosts": "الخوادم والمستضيفين", - "noHostsFound": "لم يتم العثور على مضيفين مطابقين \"{{search}}\"", - "links": "الروابط", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "حدد", - "toggleWith": "تبديل مع" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "لم يتم تعيين علامة تبويب", - "focusedPane": "اللوحة النشطة" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "لا توجد اتصالات", - "noConnectionsDesc": "افتح نافذة طرفية أو مدير ملفات أو سطح مكتب بعيد لعرض الاتصالات هنا", - "connectedFor": "متصل لمدة {{duration}}", - "connected": "متصل", - "disconnected": "غير متصل", - "closeTab": "إغلاق علامة التبويب", - "closeConnection": "اتصال وثيق", - "forgetTab": "ينسى", - "removeBackground": "يزيل", - "reconnect": "إعادة الاتصال", - "reopenTab": "إعادة فتح", - "sectionOpen": "يفتح", - "sectionBackground": "خلفية", - "backgroundDesc": "تستمر الجلسات لمدة 30 دقيقة بعد قطع الاتصال ويمكن إعادة الاتصال بها.", - "persisted": "استمر في الخلفية", - "expiresIn": "تنتهي صلاحيتها في {{duration}}", - "search": "ابحث عن الاتصالات...", - "noSearchResults": "لا توجد نتائج مطابقة لبحثك" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "جاري الاتصال بجلسة {{type}}...", - "connectionError": "خطأ في الاتصال", - "connectionFailed": "فشل الاتصال", - "failedToConnect": "فشل الحصول على رمز الاتصال", - "hostNotFound": "لم يتم العثور على المضيف", - "noHostSelected": "لم يتم اختيار مضيف", - "reconnect": "إعادة الاتصال", - "retry": "إعادة المحاولة", - "guacdUnavailable": "خدمة سطح المكتب البعيد (guacd) غير متوفرة. الرجاء التأكد من تشغيل guacd وإمكانية الوصول إليه وتكوين بشكل صحيح في إعدادات المسؤول.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "الفوز +L (شاشة القفل)", - "winKey": "مفتاح Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "تبديل", - "win": "فوز", - "stickyActive": "{{key}} (مزج - انقر للإصدار)", - "stickyInactive": "{{key}} (انقر إلى لاتش)", - "esc": "الهروب", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "المنزل", - "end": "نهاية", - "pageUp": "الصفحة لأعلى", - "pageDown": "أسفل الصفحة", - "arrowUp": "السهم للأعلى", - "arrowDown": "السهم للأسفل", - "arrowLeft": "السهم لليسار", - "arrowRight": "سهم يمين", - "fnToggle": "مفاتيح الدوال", - "reconnect": "جلسة إعادة الاتصال", - "collapse": "طي شريط الأدوات", - "expand": "توسيع شريط الأدوات", - "dragHandle": "اسحب لتغيير الموضع" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "الاتصال بالمضيف", - "clear": "مسح", - "paste": "لصق", - "reconnect": "إعادة الاتصال", - "connectionLost": "تم فقدان الاتصال", - "connected": "متصل", - "clipboardWriteFailed": "فشل النسخ إلى الحافظة. تأكد من خدمة الصفحة عبر HTTPS أو localhost.", - "clipboardReadFailed": "فشل في القراءة من الحافظة. تأكد من منح أذونات الحافظة.", - "clipboardHttpWarning": "لصق يتطلب HTTPS. استخدم Ctrl+Shift+V أو خدمة Termix عبر HTTPS.", - "unknownError": "حدث خطأ غير معروف", - "websocketError": "خطأ في اتصال WebSocket", - "connecting": "جاري الاتصال...", - "noHostSelected": "لم يتم اختيار مضيف", - "reconnecting": "إعادة الاتصال... ({{attempt}}/{{max}})", - "reconnected": "تمت إعادة الاتصال بنجاح", - "tmuxSessionCreated": "تم إنشاء جلسة tmux: {{name}}", - "tmuxSessionAttached": "جلسة tmux المرفقة: {{name}}", - "tmuxUnavailable": "لم يتم تثبيت tmux على المضيف البعيد، العودة إلى قذيفة عادية", - "tmuxSessionPickerTitle": "جلسات tmux", - "tmuxSessionPickerDesc": "جلسات tmux الموجودة على هذا المضيف. حدد واحدة لإعادة إرفاق أو إنشاء جلسة جديدة.", - "tmuxWindows": "ويندوز", - "tmuxWindowCount": "{{count}} نافذة", - "tmuxAttached": "العملاء الملحقون", - "tmuxAttachedCount": "مرفق {{count}}", - "tmuxLastActivity": "آخر نشاط", - "tmuxTimeJustNow": "الآن فقط", - "tmuxTimeMinutes": "{{count}}منذ شهر", - "tmuxTimeHours": "{{count}}منذ ساعة", - "tmuxTimeDays": "{{count}}منذ يوم", - "tmuxCreateNew": "بدء جلسة جديدة", - "tmuxCopyHint": "ضبط التحديد واضغط على Enter للنسخ إلى الحافظة", - "tmuxDetach": "فصل من جلسة tmux", - "tmuxDetached": "منفصلة عن جلسة tmux", - "maxReconnectAttemptsReached": "تم بلوغ الحد الأقصى لمحاولات إعادة الاتصال", - "closeTab": "أغلق", - "connectionTimeout": "مهلة الاتصال", - "terminalTitle": "المحطة الطرفية - {{host}}", - "terminalWithPath": "المحطة الطرفية - {{host}}:{{path}}", - "runTitle": "تشغيل {{command}} - {{host}}", - "totpRequired": "المصادقة الثنائية مطلوبة", - "totpCodeLabel": "رمز التحقق", - "totpVerify": "تحقق", - "warpgateAuthRequired": "مطلوب مصادقة التحذير", - "warpgateSecurityKey": "مفتاح الأمان", - "warpgateAuthUrl": "رابط المصادقة", - "warpgateOpenBrowser": "فتح في المتصفح", - "warpgateContinue": "لقد أكملت المصادقة", - "opksshAuthRequired": "المصادقة مفتوحة مطلوبة", - "opksshAuthDescription": "أكمل المصادقة في المتصفح الخاص بك للمتابعة. ستبقى هذه الجلسة صالحة لمدة 24 ساعة.", - "opksshOpenBrowser": "فتح المتصفح للمصادقة", - "opksshWaitingForAuth": "في انتظار المصادقة في المتصفح...", - "opksshAuthenticating": "جاري معالجة المصادقة...", - "opksshTimeout": "انتهت مهلة المصادقة. الرجاء المحاولة مرة أخرى.", - "opksshAuthFailed": "فشلت المصادقة. الرجاء التحقق من بيانات الاعتماد الخاصة بك وحاول مرة أخرى.", - "opksshSignInWith": "تسجيل الدخول باستخدام {{provider}}", - "sudoPasswordPopupTitle": "إدراج كلمة المرور؟", - "websocketAbnormalClose": "تم إغلاق الاتصال بشكل غير متوقع. قد يكون ذلك بسبب مشكلة اعدادات الوكيل العكسي أو SSL. الرجاء التحقق من سجلات الخادم.", - "connectionLogTitle": "سجل الاتصال", - "connectionLogCopy": "نسخ السجلات إلى الحافظة", - "connectionLogEmpty": "لا توجد سجلات اتصال بعد", - "connectionLogWaiting": "في انتظار سجلات الاتصال...", - "connectionLogCopied": "سجلات الاتصال نسخت إلى الحافظة", - "connectionLogCopyFailed": "فشل في نسخ السجلات إلى الحافظة", - "connectionRejected": "تم رفض الاتصال بواسطة الخادم. الرجاء التحقق من المصادقة الخاصة بك وتكوين الشبكة.", - "hostKeyRejected": "تم رفض التحقق من مفتاح المضيف SSH. تم إلغاء الاتصال.", - "sessionTakenOver": "تم فتح الجلسة في علامة تبويب أخرى. إعادة الاتصال..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "لم يتم اختيار مضيف", - "initializingEditor": "تهيئة المحرر...", - "file": "ملف", - "folder": "مجلد", - "uploadFile": "تحميل ملف", - "downloadFile": "تنزيل", - "extractArchive": "استخراج الأرشيف", - "extractingArchive": "استخراج {{name}}...", - "archiveExtractedSuccessfully": "تم استخراج {{name}} بنجاح", - "extractFailed": "فشل الاستخراج", - "compressFile": "ضغط الملف", - "compressFiles": "ضغط الملفات", - "compressFilesDesc": "ضغط العناصر {{count}} إلى أرشيف", - "archiveName": "اسم الأرشيف", - "enterArchiveName": "أدخل اسم الأرشيف...", - "compressionFormat": "تنسيق الضغط", - "selectedFiles": "الملفات المحددة", - "andMoreFiles": "والمزيد {{count}}...", - "compress": "ضغط", - "compressingFiles": "ضغط العناصر {{count}} إلى {{name}}...", - "filesCompressedSuccessfully": "تم إنشاء {{name}} بنجاح", - "compressFailed": "فشل الضغط", - "edit": "تحرير", - "preview": "معاينة", - "previous": "السابق", - "next": "التالي", - "pageXOfY": "الصفحة {{current}} من {{total}}", - "zoomOut": "تكبير خارج", - "zoomIn": "تكبير في", - "newFile": "ملف جديد", - "newFolder": "مجلد جديد", - "rename": "إعادة تسمية", - "uploading": "تحميل...", - "uploadingFile": "تحميل {{name}}...", - "fileName": "اسم الملف", - "folderName": "اسم المجلد", - "fileUploadedSuccessfully": "الملف \"{{name}}تم تحميله بنجاح", - "failedToUploadFile": "فشل في تحميل الملف", - "fileDownloadedSuccessfully": "الملف \"{{name}}تم تنزيله بنجاح", - "failedToDownloadFile": "فشل تنزيل الملف", - "fileCreatedSuccessfully": "تم إنشاء الملف \"{{name}}بنجاح", - "folderCreatedSuccessfully": "المجلد \"{{name}}\" تم إنشاؤه بنجاح", - "failedToCreateItem": "فشل إنشاء العنصر", - "operationFailed": "فشلت العملية {{operation}} ل {{name}}: {{error}}", - "failedToResolveSymlink": "فشل حل الارتباط الرمزي", - "itemsDeletedSuccessfully": "تم حذف {{count}} عنصرًا بنجاح", - "failedToDeleteItems": "فشل حذف العناصر", - "sudoPasswordRequired": "كلمة مرور المسؤول مطلوبة", - "enterSudoPassword": "أدخل كلمة مرور sudo لمتابعة هذه العملية", - "sudoPassword": "كلمة المرور", - "sudoOperationFailed": "فشلت عملية سودو", - "sudoAuthFailed": "فشل مصادقة سودو", - "dragFilesToUpload": "إسقاط الملفات هنا لتحميل", - "emptyFolder": "هذا المجلد فارغ", - "searchFiles": "البحث في الملفات...", - "upload": "تحميل", - "selectHostToStart": "حدد المضيف لبدء إدارة الملفات", - "sshRequiredForFileManager": "مدير الملفات يتطلب SSH. هذا المضيف ليس لديه SSH مفعل.", - "failedToConnect": "فشل الاتصال بSSH", - "failedToLoadDirectory": "فشل تحميل الدليل", - "noSSHConnection": "لا يوجد اتصال SSH متوفر", - "copy": "نسخ", - "cut": "قطع", - "paste": "لصق", - "copyPath": "نسخ المسار", - "copyPaths": "نسخ المسارات", - "delete": "حذف", - "properties": "الخصائص", - "refresh": "تحديث", - "downloadFiles": "تحميل ملفات {{count}} إلى المتصفح", - "copyFiles": "نسخ العناصر {{count}}", - "cutFiles": "قطع العناصر {{count}}", - "deleteFiles": "حذف العناصر {{count}}", - "filesCopiedToClipboard": "تم نسخ العناصر {{count}} إلى الحافظة", - "filesCutToClipboard": "{{count}} عناصر قُطعت على الحافظة", - "pathCopiedToClipboard": "تم نسخ المسار إلى الحافظة", - "pathsCopiedToClipboard": "{{count}} تم نسخ المسارات إلى الحافظة", - "failedToCopyPath": "فشل نسخ المسار إلى الحافظة", - "movedItems": "نقل عناصر {{count}}", - "failedToDeleteItem": "فشل في حذف العنصر", - "itemRenamedSuccessfully": "تم تغيير اسم {{type}} بنجاح", - "failedToRenameItem": "فشل في إعادة تسمية العنصر", - "download": "تنزيل", - "permissions": "الأذونات", - "size": "الحجم", - "modified": "معدّل", - "path": "المسار", - "confirmDelete": "هل أنت متأكد من أنك تريد حذف {{name}}؟", - "permissionDenied": "تم رفض الإذن", - "serverError": "خطأ في الخادم", - "fileSavedSuccessfully": "تم حفظ الملف بنجاح", - "failedToSaveFile": "فشل في حفظ الملف", - "confirmDeleteSingleItem": "هل أنت متأكد من أنك تريد حذف \"{{name}}\" بشكل دائم؟", - "confirmDeleteMultipleItems": "هل أنت متأكد من أنك تريد حذف العناصر {{count}} نهائياً؟", - "confirmDeleteMultipleItemsWithFolders": "هل أنت متأكد من أنك تريد حذف العناصر {{count}} نهائيًا؟ هذا يشمل المجلدات ومحتوياتها.", - "confirmDeleteFolder": "هل أنت متأكد من أنك تريد حذف المجلد \"{{name}}\" و جميع محتوياته؟", - "permanentDeleteWarning": "لا يمكن التراجع عن هذا الإجراء. سيتم حذف العنصر (العناصر) بشكل دائم من الخادم.", - "recent": "حديثاً", - "pinned": "مثبتة", - "folderShortcuts": "اختصارات المجلد", - "failedToReconnectSSH": "فشل في إعادة الاتصال بجلسة SSH", - "openTerminalHere": "فتح المحطة الطرفية هنا", - "run": "تشغيل", - "openTerminalInFolder": "فتح المحطة الطرفية في هذا المجلد", - "openTerminalInFileLocation": "فتح المحطة الطرفية في موقع الملف", - "runningFile": "قيد التشغيل - {{file}}", - "onlyRunExecutableFiles": "يمكن فقط تشغيل الملفات القابلة للتنفيذ", - "directories": "المجلدات", - "removedFromRecentFiles": "إزالة \"{{name}}\" من الملفات الأخيرة", - "removeFailed": "فشل الإزالة", - "unpinnedSuccessfully": "تم إلغاء تثبيت \"{{name}}بنجاح", - "unpinFailed": "فشل إلغاء التثبيت", - "removedShortcut": "إزالة الاختصار\"{{name}}\"", - "removeShortcutFailed": "فشل إزالة الاختصار", - "clearedAllRecentFiles": "مسح جميع الملفات الأخيرة", - "clearFailed": "فشل المسح", - "removeFromRecentFiles": "إزالة من الملفات الأخيرة", - "clearAllRecentFiles": "مسح جميع الملفات الأخيرة", - "unpinFile": "إلغاء تثبيت الملف", - "removeShortcut": "إزالة الاختصار", - "pinFile": "تثبيت ملف", - "addToShortcuts": "إضافة إلى الاختصارات", - "pasteFailed": "فشل اللصق", - "noUndoableActions": "لا إجراءات غير قابلة للإلغاء", - "undoCopySuccess": "عملية نسخ غير منسوخة: الملفات المحذوفة {{count}} منسوخة", - "undoCopyFailedDelete": "فشل التراجع: لا يمكن حذف أي ملفات تم نسخها", - "undoCopyFailedNoInfo": "فشل التراجع: لم يتم العثور على معلومات الملفات المنسوخة", - "undoMoveSuccess": "إلغاء تشغيل النقل: نقل الملفات {{count}} إلى الموقع الأصلي", - "undoMoveFailedMove": "فشل التراجع: لا يمكن نقل أي ملفات إلى الوراء", - "undoMoveFailedNoInfo": "فشل التراجع: تعذر العثور على معلومات الملف المنقولة", - "undoDeleteNotSupported": "لا يمكن التراجع عن عملية الحذف: تم حذف الملفات نهائيا من الخادم", - "undoTypeNotSupported": "نوع عملية التراجع غير مدعوم", - "undoOperationFailed": "فشلت عملية التراجع", - "unknownError": "خطأ غير معروف", - "confirm": "تأكيد", - "find": "العثور على...", - "replace": "استبدل", - "downloadInstead": "تحميل بدلاً من ذلك", - "keyboardShortcuts": "اختصارات لوحة المفاتيح", - "searchAndReplace": "البحث والاستبدال", - "editing": "تحرير", - "search": "البحث", - "findNext": "البحث عن التالي", - "findPrevious": "البحث عن السابق", - "save": "حفظ", - "selectAll": "حدد الكل", - "undo": "التراجع", - "redo": "إعادة", - "moveLineUp": "تحريك الخط للأعلى", - "moveLineDown": "تحريك الخط للأسفل", - "toggleComment": "تبديل التعليق", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "فشل في تحميل الصورة", - "startTyping": "ابدأ الكتابة...", - "unknownSize": "حجم غير معروف", - "fileIsEmpty": "الملف فارغ", - "largeFileWarning": "تحذير كبير من الملف", - "largeFileWarningDesc": "هذا الملف هو {{size}} في الحجم، مما قد يسبب مشاكل في الأداء عند فتحه كنص.", - "fileNotFoundAndRemoved": "لم يتم العثور على الملف \"{{name}}\" وتم إزالته من الملفات الأخيرة/المثبتة", - "failedToLoadFile": "فشل تحميل الملف: {{error}}", - "serverErrorOccurred": "حدث خطأ في الخادم. الرجاء المحاولة مرة أخرى لاحقاً.", - "autoSaveFailed": "فشل الحفظ التلقائي", - "fileAutoSaved": "حفظ الملف تلقائياً", - "moveFileFailed": "فشل في نقل {{name}}", - "moveOperationFailed": "فشلت عملية النقل", - "canOnlyCompareFiles": "يمكن فقط مقارنة ملفين", - "comparingFiles": "مقارنة الملفات: {{file1}} و {{file2}}", - "dragFailed": "فشلت عملية السحب", - "filePinnedSuccessfully": "الملف \"{{name}}تم تثبيته بنجاح", - "pinFileFailed": "فشل في تثبيت الملف", - "fileUnpinnedSuccessfully": "تم إلغاء تثبيت الملف \"{{name}}بنجاح", - "unpinFileFailed": "فشل إلغاء تثبيت الملف", - "shortcutAddedSuccessfully": "اختصار المجلد\"{{name}}أضيف بنجاح", - "addShortcutFailed": "فشل في إضافة اختصار", - "operationCompletedSuccessfully": "{{operation}} {{count}} عناصر بنجاح", - "operationCompleted": "{{operation}} {{count}} عنصر", - "downloadFileSuccess": "تم تنزيل الملف {{name}} بنجاح", - "downloadFileFailed": "فشل التحميل", - "moveTo": "الانتقال إلى {{name}}", - "diffCompareWith": "الفرق بين {{name}}", - "dragOutsideToDownload": "اسحب إلى خارج النافذة للتحميل ( ملفات{{count}})", - "newFolderDefault": "مجلد", - "newFileDefault": "Newfile.txt", - "successfullyMovedItems": "تم نقل العناصر {{count}} بنجاح إلى {{target}}", - "move": "نقل", - "searchInFile": "البحث في الملف (Ctrl+F)", - "showKeyboardShortcuts": "إظهار اختصارات لوحة المفاتيح", - "startWritingMarkdown": "بدء كتابة محتوى markdown الخاص بك...", - "loadingFileComparison": "جارٍ تحميل مقارنة الملفات...", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "قارن", - "sideBySide": "جانب جانبي", - "inline": "مضمن", - "fileComparison": "مقارنة الملفات: {{file1}} مقابل {{file2}}", - "fileTooLarge": "الملف كبير جدا: {{error}}", - "sshConnectionFailed": "فشل الاتصال بSSH. الرجاء التحقق من اتصالك بـ {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "فشل تحميل الملف: {{error}}", - "connecting": "جاري الاتصال...", - "connectedSuccessfully": "تم الاتصال بنجاح", - "totpVerificationFailed": "فشل التحقق من TOTP", - "warpgateVerificationFailed": "فشلت مصادقة الWarpgate", - "authenticationFailed": "فشل المصادقة", - "verificationCodePrompt": "رمز التحقق:", - "changePermissions": "تغيير الأذونات", - "currentPermissions": "الأذونات الحالية", - "owner": "المالك", - "group": "مجموعة", - "others": "أخرى", - "read": "قراءة", - "write": "كتابة", - "execute": "تنفيذ", - "permissionsChangedSuccessfully": "تم تغيير الأذونات بنجاح", - "failedToChangePermissions": "فشل في تغيير الأذونات", - "name": "الاسم", - "sortByName": "الاسم", - "sortByDate": "تاريخ التعديل", - "sortBySize": "الحجم", - "ascending": "تصاعدي", - "descending": "تنازلي", - "root": "جذر", - "new": "جديد", - "sortBy": "الترتيب حسب", - "items": "بنود", - "selected": "محدد", - "editor": "محرر", - "octal": "اغسطس", - "storage": "التخزين", - "disk": "قرص", - "used": "مستخدم", - "of": "من", - "toggleSidebar": "تبديل الشريط الجانبي", - "cannotLoadPdf": "لا يمكن تحميل PDF", - "pdfLoadError": "حدث خطأ أثناء تحميل ملف PDF هذا.", - "loadingPdf": "تحميل PDF...", - "loadingPage": "تحميل الصفحة..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "انسخ إلى المضيف…", - "moveToHost": "انتقل إلى المضيف…", - "copyItemsToHost": "انسخ {{count}} عنصرًا إلى المضيف…", - "moveItemsToHost": "انقل {{count}} عنصرًا إلى المضيف…", - "noHostsConnected": "لا توجد مضيفات أخرى لإدارة الملفات متاحة.", - "noHostsConnectedHint": "أضف مضيف SSH آخر مع تمكين مدير الملفات في مدير المضيف.", - "selectDestinationHost": "حدد المضيف الوجهة", - "destinationPath": "مسار الوجهة", - "recentDestinations": "الوجهات السياحية الحديثة", - "collapseRecentDestinations": "طي الوجهات الأخيرة", - "expandRecentDestinations": "توسيع الوجهات الأخيرة", - "browseFolders": "تصفح مجلدات الوجهة", - "browseDestination": "تصفح أو أدخل المسار", - "confirmCopy": "ينسخ", - "confirmMove": "يتحرك", - "transferring": "نقل…", - "compressing": "ضغط…", - "extracting": "استخراج…", - "transferringItems": "نقل {{current}} من {{total}} عنصر…", - "transferSuccess": "اكتملت عملية النقل", - "transferError": "فشلت عملية النقل", - "transferPartial": "اكتمل النقل بدون أخطاء {{count}}", - "transferPartialHint": "تعذر التحويل: {{paths}}", - "itemsSummary": "{{count}} عناصر", - "destMustBeDirectory": "يجب أن تكون الوجهة دليلاً لعمليات نقل العناصر المتعددة", - "selectThisFolder": "حدد هذا المجلد", - "browsePathWillBeCreated": "هذا المجلد غير موجود بعد. سيتم إنشاؤه عند بدء عملية النقل.", - "browsePathError": "تعذر فتح هذا المسار على المضيف الوجهة.", - "goUp": "اصعد للأعلى", - "copyFolderToHost": "انسخ المجلد إلى المضيف…", - "moveFolderToHost": "انقل المجلد إلى المضيف…", - "hostReady": "مستعد", - "hostConnecting": "جارٍ الاتصال…", - "hostDisconnected": "غير متصل", - "hostAuthRequired": "يلزم المصادقة — افتح مدير الملفات على هذا المضيف أولاً", - "hostConnectionFailed": "فشل الاتصال", - "metricsTitle": "مواعيد الانتقال", - "metricsPrepare": "تجهيز الوجهة: {{duration}}", - "metricsCompress": "الضغط على المصدر: {{duration}}", - "metricsHopSourceRead": "المصدر ← الخادم: {{throughput}}", - "metricsHopDestSftpWrite": "الخادم → الوجهة (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "الخادم → الوجهة (محلي): {{throughput}}", - "metricsTransfer": "من البداية إلى النهاية: {{throughput}} ({{duration}})", - "metricsExtract": "استخراج البيانات في الوجهة: {{duration}}", - "metricsSourceDelete": "إزالة من المصدر: {{duration}}", - "metricsTotal": "المجموع: {{duration}}", - "progressCompressing": "الضغط على المضيف المصدر…", - "progressExtracting": "جارٍ الاستخراج في الوجهة…", - "progressTransferring": "نقل البيانات…", - "progressReconnecting": "إعادة الاتصال…", - "parallelSegmentsLabel": "مسارات نقل متوازية", - "parallelSegmentsOption": "{{count}} ممرات", - "parallelSegmentsHint": "يتم تقسيم الملفات الكبيرة إلى أجزاء بحجم 256 ميجابايت. تستخدم المسارات المتعددة اتصالات منفصلة (مثل بدء عمليات نقل متعددة) لزيادة الإنتاجية الإجمالية.", - "progressTotalSpeed": "{{speed}} إجمالي ({{lanes}} مسارات)", - "progressTransferringItems": "نقل الملفات ({{current}} من {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} ملفات", - "sourceNotDeletedPartial": "تم الاحتفاظ بالملفات المصدرية (نقل جزئي)", - "jumpHostLimitation": "يجب أن يكون كلا المضيفين قابلين للوصول من خادم Termix. لا يتم دعم التوجيه المباشر بين المضيفين.", - "cancel": "يلغي", - "methodLabel": "طريقة النقل", - "methodAuto": "آلي", - "methodTar": "أرشيف تار", - "methodItemSftp": "بروتوكول نقل الملفات الآمن (SFTP) لكل ملف", - "methodAutoHint": "يختار بروتوكول tar أو SFTP لكل ملف بناءً على عدد الملفات وحجمها وقابليتها للضغط. أما الملفات المفردة فتستخدم دائمًا بروتوكول SFTP المتدفق.", - "methodTarHint": "قم بضغط الملفات في المصدر، وانقل ملفًا واحدًا، ثم استخرجها في الوجهة. يتطلب ذلك استخدام برنامج tar على كلا جهازي Unix.", - "methodItemSftpHint": "انقل كل ملف على حدة عبر بروتوكول نقل الملفات الآمن (SFTP). يعمل على جميع الأجهزة بما في ذلك نظام التشغيل ويندوز.", - "methodPreviewLoading": "طريقة حساب التحويل…", - "methodPreviewError": "تعذر معاينة طريقة النقل. سيختار الخادم طريقة النقل عند بدء العملية.", - "methodPreviewWillUseTar": "سيتم استخدام: أرشيف Tar", - "methodPreviewWillUseItemSftp": "سيتم استخدام: بروتوكول نقل الملفات الآمن (SFTP) لكل ملف", - "methodPreviewScanSummary": "{{fileCount}} ملف، {{totalSize}} إجمالي (تم فحصه على المضيف المصدر).", - "methodItemSftpLimitation": "يستخدم كل ملف نفس قناة SFTP المستخدمة في نسخ الملفات الفردية، واحداً تلو الآخر. يتم تجميع التقدم عبر جميع الملفات، لذا يتحرك الشريط ببطء أثناء نقل الملفات الكبيرة.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "لقد اخترت بروتوكول نقل الملفات الآمن (SFTP) لكل ملف على حدة.", - "user_tar": "لقد اخترت ملف tar.", - "tar_unavailable": "برنامج Tar غير متوفر على أحد المضيفين أو كليهما - سيتم استخدام SFTP لكل ملف بدلاً من ذلك.", - "windows_host": "يتم استخدام مضيف يعمل بنظام ويندوز - لا يتم استخدام برنامج tar.", - "auto_multi_large": "تلقائي: ملفات متعددة بما في ذلك ملف كبير ({{largestSize}}) مع بيانات قابلة للضغط — حزم tar في عملية نقل واحدة.", - "auto_single_large_in_archive": "تلقائي: ملف واحد كبير ({{largestSize}}) في هذه المجموعة — SFTP لكل ملف.", - "auto_many_incompressible": "تلقائي: بيانات غير قابلة للضغط في الغالب - بروتوكول نقل الملفات الآمن (SFTP) لكل ملف.", - "auto_many_files": "تلقائي: العديد من الملفات ({{fileCount}}) — يقلل tar من الحمل الزائد لكل ملف.", - "auto_default": "تلقائي: بروتوكول نقل الملفات الآمن (SFTP) لكل ملف لهذه المجموعة." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "يلغي", - "progressCancelling": "إلغاء…", - "progressStalled": "متوقف", - "resumedHint": "تمت إعادة الاتصال بعملية نقل بيانات نشطة بدأت في نافذة أخرى.", - "transferCancelled": "تم إلغاء التحويل", - "transferCancelledCopyHint": "قد تبقى بعض الملفات الجزئية في الوجهة.", - "transferCancelledMoveHint": "قد تبقى بعض الملفات الجزئية في الوجهة. لم تُحذف ملفات المصدر.", - "cleanupDestFiles": "وجهة التنظيف", - "cleanupDestFilesSuccess": "تمت إزالة الملفات الجزئية من الوجهة", - "cleanupDestFilesPartial": "تعذر حذف بعض الملفات الجزئية", - "cleanupDestFilesNothing": "لا يوجد ما يستدعي التنظيف في الوجهة", - "cleanupDestFilesError": "فشلت عملية التنظيف", - "retryTransfer": "إعادة المحاولة", - "retryTransferError": "فشلت المحاولة", - "transferFailedRetryHint": "تم الاحتفاظ ببيانات جزئية على الوجهة. ستُستأنف المحاولة عند عودة الاتصال." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "لا توجد أنفاق SSH", - "createFirstTunnelMessage": "لم تقم بإنشاء أي أنفاق SSH حتى الآن. تكوين اتصالات الأنفاق في مدير المضيف للبدء.", - "connected": "متصل", - "disconnected": "قطع", - "connecting": "جاري الاتصال...", - "error": "خطأ", - "canceling": "إلغاء...", - "connect": "الاتصال", - "disconnect": "قطع الاتصال", - "cancel": "إلغاء", - "port": "المنفذ", - "localPort": "المنفذ المحلي", - "remotePort": "المنفذ البعيد", - "currentHostPort": "منفذ المضيف الحالي", - "endpointPort": "منفذ نقطة النهاية", - "bindIp": "IP المحلي", - "endpointSshConfig": "تكوين SSH نقطة النهاية", - "endpointSshHost": "مضيف SSH نقطة النهاية", - "endpointSshHostPlaceholder": "حدد مضيف مكون", - "endpointSshHostRequired": "حدد مضيف SSH لنقطة النهاية لكل نفق عميل.", - "attempt": "محاولة {{current}} من {{max}}", - "nextRetryIn": "إعادة المحاولة التالية في {{seconds}} ثانية", - "clientTunnels": "أنفاق العميل", - "clientTunnel": "نفق العميل", - "addClientTunnel": "إضافة نفق للعميل", - "noClientTunnels": "لم يتم تكوين أنفاق العميل على هذا المكتب.", - "tunnelName": "اسم النفق", - "remoteHost": "المضيف البعيد", - "autoStart": "بدء التشغيل التلقائي", - "clientAutoStartDesc": "يبدأ عندما يفتح عميل سطح المكتب هذا ويبقى متصلا.", - "clientManualStartDesc": "استخدام البدء ووقف من هذا الصف. لا تفتحه Terمزيكس تلقائياً.", - "clientRemoteServerNote": "قد تتطلب إعادة التوجيه عن بعد AllowTcpForarding و بوابة المنافذ على خادم SSH نقطة النهائية. المنفذ البعيد يغلق عند قطع اتصال سطح المكتب.", - "clientTunnelStarted": "بدأ نفق العميل", - "clientTunnelStopped": "توقف نفق العميل", - "tunnelTestSucceeded": "نجاح اختبار النفق", - "tunnelTestFailed": "فشل اختبار النفق", - "localSaved": "أنفاق العميل المحفوظة", - "localSaveError": "فشل في حفظ أنفاق العميل المحلي", - "invalidBindIp": "يجب أن يكون IP المحلي عنوان IPv4 صالح.", - "invalidLocalTargetIp": "يجب أن يكون عنوان IP المستهدف المحلي عنوان IPv4 صالح.", - "invalidLocalPort": "يجب أن يكون المنفذ المحلي بين 1 و 65535.", - "invalidRemotePort": "يجب أن يكون المنفذ البعيد بين 1 و 65535.", - "invalidLocalTargetPort": "يجب أن يكون المنفذ المستهدف المحلي بين 1 و 65535.", - "invalidEndpointPort": "منفذ نقطة النهاية يجب أن يكون بين 1 و 65535.", - "duplicateAutoStartBind": "يمكن استخدام نفق عميل واحد لبدء التشغيل التلقائي فقط {{bind}}.", - "manualControlError": "فشل تحديث حالة النفق.", - "active": "نشط", - "start": "ابدأ", - "stop": "توقف", - "test": "اختبار", - "type": "نوع النفق", - "typeLocal": "محلي (-L)", - "typeRemote": "عن بعد (-R)", - "typeDynamic": "ديناميكية (-D)", - "typeServerLocalDesc": "المضيف الحالي إلى نقطة النهاية.", - "typeServerRemoteDesc": "نقطة العودة إلى المضيف الحالي.", - "typeClientLocalDesc": "كمبيوتر محلي لنهاية النقاش.", - "typeClientRemoteDesc": "نهاية العودة إلى الكمبيوتر المحلي.", - "typeClientDynamicDesc": "SOCKS على الكمبيوتر المحلي.", - "typeDynamicDesc": "إعادة توجيه حركة الاتصال SOCKS5 عبر SSH", - "forwardDescriptionServerLocal": "المضيف الحالي {{sourcePort}} → نقطة النهاية {{endpointPort}}.", - "forwardDescriptionServerRemote": "نقطة النهاية {{endpointPort}} → المضيف الحالي {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS على المضيف الحالي {{sourcePort}}.", - "forwardDescriptionClientLocal": "محلي {{sourcePort}} → {{endpointPort}} البعيد.", - "forwardDescriptionClientRemote": "عن بعد {{sourcePort}} → المحلي {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS على المنفذ المحلي {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → جوارب عبر {{endpoint}}", - "autoNameClientLocal": "محلي {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → المحلي {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} عبر {{endpoint}}", - "route": "المسار:", - "lastStarted": "بدأت آخر مرة", - "lastTested": "اختبارها الأخير", - "lastError": "آخر خطأ", - "maxRetries": "الحد الاقصى لمحاولات", - "maxRetriesDescription": "الحد الأقصى لمحاولات إعادة المحاولة.", - "retryInterval": "الفاصل الزمني لإعادة المحاولة (بالثواني)", - "retryIntervalDescription": "حان وقت الانتظار بين محاولات إعادة المحاولة.", - "local": "محلي", - "remote": "عن بعد", - "destination": "الوجهة", - "host": "المضيف", - "mode": "الوضع", - "noHostSelected": "لم يتم اختيار مضيف", - "working": "يعمل..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "المعالج", - "memory": "الذاكرة", - "disk": "قرص", - "network": "الشبكة", - "uptime": "وقت التحديث", - "processes": "العمليات", - "available": "متوفر", - "free": "مجاني", - "connecting": "جاري الاتصال...", - "connectionFailed": "فشل الاتصال بالخادم", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "{{count}} النواة", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "استخدام المعالج", - "memoryUsage": "استخدام الذاكرة", - "diskUsage": "استخدام القرص", - "failedToFetchHostConfig": "فشل في جلب إعدادات المضيف", - "serverOffline": "الخادم غير متصل", - "cannotFetchMetrics": "لا يمكن جلب المقاييس من الخادم غير متصل", - "totpFailed": "فشل التحقق من TOTP", - "noneAuthNotSupported": "إحصائيات الخادم لا تدعم نوع مصادقة 'لا شيء'.", - "load": "تحميل", - "systemInfo": "معلومات النظام", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "نظام التشغيل", - "kernel": "نواة", - "seconds": "ثواني", - "networkInterfaces": "واجهة الشبكة", - "noInterfacesFound": "لم يتم العثور على واجهات شبكة", - "noProcessesFound": "لم يتم العثور على عمليات", - "loginStats": "إحصائيات تسجيل دخول SSH", - "noRecentLoginData": "لا توجد بيانات تسجيل دخول حديثة", - "executingQuickAction": "تنفيذ {{name}}...", - "quickActionSuccess": "أكمل {{name}} بنجاح", - "quickActionFailed": "فشل {{name}}", - "quickActionError": "فشل في تنفيذ {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "منافذ الاستماع", + "title": "Listening Ports", "protocol": "Protocol", - "port": "المنفذ", - "address": "العنوان", - "process": "العملية", - "noData": "لا توجد بيانات منافذ الاستماع" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "جدار الحماية", - "inactive": "غير نشط", - "policy": "السياسة", - "rules": "القواعد", - "noData": "لا تتوفر بيانات جدار الحماية", - "action": "اجراء", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "المنفذ", - "source": "المصدر", - "anywhere": "في أي مكان" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "متوسط التحميل", - "swap": "تبديل", - "architecture": "معماري", - "refresh": "تحديث", - "retry": "إعادة المحاولة" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "إدارة SSH وسطح المكتب عن بُعد ذاتية الاستضافة", - "loginTitle": "تسجيل الدخول إلى Termix", - "registerTitle": "إنشاء حساب", - "forgotPassword": "نسيت كلمة المرور؟", - "rememberMe": "تذكر الجهاز لمدة 30 يوما (يشمل TOTP)", - "noAccount": "ليس لديك حساب؟", - "hasAccount": "لديك حساب بالفعل؟", - "twoFactorAuth": "المصادقة الثنائية", - "enterCode": "أدخل رمز التحقق", - "backupCode": "أو استخدم رمز النسخ الاحتياطي", - "verifyCode": "التحقق من الرمز", - "redirectingToApp": "إعادة التوجيه إلى التطبيق...", - "sshAuthenticationRequired": "مطلوب مصادقة SSH", - "sshNoKeyboardInteractive": "المصادقة التفاعلية لوحة المفاتيح غير متوفرة", - "sshAuthenticationFailed": "فشل المصادقة", - "sshAuthenticationTimeout": "مهلة المصادقة", - "sshNoKeyboardInteractiveDescription": "الخادم لا يدعم مصادقة لوحة المفاتيح التفاعلية. الرجاء توفير كلمة المرور أو مفتاح SSH الخاص بك.", - "sshAuthFailedDescription": "بيانات الاعتماد المقدمة غير صحيحة. الرجاء المحاولة مرة أخرى باستخدام بيانات اعتماد صالحة.", - "sshTimeoutDescription": "انتهت مهلة محاولة المصادقة. الرجاء المحاولة مرة أخرى.", - "sshProvideCredentialsDescription": "الرجاء تقديم بيانات اعتماد SSH الخاصة بك للاتصال بهذا الخادم.", - "sshPasswordDescription": "أدخل كلمة المرور لهذا الاتصال SSH.", - "sshKeyPasswordDescription": "إذا تم تشفير مفتاح SSH الخاص بك، أدخل كلمة المرور هنا.", - "passphraseRequired": "كلمة المرور مطلوبة", - "passphraseRequiredDescription": "مفتاح SSH مشفر. الرجاء إدخال عبارة المرور لفتحها.", - "back": "الرجوع", - "firstUser": "المستخدم الأول", - "firstUserMessage": "أنت أول مستخدم وسيتم جعله مشرف. يمكنك عرض إعدادات المشرف في القائمة المنسدلة للمستخدم الجانبي. إذا كنت تعتقد أن هذا خطأ، تحقق من سجلات المرفأ، أو قم بإنشاء مشكلة GitHub .", - "external": "خارجي", - "loginWithExternal": "تسجيل الدخول مع المزود الخارجي", - "loginWithExternalDesc": "تسجيل الدخول باستخدام موفر الهوية الخارجي المعد الخاص بك", - "externalNotSupportedInElectron": "المصادقة الخارجية غير مدعومة في تطبيق إلكترون بعد. الرجاء استخدام إصدار الويب لتسجيل الدخول OIDC.", - "resetPasswordButton": "إعادة تعيين كلمة المرور", - "sendResetCode": "إرسال رمز إعادة التعيين", - "resetCodeDesc": "أدخل اسم المستخدم الخاص بك لتلقي رمز إعادة تعيين كلمة المرور. سيتم تسجيل الدخول في سجلات حاويات الجهاز التنزيلي.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "التحقق من الرمز", - "enterResetCode": "أدخل الرمز المكون من 6 أرقام من سجلات حاويات الجهاز للمستخدم:", - "newPassword": "كلمة المرور الجديدة", - "confirmNewPassword": "تأكيد كلمة المرور", - "enterNewPassword": "أدخل كلمة المرور الجديدة للمستخدم:", - "signUp": "تسجيل الدخول", - "desktopApp": "تطبيق سطح المكتب", - "loggingInToDesktopApp": "تسجيل الدخول إلى تطبيق سطح المكتب", - "loadingServer": "جاري تحميل الخادم...", - "dataLossWarning": "إعادة تعيين كلمة المرور بهذه الطريقة ستؤدي إلى حذف جميع مضيفي SSH المحفوظين، والاعتمادات، والبيانات المشفرة الأخرى. لا يمكن التراجع عن هذا الإجراء. استخدم هذا فقط إذا كنت قد نسيت كلمة المرور الخاصة بك ولم يتم تسجيل الدخول.", - "authenticationDisabled": "المصادقة معطلة", - "authenticationDisabledDesc": "جميع طرق المصادقة معطلة حاليا. الرجاء الاتصال بالمسؤول.", - "attemptsRemaining": "عدد المحاولات المتبقية: {{count}}" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "التحقق من مفتاح استضافة SSH", - "keyChangedWarning": "تم تغيير مفتاح المضيف SSH", - "firstConnectionTitle": "أول مرة تتصل بهذا المضيف", - "firstConnectionDescription": "لا يمكن إنشاء صحة هذا المضيف. تحقق من بصمة الإصبع تطابق ما تتوقعه.", - "keyChangedDescription": "مفتاح المضيف لهذا الخادم قد تغير منذ الاتصال الأخير. يمكن أن يشير هذا إلى مشكلة أمنية.", - "previousKey": "المفتاح السابق", - "newFingerprint": "بصمة جديدة", - "fingerprint": "بصمة", - "verifyInstructions": "إذا كنت تثق بهذا المضيف، انقر فوق قبول للمتابعة وحفظ بصمة الإصبع هذه للاتصال في المستقبل.", - "securityWarning": "تحذير الأمان", - "acceptAndContinue": "قبول ومتابعة", - "acceptNewKey": "قبول المفتاح الجديد ومتابعة" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "تعذر الاتصال بقاعدة البيانات", - "unknownError": "خطأ غير معروف", - "loginFailed": "فشل تسجيل الدخول", - "failedPasswordReset": "فشل في بدء إعادة تعيين كلمة المرور", - "failedVerifyCode": "فشل التحقق من إعادة تعيين الرمز", - "failedCompleteReset": "فشل في إكمال إعادة تعيين كلمة المرور", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "فشل في بدء تسجيل الدخول OIDC", - "silentSigninOidcUnavailable": "طلب تسجيل الدخول الصامت, ولكن تسجيل الدخول OIDC غير متوفر.", - "failedUserInfo": "فشل الحصول على معلومات المستخدم بعد تسجيل الدخول", - "oidcAuthFailed": "فشل مصادقة OIDC", - "invalidAuthUrl": "رابط تفويض غير صالح مستلم من الخلفية", - "requiredField": "هذا الحقل مطلوب", - "minLength": "الحد الأدنى للطول هو {{min}}", - "passwordMismatch": "كلمتا المرور غير متطابقتين", - "passwordLoginDisabled": "اسم المستخدم/كلمة المرور غير مفعل حاليا", - "sessionExpired": "انتهت صلاحية الجلسة - الرجاء تسجيل الدخول مرة أخرى", - "totpRateLimited": "معدل محدود: العديد من محاولات التحقق من TOTP. الرجاء المحاولة مرة أخرى لاحقاً.", - "totpRateLimitedWithTime": "تم تجاوز الحد المسموح به: عدد محاولات التحقق من رمز TOTP كبير جدًا. يُرجى الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", - "resetCodeRateLimited": "معدل محدود: العديد من محاولات التحقق. الرجاء المحاولة مرة أخرى لاحقاً.", - "resetCodeRateLimitedWithTime": "معدل محدود: العديد من محاولات التحقق. الرجاء الانتظار {{time}} ثانية قبل المحاولة مرة أخرى.", - "authTokenSaveFailed": "فشل في حفظ رمز المصادقة", - "failedToLoadServer": "فشل تحميل الخادم" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "تسجيل الحساب الجديد معطل حاليًا من قبل المسؤول. الرجاء تسجيل الدخول أو الاتصال بالمسؤول.", - "userNotAllowed": "حسابك غير مصرح له بالتسجيل. الرجاء الاتصال بالمسؤول.", - "databaseConnectionFailed": "فشل الاتصال بخادم قاعدة البيانات", - "resetCodeSent": "إعادة تعيين التعليمات البرمجية المرسلة إلى سجلات Docker", - "codeVerified": "تم التحقق من الرمز بنجاح", - "passwordResetSuccess": "تم إعادة تعيين كلمة المرور بنجاح", - "loginSuccess": "تم تسجيل الدخول بنجاح", - "registrationSuccess": "تم التسجيل بنجاح" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "أنفاق سطح المكتب المحلية التي تستهدف مضيفي SSH.", - "c2sTunnelPresets": "الإعدادات المسبقة للنفق للعميل", - "c2sTunnelPresetsDesc": "حفظ قائمة النفق المحلية لهذا العميل سطح المكتب كخادم مسبقا، أو تحميل الإعداد المسبق لهذا العميل مرة أخرى.", - "c2sTunnelPresetsUnavailable": "تتوفر الإعدادات المسبقة لنفق العميل فقط في عميل سطح المكتب.", - "c2sPresetName": "اسم الإعداد المسبق", - "c2sPresetNamePlaceholder": "اسم العميل المسبق", - "c2sPresetToLoad": "الإعداد المسبق للتحميل", - "c2sNoPresetSelected": "لم يتم تحديد الإعداد المسبق", - "c2sNoPresets": "لم يتم حفظ أي إعدادات مسبقة", - "c2sLoadPreset": "تحميل", - "c2sCurrentLocalConfig": "{{count}} تم تكوين نفق (أنفاق) العميل المحلي على سطح المكتب هذا.", - "c2sPresetSyncNote": "الإعداد المسبق عبارة عن لقطات واضحة؛ تحميل واحد يحل محل قائمة نفق عميل سطح المكتب هذه.", - "c2sPresetSaved": "تم حفظ الإعداد المسبق لنفق العميل", - "c2sPresetLoaded": "تم تحميل نفق العميل مسبقاً محلياً", - "c2sPresetRenamed": "تم إعادة تسمية نفق العميل مسبقاً", - "c2sPresetDeleted": "تم حذف الضبط المسبق لنفق العميل", - "c2sPresetLoadError": "فشل تحميل الإعدادات المسبقة لنفق العميل" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "اللغة", - "keyPassword": "كلمة مرور المفتاح", - "pastePrivateKey": "لصق المفتاح الخاص بك هنا...", - "localListenerHost": "127.0.0.1 (الاستماع محليا)", - "localTargetHost": "127-0.0.1 (الهدف على هذا الحاسوب)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "أدخل كلمة المرور", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "لوحة التحكم", - "loading": "تحميل لوحة التحكم...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "الدعم", - "discord": "ديسكورد", - "serverOverview": "نظرة عامة على الخادم", - "version": "الإصدار", - "upToDate": "حتى التاريخ", - "updateAvailable": "تحديث متوفر", - "beta": "بيتا", - "uptime": "وقت التحديث", - "database": "قاعدة البيانات", - "healthy": "صحي", - "error": "خطأ", - "totalHosts": "إجمالي المضيفين", - "totalTunnels": "إجمالي الأنفاق", - "totalCredentials": "مجموع بيانات الاعتماد", - "recentActivity": "النشاط الأخير", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "جاري تحميل النشاط الأخير...", - "noRecentActivity": "لا يوجد نشاط حديث", - "quickActions": "الإجراءات السريعة", - "addHost": "إضافة مضيف", - "addCredential": "إضافة بيانات اعتماد", - "adminSettings": "إعدادات المدير", - "userProfile": "الملف الشخصي للمستخدم", - "serverStats": "إحصائيات الخادم", - "loadingServerStats": "تحميل إحصائيات الخادم...", - "noServerData": "لا تتوفر بيانات الخادم", - "cpu": "المعالج", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "تخصيص لوحة التحكم", - "dashboardSettings": "إعدادات لوحة التحكم", - "enableDisableCards": "تمكين/تعطيل البطاقات", - "resetLayout": "إعادة التعيين إلى الافتراضي", - "serverOverviewCard": "نظرة عامة على الخادم", - "recentActivityCard": "النشاط الأخير", - "networkGraphCard": "رسم الشبكة", - "networkGraph": "رسم الشبكة", - "quickActionsCard": "الإجراءات السريعة", - "serverStatsCard": "إحصائيات الخادم", - "panelMain": "الرئيسية", - "panelSide": "جانب", - "justNow": "الآن فقط" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ابدأ", - "hostsOnline": "المضيفون على الإنترنت", - "activeTunnels": "أنفاق نشطة", - "registerNewServer": "تسجيل خادم جديد", - "storeSshKeysOrPasswords": "تخزين مفاتيح SSH أو كلمات المرور", - "manageUsersAndRoles": "إدارة المستخدمين والأدوار", - "manageYourAccount": "إدارة حسابك", - "hostStatus": "حالة المضيف", - "noHostsConfigured": "لا يوجد مستضيفين تم تكوينهم", - "online": "ابدأ", - "offline": "ممنوع", - "onlineLower": "متصل", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "إضافة:", - "commandPalette": "لوحة الأوامر", - "done": "تم", - "editModeInstructions": "اسحب البطاقات لإعادة الترتيب · اسحب موفر العمود لتغيير حجم الأعمدة · اسحب الحافة السفلية لبطاقة لتغيير حجم ارتفاعها · سلة المهملات لإزالة", - "empty": "فارغ", - "clear": "مسح" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "إضافة مضيف", - "addGroup": "إضافة مجموعة", - "addLink": "إضافة رابط", - "zoomIn": "تكبير في", - "zoomOut": "تكبير خارج", - "resetView": "إعادة تعيين العرض", - "selectHost": "حدد المضيف", - "chooseHost": "اختر مضيفاً...", - "parentGroup": "المجموعة الأصل", - "noGroup": "لا توجد مجموعة", - "groupName": "اسم المجموعة", - "color": "اللون", - "source": "المصدر", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "نقل إلى المجموعة", - "selectGroup": "حدد المجموعة...", - "addConnection": "إضافة اتصال", - "hostDetails": "تفاصيل المضيف", - "removeFromGroup": "إزالة من المجموعة", - "addHostHere": "إضافة مضيف هنا", - "editGroup": "تحرير المجموعة", - "delete": "حذف", - "add": "إضافة", - "create": "إنشاء", - "move": "نقل", - "connect": "الاتصال", - "createGroup": "إنشاء مجموعة", - "selectSourcePlaceholder": "حدد المصدر...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "ملف غير صالح", - "hostAlreadyExists": "المضيف موجود بالفعل في التوبولوجيا", - "connectionExists": "الاتصال موجود بالفعل", - "unknown": "غير معروف", - "name": "الاسم", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "الحالة", - "failedToAddNode": "فشل في إضافة العقدة", - "sourceDifferentFromTarget": "يجب أن يكون المصدر والهدف مختلفين", - "exportJSON": "تصدير JSON", - "importJSON": "استيراد JSON", - "terminal": "المحطة", - "fileManager": "مدير الملفات", - "tunnel": "نفق", - "docker": "دوكر", - "serverStats": "إحصائيات الخادم", - "noNodes": "لا توجد عقدة بعد" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker غير مفعل لهذا المضيف", - "validating": "التحقق من دوكر...", - "connecting": "جاري الاتصال...", - "error": "خطأ", - "version": "روكر {{version}}", - "connectionFailed": "فشل الاتصال بـ Docker", - "containerStarted": "بدأ الحاوية {{name}}", - "failedToStartContainer": "فشل بدء تشغيل الحاوية {{name}}", - "containerStopped": "تم إيقاف الحاوية {{name}}", - "failedToStopContainer": "فشل في إيقاف الحاوية {{name}}", - "containerRestarted": "تم إعادة تشغيل الحاوية {{name}}", - "failedToRestartContainer": "فشل في إعادة تشغيل الحاوية {{name}}", - "containerPaused": "الحاوية {{name}} متوقفة مؤقتاً", - "containerUnpaused": "الحاوية {{name}} غير متوقفة مؤقتاً", - "failedToTogglePauseContainer": "فشل تبديل حالة الإيقاف المؤقت للحاوية {{name}}", - "containerRemoved": "تمت إزالة الحاوية {{name}}", - "failedToRemoveContainer": "فشل إزالة الحاوية {{name}}", - "image": "صورة", - "ports": "المنافذ", - "noPorts": "لا توجد منافذ", - "start": "ابدأ", - "confirmRemoveContainer": "هل أنت متأكد من أنك تريد إزالة الحاوية '{{name}}'؟ لا يمكن التراجع عن هذا الإجراء.", - "runningContainerWarning": "تحذير: هذه الحاوية قيد التشغيل حاليا. إزالتها ستوقف الحاوية أولاً.", - "loadingContainers": "تحميل الحاويات...", - "manager": "مدير الدوكر", - "autoRefresh": "تحديث تلقائي", - "timestamps": "الطوابع الزمنية", - "lines": "خطوط", - "filterLogs": "تصفية السجلات...", - "refresh": "تحديث", - "download": "تنزيل", - "clear": "مسح", - "logsDownloaded": "تم تنزيل السجلات بنجاح", - "last50": "آخر 50", - "last100": "آخر 100", - "last500": "آخر 500", - "last1000": "آخر 1000", - "allLogs": "جميع السجلات", - "noLogsMatching": "لا توجد سجلات مطابقة \"{{query}}\"", - "noLogsAvailable": "لا توجد سجلات متاحة", - "noContainersFound": "لم يتم العثور على حاويات", - "noContainersFoundHint": "لا توجد حاويات منصة للدفع متاحة على هذا المضيف", - "searchPlaceholder": "البحث في الحاويات...", - "allStatuses": "جميع الحالات", - "stateRunning": "تشغيل", - "statePaused": "متوقف", - "stateExited": "خروج", - "stateRestarting": "إعادة التشغيل", - "noContainersMatchFilters": "لا توجد حاويات تطابق الفلاتر الخاصة بك", - "noContainersMatchFiltersHint": "حاول تعديل معايير البحث أو التصفية الخاصة بك", - "failedToFetchStats": "فشل في جلب إحصائيات الحاويات", - "containerNotRunning": "الحاوية غير قيد التشغيل", - "startContainerToViewStats": "بدء تشغيل الحاوية لعرض الإحصاءات", - "loadingStats": "جاري تحميل الإحصاءات...", - "errorLoadingStats": "خطأ في تحميل الإحصاءات", - "noStatsAvailable": "لا تتوفر إحصائيات", - "cpuUsage": "استخدام المعالج", - "current": "الحالي", - "memoryUsage": "استخدام الذاكرة", - "networkIo": "الشبكة I/O", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "الناتج", - "blockIo": "حظر I/O", - "read": "قراءة", - "write": "كتابة", - "pids": "أرقام التعريف", - "containerInformation": "معلومات الحاوية", - "name": "الاسم", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "الولاية", - "containerMustBeRunning": "يجب أن يكون الحاوية قيد التشغيل للوصول إلى وحدة التحكم", - "verificationCodePrompt": "أدخل رمز التحقق", - "totpVerificationFailed": "فشل التحقق من TOTP. الرجاء المحاولة مرة أخرى.", - "warpgateVerificationFailed": "فشل مصادقة التحذير. الرجاء المحاولة مرة أخرى.", - "connectedTo": "متصل بـ {{containerName}}", - "disconnected": "قطع", - "consoleError": "خطأ في وحدة التحكم", - "errorMessage": "خطأ: {{message}}", - "failedToConnect": "فشل الاتصال بالحاوية", - "console": "وحدة", - "selectShell": "حدد قذيفة", - "bash": "باش", - "sh": "رماد", - "ash": "رماد", - "connect": "الاتصال", - "disconnect": "قطع الاتصال", - "notConnected": "غير متصل", - "clickToConnect": "انقر الاتصال لبدء جلسة قذيفة", - "connectingTo": "جاري الاتصال بـ {{containerName}}...", - "containerNotFound": "الحاوية غير موجودة", - "backToList": "العودة إلى القائمة", - "logs": "السجلات", - "stats": "إحصائيات", - "consoleTab": "وحدة", - "startContainerToAccess": "بدء تشغيل الحاوية للوصول إلى وحدة التحكم" + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "عام", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "المستخدمون", - "sectionSessions": "الجلسات", - "sectionRoles": "الأدوار", - "sectionDatabase": "قاعدة البيانات", - "sectionApiKeys": "مفاتيح API", - "allowRegistration": "السماح بتسجيل المستخدم", - "allowRegistrationDesc": "السماح للمستخدمين الجدد بالتسجيل الذاتي", - "allowPasswordLogin": "السماح بتسجيل الدخول لكلمة المرور", - "allowPasswordLoginDesc": "اسم المستخدم/كلمة المرور", - "oidcAutoProvision": "توفير OIDC التلقائي", - "oidcAutoProvisionDesc": "إنشاء حسابات تلقائي لمستخدمي OIDC حتى عند تعطيل التسجيل", - "allowPasswordReset": "السماح بإعادة تعيين كلمة المرور", - "allowPasswordResetDesc": "إعادة تعيين التعليمات البرمجية عبر سجلات Docker", - "sessionTimeout": "مهلة الجلسة", - "hours": "ساعات", - "sessionTimeoutRange": "دقيقة 1 ساعة - الحد الأقصى 720 ساعة", - "monitoringDefaults": "مراقبة الافتراضات", - "statusCheck": "التحقق من الحالة", - "metrics": "القياسات", - "sec": "ثواني", - "logLevel": "مستوى السجل", - "enableGuacamole": "تمكين غواكامول", - "enableGuacamoleDesc": "سطح المكتب البعيد RDP/VNC", - "guacdUrl": "رابط guacd", - "oidcDescription": "تكوين اتصال OpenID لـ SSO. الحقول المعلّمة * مطلوبة.", - "oidcClientId": "معرف العميل", - "oidcClientSecret": "سر العميل", - "oidcAuthUrl": "رابط التخويل", - "oidcIssuerUrl": "رابط المصدر", - "oidcTokenUrl": "رابط الرمز المميز", - "oidcUserIdentifier": "مسار معرف المستخدم", - "oidcDisplayName": "عرض مسار الاسم", - "oidcScopes": "النطاقات", - "oidcUserinfoUrl": "تجاوز رابط معلومات المستخدم", - "oidcAllowedUsers": "المستخدمين المسموح بهم", - "oidcAllowedUsersDesc": "بريد إلكتروني واحد لكل سطر. اتركه فارغاً للسماح للجميع.", - "removeOidc": "إزالة", - "usersCount": "{{count}} مستخدمون", - "createUser": "إنشاء", - "newRole": "دور جديد", - "roleName": "الاسم", - "roleDisplayName": "اسم العرض", - "roleDescription": "الوصف", - "rolesCount": "أدوار {{count}}", - "createRole": "إنشاء", - "creating": "إنشاء...", - "exportDatabase": "تصدير قاعدة البيانات", - "exportDatabaseDesc": "تحميل نسخة احتياطية من جميع المضيفين ووثائق الاعتماد والإعدادات", - "export": "تصدير", - "exporting": "تصدير...", - "importDatabase": "استيراد قاعدة البيانات", - "importDatabaseDesc": "استعادة من ملف النسخ الاحتياطي sqlite", - "importDatabaseSelected": "مختار: {{name}}", - "selectFile": "حدد ملف", - "changeFile": "تغيير", - "import": "استيراد", - "importing": "استيراد...", - "apiKeysCount": "مفاتيح {{count}}", - "newApiKey": "مفتاح API جديد", - "apiKeyCreatedWarning": "تم إنشاء المفتاح - انسخه الآن، لن يظهر مرة أخرى.", - "apiKeyName": "الاسم", - "apiKeyUser": "المستخدم", - "apiKeySelectUser": "اختر مستخدم...", - "apiKeyExpiresAt": "تنتهي في", - "createKey": "إنشاء مفتاح", - "apiKeyNoExpiry": "لا تنتهي", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "المصادقة المزدوجة", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "محلي", - "adminStatusAdministrator": "المدير", - "adminStatusRegularUser": "مستخدم عادي", - "adminBadge": "ديمين", - "systemBadge": "النظام", - "customBadge": "الكوستوم", - "youBadge": "أنت", - "sessionsActive": "{{count}} نشط", - "sessionActive": "نشط: {{time}}", - "sessionExpires": "التجربة: {{time}}", - "revokeAll": "الكل", - "revokeAllSessionsSuccess": "تم إلغاء جميع جلسات المستخدم", - "revokeAllSessionsFailed": "فشل في إلغاء الجلسات", - "revokeSessionFailed": "فشل في إلغاء الجلسة", - "addRole": "إضافة دور", - "noCustomRoles": "لا توجد أدوار مخصصة", - "removeRoleFailed": "فشل في إزالة الدور", - "assignRoleFailed": "فشل في تعيين الدور", - "deleteRoleFailed": "فشل في حذف الدور", - "userAdminAccess": "المدير", - "userAdminAccessDesc": "الوصول الكامل إلى جميع إعدادات المشرف", - "userRoles": "الأدوار", - "revokeAllUserSessions": "إلغاء جميع الجلسات", - "revokeAllUserSessionsDesc": "فرض إعادة تسجيل الدخول على جميع الأجهزة", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "حذف هذا المستخدم دائم.", - "deleteUser": "حذف {{username}}", - "deleting": "حذف...", - "deleteUserFailed": "فشل في حذف المستخدم", - "deleteUserSuccess": "تم حذف المستخدم \"{{username}}\"", - "deleteRoleSuccess": "تم حذف الدور \"{{name}}\"", - "revokeKeySuccess": "تم إلغاء المفتاح \"{{name}}\"", - "revokeKeyFailed": "فشل في إلغاء المفتاح", - "copiedToClipboard": "نسخ إلى الحافظة", - "done": "تم", - "createUserTitle": "إنشاء مستخدم", - "createUserDesc": "إنشاء حساب محلي جديد.", - "createUserUsername": "اسم المستخدم", - "createUserPassword": "كلمة المرور", - "createUserPasswordHint": "الحد الأدنى 6 أحرف.", - "createUserEnterUsername": "أدخل اسم المستخدم", - "createUserEnterPassword": "أدخل كلمة المرور", - "createUserSubmit": "إنشاء مستخدم", - "editUserTitle": "إدارة المستخدم: {{username}}", - "editUserDesc": "تحرير الأدوار، حالة المشرف والجلسات وإعدادات الحساب.", - "editUserUsername": "اسم المستخدم", - "editUserAuthType": "نوع المصادقة", - "editUserAdminStatus": "حالة المشرف", - "editUserUserId": "معرف المستخدم", - "linkAccountTitle": "ربط حساب كلمة المرور OIDC", - "linkAccountDesc": "دمج حساب OIDC {{username}} مع حساب محلي موجود.", - "linkAccountWarningTitle": "وسيقوم هذا بما يلي:", - "linkAccountEffect1": "حذف حساب OIDC-فقط", - "linkAccountEffect2": "إضافة تسجيل الدخول OIDC إلى الحساب المستهدف", - "linkAccountEffect3": "السماح لكل من OIDC و كلمة المرور", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "أدخل اسم مستخدم الحساب المحلي للربط به", - "linkAccounts": "ربط الحسابات", - "linkAccountSuccess": "حساب OIDC مرتبط بـ \"{{username}}\"", - "linkAccountFailed": "فشل ربط حساب OIDC", - "linkAccountInProgress": "جارٍ الربط...", - "saving": "حفظ...", - "updateRegistrationFailed": "فشل تحديث إعدادات التسجيل", - "updatePasswordLoginFailed": "فشل تحديث إعداد تسجيل الدخول لكلمة المرور", - "updateOidcAutoProvisionFailed": "فشل تحديث إعدادات توفير OIDC التلقائي", - "updatePasswordResetFailed": "فشل تحديث إعدادات إعادة تعيين كلمة المرور", - "sessionTimeoutRange2": "مهلة الجلسة يجب أن تكون بين 1 و 720 ساعة", - "sessionTimeoutSaved": "تم حفظ مهلة الجلسة", - "sessionTimeoutSaveFailed": "فشل في حفظ مهلة الجلسة", - "monitoringIntervalInvalid": "قيم الفاصل الزمني غير صالحة", - "monitoringSaved": "تم حفظ إعدادات الرصد", - "monitoringSaveFailed": "فشل في حفظ إعدادات المراقبة", - "guacamoleSaved": "تم حفظ إعدادات غواكامول", - "guacamoleSaveFailed": "فشل في حفظ إعدادات غواكامول", - "guacamoleUpdateFailed": "فشل تحديث إعدادات غواكامول", - "logLevelUpdateFailed": "فشل تحديث مستوى السجل", - "oidcSaved": "تم حفظ إعدادات OIDC", - "oidcSaveFailed": "فشل في حفظ إعدادات OIDC", - "oidcRemoved": "تم حذف إعدادات OIDC", - "oidcRemoveFailed": "فشل في إزالة إعدادات OIDC", - "createUserRequired": "اسم المستخدم وكلمة المرور مطلوبتان", - "createUserPasswordTooShort": "يجب أن تتكون كلمة المرور من 6 أحرف على الأقل", - "createUserSuccess": "تم إنشاء المستخدم \"{{username}}\"", - "createUserFailed": "فشل في إنشاء المستخدم", - "updateAdminStatusFailed": "فشل تحديث حالة المشرف", - "allSessionsRevoked": "تم إلغاء جميع الجلسات", - "revokeSessionsFailed": "فشل في إلغاء الجلسات", - "createRoleRequired": "الاسم واسم العرض مطلوبين", - "createRoleSuccess": "تم إنشاء الدور \"{{name}}\"", - "createRoleFailed": "فشل إنشاء الدور", - "apiKeyNameRequired": "اسم المفتاح مطلوب", - "apiKeyUserRequired": "معرف المستخدم مطلوب", - "apiKeyCreatedSuccess": "مفتاح API \"{{name}}\" تم إنشاؤه", - "apiKeyCreateFailed": "فشل إنشاء مفتاح API", - "exportSuccess": "تم تصدير قاعدة البيانات بنجاح", - "exportFailed": "فشل تصدير قاعدة البيانات", - "importSelectFile": "الرجاء تحديد ملف أولاً", - "importCompleted": "اكتمل الاستيراد: تم استيراد {{total}} عنصرًا، وتم تخطي {{skipped}} عنصرًا", - "importFailed": "فشل الاستيراد: {{error}}", - "importError": "فشل استيراد قاعدة البيانات" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "المضيف", - "hostPlaceholder": "192.168.1-1 أو مثال.com", - "portLabel": "المنفذ", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "اسم المستخدم", - "usernamePlaceholder": "اسم المستخدم", - "authLabel": "المصادقة", - "passwordLabel": "كلمة المرور", - "passwordPlaceholder": "كلمة المرور", - "privateKeyLabel": "المفتاح الخاص", - "privateKeyPlaceholder": "لصق المفتاح الخاص...", - "credentialLabel": "بيانات", - "credentialPlaceholder": "حدد بيانات الاعتماد المحفوظة", - "connectToTerminal": "الاتصال بالمحطة الطرفية", - "connectToFiles": "الاتصال بالملفات" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "لم يتم تحديد محطة طرفية", - "noTerminalSelectedHint": "فتح علامة تبويب محطة SSH لعرض سجل الأوامر", - "searchPlaceholder": "سجل البحث...", - "clearAll": "مسح الكل", - "noHistoryEntries": "لا توجد إدخالات في السجل", - "trackingDisabled": "تعقب التاريخ معطل", - "trackingDisabledHint": "قم بتمكينه في إعدادات الملف الشخصي لتسجيل الأوامر." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "تسجيل المفتاح", - "recordToTerminals": "تسجيل في المحطات الطرفية", - "selectAll": "الكل", - "selectNone": "لا", - "noTerminalTabsOpen": "لا توجد علامات تبويب طرفية مفتوحة", - "selectTerminalsAbove": "حدد المحطات الطرفية أعلاه", - "broadcastInputPlaceholder": "اكتب هنا لقوائم مفاتيح البث...", - "stopRecording": "إيقاف التسجيل", - "startRecording": "بدء التسجيل", - "settingsTitle": "الإعدادات", - "enableRightClickCopyPaste": "تمكين نسخ/لصق بالزر الأيمن" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "تخطيط", - "selectLayoutAbove": "حدد تخطيط أعلاه", - "selectLayoutHint": "اختر عدد الألواح التي سيتم عرضها", - "panesTitle": "لوحات", - "openTabsTitle": "افتح علامات التبويب", - "dragTabsHint": "اسحب علامات التبويب إلى الأجزاء العلوية، أو استخدم خاصية التعيين السريع.", - "dropHere": "إسقاط هنا", - "emptyPane": "فارغ", - "dashboard": "لوحة التحكم", - "clearSplitScreen": "مسح تقسيم الشاشة", - "quickAssign": "التعيين السريع", - "alreadyAssigned": "لوحة {{index}}", - "splitTab": "علامة تبويب مقسمة", - "addToSplit": "أضف إلى التقسيم", - "removeFromSplit": "أزل من التقسيم", - "assignToPane": "تعيين إلى اللوحة" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "إنشاء كتلة كتلة", - "createSnippetDescription": "إنشاء كتلة أوامر جديدة للتنفيذ السريع", - "nameLabel": "الاسم", - "namePlaceholder": "على سبيل المثال، إعادة تشغيل Nginx", - "descriptionLabel": "الوصف", - "descriptionPlaceholder": "وصف اختياري", - "optional": "اختياري", - "folderLabel": "مجلد", - "noFolder": "لا يوجد مجلد (غير مصنف)", - "commandLabel": "أمر", - "commandPlaceholder": "على سبيل المثال, sudo systemctl إعادة تشغيل nginx", - "cancel": "إلغاء", - "createSnippetButton": "إنشاء كتلة كتلة", - "createFolderTitle": "إنشاء مجلد", - "createFolderDescription": "تنظيم كتل الكتل في مجلدات", - "folderNameLabel": "اسم المجلد", - "folderNamePlaceholder": "على سبيل المثال أوامر النظام، سكريبتات Docker", - "folderColorLabel": "لون المجلد", - "folderIconLabel": "أيقونة المجلد", - "previewLabel": "معاينة", - "folderNameFallback": "اسم المجلد", - "createFolderButton": "إنشاء مجلد", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "الكل", - "selectNone": "لا", - "noTerminalTabsOpen": "لا توجد علامات تبويب طرفية مفتوحة", - "searchPlaceholder": "البحث عن كتل الكود...", - "newSnippet": "كتلة جديدة", - "newFolder": "مجلد جديد", - "run": "تشغيل", - "noSnippetsInFolder": "لا يوجد كتل في هذا المجلد", - "uncategorized": "غير مصنف", - "editSnippetTitle": "تعديل كتلة الكود", - "editSnippetDescription": "تحديث كتلة الأوامر", - "saveSnippetButton": "حفظ التغييرات", - "createSuccess": "تم إنشاء كتلة الكود بنجاح", - "createFailed": "فشل في إنشاء كتلة الكود", - "updateSuccess": "تم تحديث كتلة الكود بنجاح", - "updateFailed": "فشل تحديث كتلة الكود", - "deleteFailed": "فشل حذف كتلة الكود", - "folderCreateSuccess": "تم إنشاء المجلد بنجاح", - "folderCreateFailed": "فشل في إنشاء المجلد", - "editFolderTitle": "تعديل المجلد", - "editFolderDescription": "إعادة تسمية أو تغيير مظهر هذا المجلد", - "saveFolderButton": "حفظ التغييرات", - "editFolder": "تحرير المجلد", - "deleteFolder": "حذف المجلد", - "folderDeleteSuccess": "المجلد \"{{name}}محذوف", - "folderDeleteFailed": "فشل في حذف المجلد", - "folderEditSuccess": "تم تحديث المجلد بنجاح", - "folderEditFailed": "فشل تحديث المجلد", - "confirmRunMessage": "تشغيل \"{{name}}\"?", - "confirmRunButton": "يجري", - "runSuccess": "R\"{{name}}\" في المحطة الطرفية {{count}}", - "copySuccess": "تم نسخ \"{{name}}\" إلى الحافظة", - "shareTitle": "مشاركة كتلة الكود", - "shareUser": "المستخدم", - "shareRole": "دور", - "selectUser": "اختر مستخدم...", - "selectRole": "اختر دور...", - "shareSuccess": "تم مشاركة كتلة الكود بنجاح", - "shareFailed": "فشل مشاركة كتلة الكود", - "revokeSuccess": "تم إلغاء الوصول", - "revokeFailed": "فشل في إلغاء الوصول", - "currentAccess": "الوصول الحالي", - "shareLoadError": "فشل تحميل بيانات المشاركة", - "loading": "تحميل...", - "close": "أغلق", - "reorderFailed": "فشل حفظ ترتيب المقتطفات" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "حساب", - "sectionAppearance": "المظهر", - "sectionSecurity": "أمان", - "sectionApiKeys": "مفاتيح API", - "sectionC2sTunnels": "أنفاق C2S", - "usernameLabel": "اسم المستخدم", - "roleLabel": "دور", - "roleAdministrator": "المدير", - "authMethodLabel": "طريقة المصادقة", - "authMethodLocal": "محلي", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "تشغيل", - "twoFaOff": "متوقف", - "versionLabel": "الإصدار", - "deleteAccount": "حذف الحساب", - "deleteAccountDescription": "حذف حسابك بشكل دائم", - "deleteButton": "حذف", - "deleteAccountPermanent": "هذا الإجراء دائم ولا يمكن التراجع عنه.", - "deleteAccountWarning": "سيتم حذف جميع الجلسات والمضيفين ووثائق التفويض والإعدادات بشكل دائم.", - "confirmPasswordDeletePlaceholder": "أدخل كلمة المرور للتأكيد", - "languageLabel": "اللغة", - "themeLabel": "السمة", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "لون اللكنة", - "settingsTerminal": "المحطة", - "commandAutocomplete": "إكمال الأوامر التلقائي", - "commandAutocompleteDesc": "إظهار الإكمال التلقائي أثناء الكتابة", - "historyTracking": "تتبع التاريخ", - "historyTrackingDesc": "تتبع الأوامر الطرفية", - "syntaxHighlighting": "تسليط الضوء على بناء الجملة", - "syntaxHighlightingDesc": "تسليط الضوء على إخراج المحطة", - "commandPalette": "لوحة الأوامر", - "commandPaletteDesc": "تمكين اختصار لوحة المفاتيح", - "reopenTabsOnLogin": "أعد فتح علامات التبويب عند تسجيل الدخول", - "reopenTabsOnLoginDesc": "استعد علامات التبويب المفتوحة عند تسجيل الدخول أو تحديث الصفحة، حتى من جهاز آخر.", - "confirmTabClose": "تأكيد إغلاق علامة التبويب", - "confirmTabCloseDesc": "السؤال قبل إغلاق علامات التبويب الطرفية", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "إظهار الوسوم المضيفة", - "showHostTagsDesc": "عرض العلامات في قائمة المضيف", - "hostTrayOnClick": "انقر لتوسيع إجراءات المضيف", - "hostTrayOnClickDesc": "أظهر أزرار الاتصال دائمًا؛ انقر لتوسيع خيارات الإدارة بدلاً من التمرير فوقها", - "pinAppRail": "دبوس سكة التطبيق", - "pinAppRailDesc": "حافظ على شريط التطبيق الجانبي الأيسر مفتوحًا دائمًا بدلاً من توسيعه عند تمرير المؤشر فوقه.", - "settingsSnippets": "كتل الكود", - "foldersCollapsed": "مجلدات سقطت", - "foldersCollapsedDesc": "طي المجلدات بشكل افتراضي", - "confirmExecution": "تأكيد التنفيذ", - "confirmExecutionDesc": "تأكيد قبل تشغيل كتل الكود", - "settingsUpdates": "التحديثات", - "disableUpdateChecks": "تعطيل التحقق من التحديث", - "disableUpdateChecksDesc": "إيقاف التحقق من وجود تحديثات", - "totpAuthenticator": "مصادقة TOTP", - "totpEnabled": "2FA مفعل", - "totpDisabled": "إضافة أمان تسجيل الدخول الإضافي", - "disable": "تعطيل", - "enable": "تمكين", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "مسح رمز QR أو إدخال سر في تطبيق المصادقة الخاص بك، ثم أدخل الرمز المكون من 6 أرقام", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "تحقق", - "changePassword": "تغيير كلمة المرور", - "currentPasswordLabel": "كلمة المرور الحالية", - "currentPasswordPlaceholder": "كلمة المرور الحالية", - "newPasswordLabel": "كلمة المرور الجديدة", - "newPasswordPlaceholder": "كلمة مرور جديدة", - "confirmPasswordLabel": "تأكيد كلمة المرور الجديدة", - "confirmPasswordPlaceholder": "تأكيد كلمة المرور الجديدة", - "updatePassword": "تحديث كلمة المرور", - "createApiKeyTitle": "إنشاء مفتاح API", - "createApiKeyDescription": "إنشاء مفتاح API جديد للوصول إلى البرامج.", - "apiKeyNameLabel": "الاسم", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "اختياري", - "cancel": "إلغاء", - "createKey": "إنشاء مفتاح", - "apiKeyCount": "مفاتيح {{count}}", - "newKey": "مفتاح جديد", - "noApiKeys": "لا توجد مفاتيح API حتى الآن.", - "apiKeyActive": "نشط", - "apiKeyUsageHint": "تضمين المفتاح الخاص بك في", - "apiKeyUsageHintHeader": "الرأس.", - "apiKeyPermissionsHint": "المفاتيح ترث أذونات المستخدم المنشئ.", - "roleUser": "المستخدم", - "authMethodDual": "المصادقة المزدوجة", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "فشل في بدء إعداد TOTP", - "totpEnter6Digits": "أدخل رمز من 6 أرقام", - "totpEnabledSuccess": "المصادقة الثنائية مفعلة", - "totpInvalidCode": "الرمز غير صالح، الرجاء المحاولة مرة أخرى", - "totpDisableInputRequired": "أدخل رمز TOTP أو كلمة المرور", - "totpDisabledSuccess": "المصادقة الثنائية معطلة", - "totpDisableFailed": "فشل في تعطيل 2FA", - "totpDisableTitle": "تعطيل 2FA", - "totpDisablePlaceholder": "أدخل رمز TOTP أو كلمة المرور", - "totpDisableConfirm": "تعطيل 2FA", - "totpContinueVerify": "مواصلة التحقق", - "totpVerifyTitle": "التحقق من الرمز", - "totpBackupTitle": "رموز النسخ الاحتياطي", - "totpDownloadBackup": "تنزيل رموز النسخ الاحتياطي", - "done": "تم", - "secretCopied": "تم نسخ السر إلى الحافظة", - "apiKeyNameRequired": "اسم المفتاح مطلوب", - "apiKeyCreated": "مفتاح API \"{{name}}\" تم إنشاؤه", - "apiKeyCreateFailed": "فشل إنشاء مفتاح API", - "apiKeyUser": "المستخدم", - "apiKeyExpires": "تنتهي", - "apiKeyRevoked": "تم إلغاء مفتاح API \"{{name}}\"", - "apiKeyRevokeFailed": "فشل في إلغاء مفتاح API", - "passwordFieldsRequired": "كلمات المرور الحالية والجديدة مطلوبة", - "passwordMismatch": "كلمتا المرور غير متطابقتين", - "passwordTooShort": "يجب أن تتكون كلمة المرور من 6 أحرف على الأقل", - "passwordUpdated": "تم تحديث كلمة المرور بنجاح", - "passwordUpdateFailed": "فشل تحديث كلمة المرور", - "deletePasswordRequired": "كلمة المرور مطلوبة لحذف حسابك", - "deleteFailed": "فشل في حذف الحساب", - "deleting": "حذف...", - "colorPickerTooltip": "فتح منتقي الألوان", - "themeSystem": "النظام", - "themeLight": "فاتح", - "themeDark": "داكن", - "themeDracula": "دراكولا", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "متشمس", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "داكن واحد", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/bg_BG.json b/src/ui/locales/translated/bg_BG.json index c1aec9ce..9bc5af32 100644 --- a/src/ui/locales/translated/bg_BG.json +++ b/src/ui/locales/translated/bg_BG.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Папки", - "folder": "Папка", - "password": "Парола", - "key": "Ключ", - "sshPrivateKey": "SSH частен ключ", - "upload": "Качване", - "keyPassword": "Ключова парола", - "sshKey": "SSH ключ", - "uploadPrivateKeyFile": "Качване на файл с частен ключ", - "searchCredentials": "Търсене на идентификационни данни...", - "addCredential": "Добавяне на идентификационни данни", - "caCertificate": "Сертификат на CA (-cert.pub)", - "caCertificateDescription": "По избор: Качете или поставете файла със сертификат, подписан от CA (напр. id_ed25519-cert.pub). Задължително, когато вашият SSH сървър използва оторизация, базирана на сертификат.", - "uploadCertFile": "Качване на файл -cert.pub", - "clearCert": "Изчисти", - "certLoaded": "Сертификатът е зареден", - "certPublicKeyLabel": "Сертификат за CA", - "certTypeLabel": "Тип сертификат", - "pasteOrUploadCert": "Поставете или качете сертификат -cert.pub...", - "hasCaCert": "Има CA сертификат", - "noCaCert": "Няма сертификат за CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Зареждането на известията не бе успешно", - "failedToDismissAlert": "Отхвърлянето на предупреждението не бе успешно" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Конфигурация на сървъра", - "description": "Конфигурирайте URL адреса на Termix сървъра, за да се свържете с вашите backend услуги", - "serverUrl": "URL адрес на сървъра", - "enterServerUrl": "Моля, въведете URL адрес на сървъра", - "saveFailed": "Запазването на конфигурацията не бе успешно", - "saveError": "Грешка при запазване на конфигурацията", - "saving": "Запазване...", - "saveConfig": "Запазване на конфигурацията", - "helpText": "Въведете URL адреса, където работи вашият Termix сървър (напр. http://localhost:30001 или https://your-server.com)", - "changeServer": "Промяна на сървъра", - "mustIncludeProtocol": "URL адресът на сървъра трябва да започва с http:// или https://", - "allowInvalidCertificate": "Разрешаване на невалиден сертификат", - "allowInvalidCertificateDesc": "Използвайте само за надеждни самостоятелно хоствани сървъри със самоподписани или IP-адресни сертификати.", - "useEmbedded": "Използване на локален сървър", - "embeddedDesc": "Стартирайте Termix с вградения локален сървър (не е необходим отдалечен сървър)", - "embeddedConnecting": "Свързване с локален сървър...", - "embeddedNotReady": "Локалният сървър все още не е готов. Моля, изчакайте малко и опитайте отново.", - "localServer": "Локален сървър" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Грешка при проверка на версията", - "checkFailed": "Проверката за актуализации не бе успешна", - "upToDate": "Приложението е актуално", - "currentVersion": "Използвате версия {{version}}", - "updateAvailable": "Налична е актуализация", - "newVersionAvailable": "Налична е нова версия! Използвате {{current}}, но {{latest}} е налична.", - "betaVersion": "Бета версия", - "betaVersionDesc": "Изпълнявате {{current}}, която е по-нова от последната стабилна версия {{latest}}.", - "releasedOn": "Публикувано на {{date}}", - "downloadUpdate": "Изтегляне на актуализация", - "checking": "Проверка за актуализации...", - "checkUpdates": "Проверка за актуализации", - "checkingUpdates": "Проверка за актуализации...", - "updateRequired": "Необходима е актуализация" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Затвори", - "minimize": "Минимизиране", - "online": "Онлайн", - "offline": "Офлайн", - "continue": "Продължи", - "maintenance": "Поддръжка", - "degraded": "Деградирал", - "error": "Грешка", - "warning": "Предупреждение", - "unsavedChanges": "Незапазени промени", - "dismiss": "Отхвърляне", - "loading": "Зареждане...", - "optional": "По избор", - "connect": "Свържете се", - "copied": "Копирано", - "connecting": "Свързване...", - "updateAvailable": "Налична е актуализация", - "appName": "Термикс", - "openInNewTab": "Отваряне в нов раздел", - "noReleases": "Няма издания", - "updatesAndReleases": "Актуализации и издания", - "newVersionAvailable": "Налична е нова версия ({{version}}).", - "failedToFetchUpdateInfo": "Неуспешно извличане на информация за актуализацията", - "preRelease": "Предварително издание", - "noReleasesFound": "Не са намерени издания.", - "cancel": "Отказ", - "username": "Потребителско име", - "login": "Вход", - "register": "Регистрация", - "password": "Парола", - "confirmPassword": "Потвърдете паролата", - "back": "Обратно", - "save": "Запазване", - "saving": "Запазване...", - "delete": "Изтриване", - "rename": "Преименуване", - "edit": "Редактиране", - "add": "Добавяне", - "confirm": "Потвърди", - "no": "Не", - "or": "ИЛИ", - "next": "Следващо", - "previous": "Предишен", - "refresh": "Обновяване", - "language": "Език", - "checking": "Проверка...", - "checkingDatabase": "Проверка на връзката с базата данни...", - "checkingAuthentication": "Проверка на удостоверяването...", - "backendReconnected": "Връзката със сървъра е възстановена", - "connectionDegraded": "Връзката със сървъра е прекъсната, възстановяване…", - "reload": "Презареждане", - "remove": "Премахване", - "create": "Създаване", - "update": "Актуализация", - "copy": "Копиране", - "copyFailed": "Копирането в буферната памет не бе успешно", - "maximize": "Максимизиране", - "restore": "Възстановяване", - "of": "от" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Дом", - "terminal": "Терминал", - "docker": "Докер", - "tunnels": "Тунели", - "fileManager": "Файлов мениджър", - "serverStats": "Статистика на сървъра", - "admin": "Администратор", - "userProfile": "Потребителски профил", - "splitScreen": "Разделен екран", - "confirmClose": "Да се затвори ли тази активна сесия?", - "close": "Затвори", - "cancel": "Отказ", - "sshManager": "SSH мениджър", - "cannotSplitTab": "Този раздел не може да бъде разделен", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Копиране на парола", - "copySudoPassword": "Копиране на парола за Sudo", - "passwordCopied": "Паролата е копирана в клипборда", - "noPasswordAvailable": "Няма налична парола", - "failedToCopyPassword": "Копирането на паролата не бе успешно", - "refreshTab": "Обновяване на връзката", - "openFileManager": "Отворете файловия мениджър", - "dashboard": "Табло за управление", - "networkGraph": "Мрежова графика", - "quickConnect": "Бързо свързване", - "sshTools": "SSH инструменти", - "history": "История", - "hosts": "Домакини", - "snippets": "Откъси", - "hostManager": "Мениджър на домакини", - "credentials": "Пълномощия", - "connections": "Връзки", - "roleAdministrator": "Администратор", - "roleUser": "Потребител" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Домакини", - "noHosts": "Няма SSH хостове", - "retry": "Опитай отново", - "refresh": "Обновяване", - "optional": "По избор", - "downloadSample": "Изтегляне на пример", - "failedToDeleteHost": "Неуспешно изтриване на {{name}}", - "importSkipExisting": "Импортиране (пропускане на съществуващото)", - "connectionDetails": "Детайли за връзката", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", - "telnet": "Телнет", - "remoteDesktop": "Отдалечен работен плот", - "port": "Порт", - "username": "Потребителско име", - "folder": "Папка", - "tags": "Етикети", - "pin": "Закачи", - "addHost": "Добавяне на хост", - "editHost": "Редактиране на хост", - "cloneHost": "Клониране на хост", - "enableTerminal": "Активиране на терминала", - "enableTunnel": "Активиране на тунел", - "enableFileManager": "Активиране на файловия мениджър", - "enableDocker": "Активиране на Докер", - "defaultPath": "Път по подразбиране", - "connection": "Връзка", - "upload": "Качване", - "authentication": "Удостоверяване", - "password": "Парола", - "key": "Ключ", - "credential": "Пълномощия", - "none": "Няма", - "sshPrivateKey": "SSH частен ключ", - "keyType": "Тип ключ", - "uploadFile": "Качване на файл", - "tabGeneral": "Общи", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", - "tabRdp": "ПРСР", + "tabTerminal": "Terminal", + "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Тунели", - "tabDocker": "Докер", - "tabFiles": "Файлове", - "tabStats": "Статистика", - "tabTelnet": "Телнет", - "tabSharing": "Споделяне", - "tabAuthentication": "Удостоверяване", - "terminal": "Терминал", - "tunnel": "Тунел", - "fileManager": "Файлов мениджър", - "serverStats": "Статистика на сървъра", - "status": "Статус", - "folderRenamed": "Папка „{{oldName}}“ е преименувана успешно на „{{newName}}“", - "failedToRenameFolder": "Преименуването на папката не бе успешно", - "movedToFolder": "Преместено в „{{folder}}“", - "editHostTooltip": "Редактиране на хоста", - "statusChecks": "Проверки на състоянието", - "metricsCollection": "Колекция от показатели", - "metricsInterval": "Интервал на събиране на показатели", - "metricsIntervalDesc": "Колко често да се събира статистика за сървъра (5 секунди - 1 час)", - "behavior": "Поведение", - "themePreview": "Преглед на темата", - "theme": "Тема", - "fontFamily": "Семейство шрифтове", - "fontSize": "Размер на шрифта", - "letterSpacing": "Разстояние между буквите", - "lineHeight": "Височина на реда", - "cursorStyle": "Стил на курсора", - "cursorBlink": "Мигане на курсора", - "scrollbackBuffer": "Буфер за превъртане назад", - "bellStyle": "Стил на камбаната", - "rightClickSelectsWord": "Щракване с десен бутон Избира дума", - "fastScrollModifier": "Модификатор за бързо превъртане", - "fastScrollSensitivity": "Чувствителност на бързото превъртане", - "sshAgentForwarding": "Пренасочване на SSH агент", - "backspaceMode": "Режим на връщане назад", - "startupSnippet": "Стартов фрагмент", - "selectSnippet": "Изберете фрагмент", - "forceKeyboardInteractive": "Принудително интерактивно с клавиатура", - "overrideCredentialUsername": "Замяна на потребителско име за идентификационни данни", - "overrideCredentialUsernameDesc": "Използвайте посоченото по-горе потребителско име вместо потребителското име от идентификационните данни", - "jumpHostChain": "Верига за прескачане на хост", - "portKnocking": "Порт Нокинг", - "addKnock": "Добавяне на порт", - "addProxyNode": "Добавяне на възел", - "proxyNode": "Прокси възел", - "proxyType": "Тип прокси", - "quickActions": "Бързи действия", - "sudoPasswordAutoFill": "Автоматично попълване на парола за Sudo", - "sudoPassword": "Парола за Sudo", - "keepaliveInterval": "Интервал на поддържане на активността (ms)", - "moshCommand": "MOSH команда", - "environmentVariables": "Променливи на средата", - "addVariable": "Добавяне на променлива", - "docker": "Докер", - "copyTerminalUrl": "Копиране на URL адреса на терминала", - "copyFileManagerUrl": "Копиране на URL адреса на файловия мениджър", - "copyRemoteDesktopUrl": "Копиране на URL адреса на отдалечен работен плот", - "failedToConnect": "Неуспешно свързване с конзолата", - "connect": "Свържете се", - "disconnect": "Изключване", - "start": "Старт", - "enableStatusCheck": "Активиране на проверка на състоянието", - "enableMetrics": "Активиране на показатели", - "bulkUpdateFailed": "Груповата актуализация не бе успешна", - "selectAll": "Избери всички", - "deselectAll": "Премахни избора от всички", - "protocols": "Протоколи", - "secureShell": "Защитена обвивка", - "virtualNetwork": "Виртуална мрежа", - "unencryptedShell": "Некриптирана обвивка", - "addressIp": "Адрес / IP адрес", - "friendlyName": "Приятелско име", - "folderAndAdvanced": "Папка и разширени", - "privateNotes": "Лични бележки", - "privateNotesPlaceholder": "Подробности за този сървър...", - "pinToTop": "Закачи горе", - "pinToTopDesc": "Винаги показвай този хост в началото на списъка", - "portKnockingSequence": "Последователност на детонацията на порта", - "addKnockBtn": "Добави Knock", - "noPortKnocking": "Не е конфигурирано „порт детониране“.", - "knockPort": "Нок Порт", - "protocol": "Протокол", - "delayAfterMs": "Закъснение след (ms)", - "useSocks5Proxy": "Използвайте SOCKS5 прокси", - "useSocks5ProxyDesc": "Маршрутизиране на връзката през прокси сървър", - "proxyHost": "Прокси хост", - "proxyPort": "Прокси порт", - "proxyUsername": "Потребителско име на прокси", - "proxyPassword": "Парола за прокси сървър", - "proxySingleMode": "Единичен прокси", - "proxyChainMode": "Прокси верига", - "you": "Ти", - "jumpHostChainLabel": "Верига за прескачане на хост", - "addJumpBtn": "Добавяне на скок", - "noJumpHosts": "Няма конфигурирани хостове за преходи.", - "selectAServer": "Изберете сървър...", - "sshPort": "SSH порт", - "authMethod": "Метод за удостоверяване", - "storedCredential": "Съхранени идентификационни данни", - "selectACredential": "Изберете идентификационен номер...", - "keyTypeLabel": "Тип ключ", - "keyTypeAuto": "Автоматично откриване", - "keyPasteTab": "Паста", - "keyUploadTab": "Качване", - "keyFileLoaded": "Ключовият файл е зареден", - "keyUploadClick": "Кликнете, за да качите .pem / .key / .ppk", - "clearKey": "Клавиш за изчистване", - "keySaved": "SSH ключът е запазен", - "keyReplaceNotice": "поставете нов ключ по-долу, за да го замените", - "keyPassphraseSaved": "Паролата е запазена, въведете, за да я промените", - "replaceKey": "Сменете ключа", - "forceKeyboardInteractiveLabel": "Принудителна интерактивна клавиатура", - "forceKeyboardInteractiveShortDesc": "Принудително въвеждане на парола ръчно, дори ако са налични ключове", - "terminalAppearance": "Външен вид на терминала", - "colorTheme": "Цветова тема", - "fontFamilyLabel": "Семейство шрифтове", - "fontSizeLabel": "Размер на шрифта", - "cursorStyleLabel": "Стил на курсора", - "letterSpacingPx": "Разстояние между буквите (px)", - "lineHeightLabel": "Височина на реда", - "bellStyleLabel": "Стил на камбаната", - "backspaceModeLabel": "Режим на връщане назад", - "cursorBlinking": "Мигане на курсора", - "cursorBlinkingDesc": "Активиране на мигаща анимация за курсора на терминала", - "rightClickSelectsWordLabel": "Щракване с десен бутон Избира дума", - "rightClickSelectsWordShortDesc": "Изберете думата под курсора при щракване с десния бутон", - "behaviorAndAdvanced": "Поведение и напреднали", - "scrollbackBufferLabel": "Буфер за превъртане назад", - "scrollbackMaxLines": "Максимален брой редове, съхранявани в историята", - "sshAgentForwardingLabel": "Пренасочване на SSH агент", - "sshAgentForwardingShortDesc": "Предайте локалните си SSH ключове на този хост", - "enableAutoMosh": "Активиране на Auto-Mosh", - "enableAutoMoshDesc": "Предпочитайте Mosh пред SSH, ако е наличен", - "enableAutoTmux": "Активиране на Auto-Tmux", - "enableAutoTmuxDesc": "Автоматично стартиране или прикачване към tmux сесия", - "sudoPasswordAutoFillLabel": "Автоматично попълване на парола за Sudo", - "sudoPasswordAutoFillShortDesc": "Автоматично предоставяне на парола за sudo при подкана", - "sudoPasswordLabel": "Парола за Sudo", - "environmentVariablesLabel": "Променливи на средата", - "addVariableBtn": "Добавяне на променлива", - "noEnvVars": "Няма конфигурирани променливи на средата.", - "fastScrollModifierLabel": "Модификатор за бързо превъртане", - "fastScrollSensitivityLabel": "Чувствителност на бързото превъртане", - "moshCommandLabel": "Команда Мош", - "startupSnippetLabel": "Стартов фрагмент", - "keepaliveIntervalLabel": "Интервал за поддържане на активност (секунди)", - "maxKeepaliveMisses": "Максимални пропуски на Keepalive", - "tunnelSettings": "Настройки на тунела", - "enableTunneling": "Активиране на тунелиране", - "enableTunnelingDesc": "Активиране на SSH тунелната функционалност за този хост", - "serverTunnelsSection": "Сървърни тунели", - "addTunnelBtn": "Добавяне на тунел", - "noTunnelsConfigured": "Няма конфигурирани тунели.", - "tunnelLabel": "Тунел {{number}}", - "tunnelType": "Тип тунел", - "tunnelModeLocalDesc": "Пренасочване на локален порт към порт на отдалечения сървър (или хост, достъпен от него).", - "tunnelModeRemoteDesc": "Пренасочете порт на отдалечения сървър обратно към локален порт на вашата машина.", - "tunnelModeDynamicDesc": "Създайте SOCKS5 прокси на локален порт за динамично пренасочване на портове.", - "sameHost": "Този хост (директен тунел)", - "endpointHost": "Краен хост", - "endpointPort": "Порт за крайна точка", - "bindHost": "Свързване на хост", - "sourcePort": "Източник Порт", - "maxRetries": "Максимален брой повторни опити", - "retryIntervalS": "Интервал на повторен опит (и)", - "autoStartLabel": "Автоматично стартиране", - "autoStartDesc": "Автоматично свързване на този тунел при зареждане на хоста", - "tunnelConnecting": "Тунел, свързващ...", - "tunnelDisconnected": "Тунелът е прекъснат", - "failedToConnectTunnel": "Свързването не бе успешно", - "failedToDisconnectTunnel": "Прекъсването не бе успешно", - "dockerIntegration": "Интеграция на Docker", - "enableDockerMonitor": "Активиране на Докер", - "enableDockerMonitorDesc": "Наблюдавайте и управлявайте контейнери на този хост чрез Docker", - "enableFileManagerMonitor": "Активиране на файловия мениджър", - "enableFileManagerMonitorDesc": "Преглеждайте и управлявайте файлове на този хост през SFTP", - "defaultPathLabel": "Път по подразбиране", - "fileManagerPathHint": "Директорията, която да се отвори при стартиране на файловия мениджър за този хост.", - "statusChecksLabel": "Проверки на състоянието", - "enableStatusChecks": "Активиране на проверки на състоянието", - "enableStatusChecksDesc": "Периодично проверявайте наличността на този хост чрез ping.", - "useGlobalInterval": "Използване на глобален интервал", - "useGlobalIntervalDesc": "Замяна с интервала за проверка на състоянието на целия сървър", - "checkIntervalS": "Интервал(и) на проверка", - "checkIntervalDesc": "Секунди между всеки ping за свързване", - "metricsCollectionLabel": "Колекция от показатели", - "enableMetricsLabel": "Активиране на показатели", - "enableMetricsDesc": "Събиране на данни за използването на процесора, RAM, диска и мрежата от този хост", - "useGlobalMetrics": "Използване на глобален интервал", - "useGlobalMetricsDesc": "Замяна с интервала на показателите за целия сървър", - "metricsIntervalS": "Интервал(и) на показателите", - "metricsIntervalDesc2": "Секунди между моментните снимки на показателите", - "visibleWidgets": "Видими джаджи", - "cpuUsageLabel": "Използване на процесора", - "cpuUsageDesc": "Процент на процесора, средни стойности на натоварването, sparkline графика", - "memoryLabel": "Използване на паметта", - "memoryDesc": "Използване на RAM, суап, кеширана памет", - "storageLabel": "Използване на диска", - "storageDesc": "Използване на дисково пространство за точка на монтиране", - "networkLabel": "Мрежови интерфейси", - "networkDesc": "Списък с интерфейси и честотна лента", - "uptimeLabel": "Време на работа", - "uptimeDesc": "Време за работа на системата и време за зареждане", - "systemInfoLabel": "Информация за системата", - "systemInfoDesc": "ОС, ядро, име на хост, архитектура", - "recentLoginsLabel": "Последни влизания", - "recentLoginsDesc": "Успешни и неуспешни събития за влизане", - "topProcessesLabel": "Най-важни процеси", - "topProcessesDesc": "PID, CPU%, MEM%, команда", - "listeningPortsLabel": "Портове за слушане", - "listeningPortsDesc": "Отворени портове с процес и състояние", - "firewallLabel": "Защитна стена", - "firewallDesc": "Състояние на защитната стена, AppArmor, SELinux", - "quickActionsLabel": "Бързи действия", - "quickActionsToolbar": "Бързите действия се показват като бутони в лентата с инструменти „Статистика на сървъра“ за изпълнение на команди с едно щракване.", - "noQuickActions": "Все още няма бързи действия.", - "buttonLabel": "Етикет на бутона", - "selectSnippetPlaceholder": "Изберете фрагмент...", - "addActionBtn": "Добавяне на действие", - "hostSharedSuccessfully": "Хостът е споделен успешно", - "failedToShareHost": "Споделянето на хоста не бе успешно", - "accessRevoked": "Достъпът е отменен", - "failedToRevokeAccess": "Неуспешно отменяне на достъпа", - "cancelBtn": "Отказ", - "savingBtn": "Запазване...", - "addHostBtn": "Добавяне на хост", - "hostUpdated": "Хостът е актуализиран", - "hostCreated": "Хостът е създаден", - "failedToSave": "Запазването на хоста не бе успешно", - "credentialUpdated": "Пълномощията са актуализирани", - "credentialCreated": "Идентификационните данни са създадени", - "failedToSaveCredential": "Запазването на идентификационните данни не бе успешно", - "backToHosts": "Обратно към Домакини", - "backToCredentials": "Обратно към удостоверенията", - "pinned": "Закачено", - "noHostsFound": "Не са намерени хостове", - "tryDifferentTerm": "Опитайте с друг термин", - "addFirstHost": "Добавете първия си хост, за да започнете", - "noCredentialsFound": "Не са намерени идентификационни данни", - "addCredentialBtn": "Добавяне на идентификационни данни", - "updateCredentialBtn": "Актуализиране на идентификационните данни", - "features": "Характеристики", - "noFolder": "(Няма папка)", - "deleteSelected": "Изтриване", - "exitSelection": "Изход от селекцията", - "importSkip": "Импортиране (пропускане на съществуващото)", - "importOverwrite": "Импортиране (презаписване)", - "collapseBtn": "Свиване", - "importExportBtn": "Внос / Износ", - "hostStatusesRefreshed": "Състоянията на хоста са обновени", - "failedToRefreshHosts": "Неуспешно обновяване на хостовете", - "movedHostTo": "Преместено {{host}} в „{{folder}}“", - "failedToMoveHost": "Преместването на хоста не бе успешно", - "folderRenamedTo": "Папката е преименувана на „{{name}}“", - "deletedFolder": "Изтрита папка „{{name}}“", - "failedToDeleteFolder": "Изтриването на папката не бе успешно", - "deleteAllInFolder": "Да се изтрият ли всички хостове в „{{name}}“? Това не може да се отмени.", - "deletedHost": "Изтрито {{name}}", - "copiedToClipboard": "Копирано в буферната памет", - "terminalUrlCopied": "URL адресът на терминала е копиран", - "fileManagerUrlCopied": "URL адресът на файловия мениджър е копиран", - "tunnelUrlCopied": "URL адресът на тунела е копиран", - "dockerUrlCopied": "URL адресът на Docker е копиран", - "serverStatsUrlCopied": "URL адресът за статистика на сървъра е копиран", - "rdpUrlCopied": "RDP URL адресът е копиран", - "vncUrlCopied": "VNC URL адресът е копиран", - "telnetUrlCopied": "URL адресът на Telnet е копиран", - "remoteDesktopUrlCopied": "URL адресът на отдалечен работен плот е копиран", - "expandActions": "Разгъване на действията", - "collapseActions": "Свиване на действията", - "wakeOnLanAction": "Събуждане по LAN", - "wakeOnLanSuccess": "Магически пакет, изпратен до {{name}}", - "wakeOnLanError": "Изпращането на магически пакет не бе успешно", - "cloneHostAction": "Клониране на хост", - "copyAddress": "Копиране на адрес", - "copyLink": "Копиране на връзката", - "copyTerminalUrlAction": "Копиране на URL адреса на терминала", - "copyFileManagerUrlAction": "Копиране на URL адреса на файловия мениджър", - "copyTunnelUrlAction": "Копиране на URL адреса на тунела", - "copyDockerUrlAction": "Копиране на URL адреса на Docker", - "copyServerStatsUrlAction": "Копиране на URL адреса за статистика на сървъра", - "copyRdpUrlAction": "Копиране на RDP URL адрес", - "copyVncUrlAction": "Копиране на VNC URL адрес", - "copyTelnetUrlAction": "Копиране на Telnet URL адрес", - "copyRemoteDesktopUrlAction": "Копиране на URL адреса на отдалечен работен плот", - "deleteCredentialConfirm": "Изтриване на идентификационните данни „{{name}}“?", - "deletedCredential": "Изтрито {{name}}", - "deploySSHKeyTitle": "Разполагане на SSH ключ", - "deployingBtn": "Разгръщане...", - "deployBtn": "Разгръщане", - "failedToDeployKey": "Разгръщането на ключа не бе успешно", - "deleteHostsConfirm": "Изтриване на {{count}} хост{{plural}}? Това не може да бъде отменено.", - "movedToRoot": "Преместено в корена", - "failedToMoveHosts": "Преместването на хостовете не бе успешно", - "enableTerminalFeature": "Активиране на терминала", - "disableTerminalFeature": "Деактивиране на терминала", - "enableFilesFeature": "Активиране на файлове", - "disableFilesFeature": "Деактивиране на файлове", - "enableTunnelsFeature": "Активиране на тунели", - "disableTunnelsFeature": "Деактивиране на тунелите", - "enableDockerFeature": "Активиране на Докер", - "disableDockerFeature": "Деактивиране на Докер", - "addTagsPlaceholder": "Добавяне на етикети...", - "authDetails": "Детайли за удостоверяване", - "credType": "Тип", - "generateKeyPairDesc": "Генерирайте нова двойка ключове, като както частните, така и публичните ключове ще бъдат попълнени автоматично.", - "generatingKey": "Генериране...", - "generateLabel": "Генериране {{label}}", - "uploadFileBtn": "Качване на файл", - "keyPassphraseOptional": "Ключова парола (по избор)", - "sshPublicKeyOptional": "SSH публичен ключ (по избор)", - "publicKeyGenerated": "Генериран публичен ключ", - "failedToGeneratePublicKey": "Неуспешно извличане на публичен ключ", - "publicKeyCopied": "Публичният ключ е копиран", - "keyPairGenerated": "{{label}} генерирана двойка ключове", - "failedToGenerateKeyPair": "Неуспешно генериране на двойка ключове", - "searchHostsPlaceholder": "Търсене на хостове, адреси, тагове…", - "searchCredentialsPlaceholder": "Търсене на идентификационни данни…", - "refreshBtn": "Обновяване", - "addTag": "Добавяне на етикети...", - "deleteConfirmBtn": "Изтриване", - "tunnelRequirementsText": "SSH сървърът трябва да има зададени стойности за GatewayPorts (да), AllowTcpForwarding (да) и PermitRootLogin (да) в /etc/ssh/sshd_config.", - "deleteHostConfirm": "Да се изтрие ли „{{name}}“?", - "enableAtLeastOneProtocol": "Активирайте поне един протокол по-горе, за да конфигурирате настройките за удостоверяване и връзка.", - "keyPassphrase": "Ключова парола", - "connectBtn": "Свържете се", - "disconnectBtn": "Изключване", - "basicInformation": "Основна информация", - "authDetailsSection": "Детайли за удостоверяване", - "credTypeLabel": "Тип", - "hostsTab": "Домакини", - "credentialsTab": "Пълномощия", - "selectMultiple": "Изберете няколко", - "selectHosts": "Изберете хостове", - "connectionLabel": "Връзка", - "authenticationLabel": "Удостоверяване", - "generateKeyPairTitle": "Генериране на двойка ключове", - "generateKeyPairDescription": "Генерирайте нова двойка ключове, като както частните, така и публичните ключове ще бъдат попълнени автоматично.", - "generateFromPrivateKey": "Генериране от частен ключ", - "refreshBtn2": "Обновяване", - "exitSelectionTitle": "Изход от селекцията", - "exportAll": "Експортиране на всички", - "addHostBtn2": "Добавяне на хост", - "addCredentialBtn2": "Добавяне на идентификационни данни", - "checkingHostStatuses": "Проверка на състоянието на хоста...", - "pinnedSection": "Закачено", - "hostsExported": "Хостовете са експортирани успешно", - "exportFailed": "Експортирането на хостове не бе успешно", - "sampleDownloaded": "Примерен файл е изтеглен", - "failedToDeleteCredential2": "Неуспешно изтриване на идентификационните данни", - "noFolderOption": "(Няма папка)", - "nSelected": "{{count}} избрано", - "featuresMenu": "Характеристики", - "moveMenu": "Преместване", - "cancelSelection": "Отказ", - "deployDialogDesc": "Разположи {{name}} в authorized_keys на хоста.", - "targetHostLabel": "Целевият хост", - "selectHostOption": "Изберете хост...", - "keyDeployedSuccess": "Ключът е внедрен успешно", - "failedToDeployKey2": "Разгръщането на ключа не бе успешно", - "deletedCount": "Изтрити {{count}} хостове", - "failedToDeleteCount": "Неуспешно изтриване на {{count}} хостове", - "duplicatedHost": "Дублирано „{{name}}“", - "failedToDuplicateHost": "Дублирането на хоста не бе успешно", - "updatedCount": "Актуализирани {{count}} хостове", - "friendlyNameLabel": "Приятелско име", - "descriptionLabel": "Описание", - "loadingHost": "Зареждане на хоста...", - "loadingHosts": "Зареждане на хостове...", - "loadingCredentials": "Зареждане на идентификационни данни...", - "noHostsYet": "Все още няма домакини", - "noHostsMatchSearch": "Няма хостове, които да отговарят на вашето търсене", - "hostNotFound": "Хостът не е намерен", - "searchHosts": "Търсене на хостове...", - "sortHosts": "Сортиране на хостове", - "sortDefault": "По подразбиране ред", - "sortNameAsc": "Име (А → Я)", - "sortNameDesc": "Име (Я → А)", - "sortIpAsc": "IP адрес (възходящ)", - "sortIpDesc": "IP адрес (описание)", - "sortOnlineFirst": "Първо онлайн", - "sortOfflineFirst": "Първо офлайн", - "sortPinnedFirst": "Първо закачено", - "filterHosts": "Филтриране на хостове", - "filterClearAll": "Изчистване на филтрите", - "filterStatusGroup": "Статус", - "filterOnline": "Онлайн", - "filterOffline": "Офлайн", - "filterPinned": "Закачено", - "filterAuthGroup": "Тип оторизация", - "filterAuthPassword": "Парола", - "filterAuthKey": "SSH ключ", - "filterAuthCredential": "Пълномощия", - "filterAuthNone": "Няма", - "filterAuthOpkssh": "ОПКСШ", - "filterProtocolGroup": "Протокол", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "ПРСР", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Телнет", - "filterFeaturesGroup": "Характеристики", - "filterFeatureTerminal": "Терминал", - "filterFeatureFileManager": "Файлов мениджър", - "filterFeatureTunnel": "Тунел", - "filterFeatureDocker": "Докер", - "filterTagsGroup": "Етикети", - "shareHost": "Споделяне на хост", - "shareHostTitle": "Споделяне: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Този хост трябва да използва идентификационни данни, за да активира споделянето. Първо редактирайте хоста и задайте идентификационни данни." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Връзка", - "authentication": "Удостоверяване", - "connectionSettings": "Настройки за връзка", - "displaySettings": "Настройки на дисплея", - "audioSettings": "Аудио настройки", - "rdpPerformance": "Производителност на RDP", - "deviceRedirection": "Пренасочване на устройството", - "session": "Сесия", - "gateway": "Портал", - "remoteApp": "Отдалечено приложение", - "clipboard": "Клипборд", - "sessionRecording": "Запис на сесия", - "wakeOnLan": "Събуждане по локална мрежа", - "vncSettings": "VNC настройки", - "terminalSettings": "Настройки на терминала", - "rdpPort": "RDP порт", - "username": "Потребителско име", - "password": "Парола", - "domain": "Домейн", - "securityMode": "Режим на сигурност", - "colorDepth": "Дълбочина на цвета", - "width": "Ширина", - "height": "Височина", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Метод за преоразмеряване", - "clientName": "Име на клиента", - "initialProgram": "Първоначална програма", - "serverLayout": "Разположение на сървъра", - "timezone": "Часова зона", - "gatewayHostname": "Име на хост на шлюз", - "gatewayPort": "Порт на шлюза", - "gatewayUsername": "Потребителско име на шлюз", - "gatewayPassword": "Парола за шлюз", - "gatewayDomain": "Домейн на шлюз", - "remoteAppProgram": "Програма за отдалечено приложение", - "workingDirectory": "Работна директория", - "arguments": "Аргументи", - "normalizeLineEndings": "Нормализиране на края на редовете", - "recordingPath": "Път на запис", - "recordingName": "Име на записа", - "macAddress": "MAC адрес", - "broadcastAddress": "Адрес за излъчване", - "udpPort": "UDP порт", - "waitTimeS": "Време за изчакване (и)", - "driveName": "Име на устройството", - "drivePath": "Път на устройството", - "ignoreCertificate": "Игнориране на сертификата", - "ignoreCertificateDesc": "Разрешаване на връзки към хостове със самоподписани сертификати", - "forceLossless": "Принудително беззагубно", - "forceLosslessDesc": "Принудително кодиране на изображения без загуби (по-високо качество, по-голяма честотна лента)", - "disableAudio": "Деактивиране на звука", - "disableAudioDesc": "Изключване на звука от отдалечената сесия", - "enableAudioInput": "Активиране на аудио вход (микрофон)", - "enableAudioInputDesc": "Пренасочване на локалния микрофон към отдалечената сесия", - "wallpaper": "Тапет", - "wallpaperDesc": "Показване на тапет на работния плот (деактивирането му подобрява производителността)", - "theming": "Темиране", - "themingDesc": "Активиране на визуални теми и стилове", - "fontSmoothing": "Изглаждане на шрифта", - "fontSmoothingDesc": "Активиране на рендирането на шрифтове ClearType", - "fullWindowDrag": "Плъзгане на целия прозорец", - "fullWindowDragDesc": "Показване на съдържанието на прозореца при плъзгане", - "desktopComposition": "Композиция на работния плот", - "desktopCompositionDesc": "Активиране на ефектите на Aero Glass", - "menuAnimations": "Анимации на менюто", - "menuAnimationsDesc": "Активиране на анимации за избледняване на менюта и слайдове", - "disableBitmapCaching": "Деактивиране на кеширането на растерни изображения", - "disableBitmapCachingDesc": "Изключете кеша на растерните изображения (може да помогне с проблеми)", - "disableOffscreenCaching": "Деактивиране на кеширането извън екрана", - "disableOffscreenCachingDesc": "Изключване на кеша извън екрана", - "disableGlyphCaching": "Деактивиране на кеширането на глифи", - "disableGlyphCachingDesc": "Изключване на кеша на глифове", - "enableGfx": "Активиране на графични ефекти", - "enableGfxDesc": "Използвайте графичен конвейер RemoteFX", - "enablePrinting": "Активиране на печат", - "enablePrintingDesc": "Пренасочване на локални принтери към отдалечена сесия", - "enableDriveRedirection": "Активиране на пренасочване на устройството", - "enableDriveRedirectionDesc": "Карта на локална папка като устройство в отдалечена сесия", - "createDrivePath": "Създаване на път за устройство", - "createDrivePathDesc": "Автоматично създаване на папката, ако тя не съществува", - "disableDownload": "Деактивиране на изтеглянето", - "disableDownloadDesc": "Предотвратяване на изтеглянето на файлове от отдалечената сесия", - "disableUpload": "Деактивиране на качването", - "disableUploadDesc": "Предотвратяване на качването на файлове към отдалечената сесия", - "enableTouch": "Активиране на докосване", - "enableTouchDesc": "Активиране на пренасочване на сензорен вход", - "consoleSession": "Конзолна сесия", - "consoleSessionDesc": "Свържете се с конзолата (сесия 0) вместо с нова сесия", - "sendWolPacket": "Изпращане на WOL пакет", - "sendWolPacketDesc": "Изпратете магически пакет, за да събудите този хост преди свързване", - "disableCopy": "Деактивиране на копирането", - "disableCopyDesc": "Предотвратяване на копирането на текст от отдалечената сесия", - "disablePaste": "Деактивиране на поставянето", - "disablePasteDesc": "Предотвратяване на поставянето на текст в отдалечената сесия", - "createPathIfMissing": "Създаване на път, ако липсва", - "createPathIfMissingDesc": "Автоматично създаване на директорията за записи", - "excludeOutput": "Изключване на изхода", - "excludeOutputDesc": "Не записвайте изхода на екрана (само метаданни)", - "excludeMouse": "Изключване на мишката", - "excludeMouseDesc": "Не записвайте движенията на мишката", - "includeKeystrokes": "Включи клавишни комбинации", - "includeKeystrokesDesc": "Записвайте сурови натискания на клавиши в допълнение към изхода на екрана", - "vncPort": "VNC порт", - "vncPassword": "VNC парола", - "vncUsernameOptional": "Потребителско име (по избор)", - "vncLeaveBlank": "Оставете празно, ако не е необходимо", - "cursorMode": "Режим на курсора", - "swapRedBlue": "Разменете червено/синьо", - "swapRedBlueDesc": "Разменете червените и сините цветови канали (коригира някои проблеми с цветовете)", - "readOnly": "Само за четене", - "readOnlyDesc": "Преглед на отдалечения екран без изпращане на каквито и да било входни данни", - "telnetPort": "Telnet порт", - "terminalType": "Тип терминал", - "fontName": "Име на шрифта", - "fontSize": "Размер на шрифта", - "colorScheme": "Цветова схема", - "backspaceKey": "Клавиш Backspace", - "saveHostFirst": "Първо запазете хоста.", - "sharingOptionsAfterSave": "Опциите за споделяне са налични след запазване на хоста.", - "sharingLoadError": "Зареждането на данните за споделяне не бе успешно. Проверете връзката си и опитайте отново.", - "shareHostSection": "Споделяне на хост", - "shareWithUser": "Споделяне с потребителя", - "shareWithRole": "Споделяне с роля", - "selectUser": "Изберете потребител", - "selectRole": "Изберете роля", - "selectUserOption": "Изберете потребител...", - "selectRoleOption": "Изберете роля...", - "permissionLevel": "Ниво на разрешение", - "expiresInHours": "Изтича след (часове)", - "noExpiryPlaceholder": "Оставете празно за дата без срок на валидност", - "shareBtn": "Споделяне", - "currentAccess": "Текущ достъп", - "typeHeader": "Тип", - "targetHeader": "Цел", - "permissionHeader": "Разрешение", - "grantedByHeader": "Предоставено от", - "expiresHeader": "Изтича", - "noAccessEntries": "Все още няма записи за достъп.", - "expiredLabel": "Изтекъл", - "neverLabel": "Никога", - "revokeBtn": "Отмяна", - "cancelBtn": "Отказ", - "savingBtn": "Запазване...", - "updateHostBtn": "Актуализиране на хоста", - "addHostBtn": "Добавяне на хост" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Търсене на хостове, команди или настройки...", - "quickActions": "Бързи действия", - "hostManager": "Мениджър на домакини", - "hostManagerDesc": "Управление, добавяне или редактиране на хостове", - "addNewHost": "Добавяне на нов хост", - "addNewHostDesc": "Регистрирайте нов хост", - "adminSettings": "Административни настройки", - "adminSettingsDesc": "Конфигуриране на системни предпочитания и потребители", - "userProfile": "Потребителски профил", - "userProfileDesc": "Управлявайте профила и предпочитанията си", - "addCredential": "Добавяне на идентификационни данни", - "addCredentialDesc": "Съхранявайте SSH ключове или пароли", - "recentActivity": "Последна активност", - "serversAndHosts": "Сървъри и хостове", - "noHostsFound": "Не са намерени хостове, съответстващи на „{{search}}“", - "links": "Връзки", - "navigate": "Навигиране", - "select": "Изберете", - "toggleWith": "Превключване с" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Панел {{index}} - празен", - "noTabAssigned": "Няма зададен раздел", - "focusedPane": "Активен панел" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Няма връзки", - "noConnectionsDesc": "Отворете терминал, файлов мениджър или отдалечен работен плот, за да видите връзките тук", - "connectedFor": "Свързано за {{duration}}", - "connected": "Свързан", - "disconnected": "Изключен", - "closeTab": "Затвори раздела", - "closeConnection": "Затвори връзката", - "forgetTab": "Забрави", - "removeBackground": "Премахване", - "reconnect": "Свържете се отново", - "reopenTab": "Отвори отново", - "sectionOpen": "Отворено", - "sectionBackground": "Предистория", - "backgroundDesc": "Сесиите продължават 30 минути след прекъсване на връзката и могат да бъдат свързани отново.", - "persisted": "Продължава във фонов режим", - "expiresIn": "Изтича след {{duration}}", - "search": "Търсене на връзки...", - "noSearchResults": "Няма връзки, които да отговарят на търсенето ви" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Свързване към сесия {{type}}...", - "connectionError": "Грешка при свързване", - "connectionFailed": "Връзката не бе успешна", - "failedToConnect": "Неуспешно получаване на токен за връзка", - "hostNotFound": "Хостът не е намерен", - "noHostSelected": "Няма избран хост", - "reconnect": "Свържете се отново", - "retry": "Опитай отново", - "guacdUnavailable": "Услугата за отдалечен работен плот (guacd) не е налична. Моля, уверете се, че guacd работи, е достъпна и е конфигурирана правилно в администраторските настройки.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Заключен екран)", - "winKey": "Клавиш за Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "Алтернативно", - "shift": "Изместване", - "win": "Спечелете", - "stickyActive": "{{key}} (заключено - щракнете за освобождаване)", - "stickyInactive": "{{key}} (щракнете, за да заключите)", - "esc": "Бягство", - "tab": "Раздел", - "home": "Дом", - "end": "Край", - "pageUp": "Страница нагоре", - "pageDown": "Страница надолу", - "arrowUp": "Стрелка нагоре", - "arrowDown": "Стрелка надолу", - "arrowLeft": "Стрелка наляво", - "arrowRight": "Стрелка надясно", - "fnToggle": "Функционални клавиши", - "reconnect": "Сесия за повторно свързване", - "collapse": "Свиване на лентата с инструменти", - "expand": "Разгъване на лентата с инструменти", - "dragHandle": "Плъзнете, за да препозиционирате" + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Свързване с хоста", - "clear": "Изчисти", - "paste": "Паста", - "reconnect": "Свържете се отново", - "connectionLost": "Връзката е прекъсната", - "connected": "Свързан", - "clipboardWriteFailed": "Копирането в клипборда не бе успешно. Уверете се, че страницата се предоставя през HTTPS или localhost.", - "clipboardReadFailed": "Четенето от клипборда не бе успешно. Уверете се, че са предоставени разрешения за достъп до клипборда.", - "clipboardHttpWarning": "Поставянето изисква HTTPS. Използвайте Ctrl+Shift+V или обслужвайте Termix през HTTPS.", - "unknownError": "Възникна неизвестна грешка", - "websocketError": "Грешка при свързване с WebSocket", - "connecting": "Свързване...", - "noHostSelected": "Няма избран хост", - "reconnecting": "Повторно свързване... ({{attempt}}/{{max}})", - "reconnected": "Успешно повторно свързване", - "tmuxSessionCreated": "tmux сесия, създадена: {{name}}", - "tmuxSessionAttached": "Прикачена tmux сесия: {{name}}", - "tmuxUnavailable": "tmux не е инсталиран на отдалечения хост, връща се към стандартната обвивка", - "tmuxSessionPickerTitle": "tmux сесии", - "tmuxSessionPickerDesc": "На този хост са открити съществуващи tmux сесии. Изберете една, за да я свържете отново или да създадете нова сесия.", - "tmuxWindows": "Прозорци", - "tmuxWindowCount": "{{count}} прозорец", - "tmuxAttached": "Прикачени клиенти", - "tmuxAttachedCount": "{{count}} прикачен", - "tmuxLastActivity": "Последна активност", - "tmuxTimeJustNow": "точно сега", - "tmuxTimeMinutes": "преди {{count}}мин.", - "tmuxTimeHours": "преди {{count}}ч", - "tmuxTimeDays": "преди {{count}}дни", - "tmuxCreateNew": "Започнете нова сесия", - "tmuxCopyHint": "Променете селекцията и натиснете Enter, за да я копирате в клипборда", - "tmuxDetach": "Отделяне от tmux сесия", - "tmuxDetached": "Отделен от tmux сесията", - "maxReconnectAttemptsReached": "Достигнат е максималният брой опити за повторно свързване", - "closeTab": "Затвори", - "connectionTimeout": "Време за изчакване на връзката", - "terminalTitle": "Терминал - {{host}}", - "terminalWithPath": "Терминал - {{host}}:{{path}}", - "runTitle": "Изпълнява се {{command}} - {{host}}", - "totpRequired": "Изисква се двуфакторно удостоверяване", - "totpCodeLabel": "Код за потвърждение", - "totpVerify": "Проверка", - "warpgateAuthRequired": "Изисква се удостоверяване на Warpgate", - "warpgateSecurityKey": "Ключ за сигурност", - "warpgateAuthUrl": "URL адрес за удостоверяване", - "warpgateOpenBrowser": "Отваряне в браузъра", - "warpgateContinue": "Завърших удостоверяването", - "opksshAuthRequired": "Изисква се удостоверяване на OPKSSH", - "opksshAuthDescription": "За да продължите, завършете удостоверяването в браузъра си. Тази сесия ще остане валидна 24 часа.", - "opksshOpenBrowser": "Отворете браузъра за удостоверяване", - "opksshWaitingForAuth": "Чака се удостоверяване в браузъра...", - "opksshAuthenticating": "Обработва се удостоверяване...", - "opksshTimeout": "Времето за изчакване на удостоверяването изтече. Моля, опитайте отново.", - "opksshAuthFailed": "Удостоверяването не бе успешно. Моля, проверете идентификационните си данни и опитайте отново.", - "opksshSignInWith": "Влезте с {{provider}}", - "sudoPasswordPopupTitle": "Въвеждане на парола?", - "websocketAbnormalClose": "Връзката се прекъсна неочаквано. Това може да се дължи на проблем с обратен прокси или SSL конфигурация. Моля, проверете лог файловете на сървъра.", - "connectionLogTitle": "Дневник на връзките", - "connectionLogCopy": "Копиране на лог файлове в клипборда", - "connectionLogEmpty": "Все още няма регистрационни файлове за връзка", - "connectionLogWaiting": "Чакат се регистрационни файлове за връзка...", - "connectionLogCopied": "Дневниците на връзките са копирани в клипборда", - "connectionLogCopyFailed": "Копирането на регистрационните файлове в буфера не бе успешно", - "connectionRejected": "Връзката е отхвърлена от сървъра. Моля, проверете удостоверяването и мрежовата си конфигурация.", - "hostKeyRejected": "Проверката на SSH ключа за хост е отхвърлена. Връзката е прекратена.", - "sessionTakenOver": "Сесията беше отворена в друг раздел. Повторно свързване..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Няма избран хост", - "initializingEditor": "Инициализиране на редактора...", - "file": "Файл", - "folder": "Папка", - "uploadFile": "Качване на файл", - "downloadFile": "Изтегляне", - "extractArchive": "Извличане на архив", - "extractingArchive": "Извличане на {{name}}...", - "archiveExtractedSuccessfully": "{{name}} е извлечено успешно", - "extractFailed": "Извличането не бе успешно", - "compressFile": "Компресиране на файл", - "compressFiles": "Компресиране на файлове", - "compressFilesDesc": "Компресиране на {{count}} елементи в архив", - "archiveName": "Име на архива", - "enterArchiveName": "Въведете име на архива...", - "compressionFormat": "Формат на компресия", - "selectedFiles": "Избрани файлове", - "andMoreFiles": "и още {{count}}...", - "compress": "Компресиране", - "compressingFiles": "Компресиране на {{count}} елементи в {{name}}...", - "filesCompressedSuccessfully": "{{name}} е създадено успешно", - "compressFailed": "Компресията не беше успешна", - "edit": "Редактиране", - "preview": "Преглед", - "previous": "Предишен", - "next": "Следващо", - "pageXOfY": "Страница {{current}} от {{total}}", - "zoomOut": "Намаляване на мащаба", - "zoomIn": "Увеличаване на мащаба", - "newFile": "Нов файл", - "newFolder": "Нова папка", - "rename": "Преименуване", - "uploading": "Качване...", - "uploadingFile": "Качване {{name}}...", - "fileName": "Име на файл", - "folderName": "Име на папката", - "fileUploadedSuccessfully": "Файлът „{{name}}“ е качен успешно", - "failedToUploadFile": "Качването на файла не бе успешно", - "fileDownloadedSuccessfully": "Файлът „{{name}}“ е изтеглен успешно", - "failedToDownloadFile": "Изтеглянето на файла не бе успешно", - "fileCreatedSuccessfully": "Файлът „{{name}}“ е създаден успешно", - "folderCreatedSuccessfully": "Папка „{{name}}“ е създадена успешно", - "failedToCreateItem": "Създаването на елемент не бе успешно", - "operationFailed": "Операцията {{operation}} е неуспешна за {{name}}: {{error}}", - "failedToResolveSymlink": "Неуспешно разрешаване на символната връзка", - "itemsDeletedSuccessfully": "{{count}} елементи са изтрити успешно", - "failedToDeleteItems": "Изтриването на елементи не бе успешно", - "sudoPasswordRequired": "Изисква се администраторска парола", - "enterSudoPassword": "Въведете паролата за sudo, за да продължите тази операция", - "sudoPassword": "Парола за Sudo", - "sudoOperationFailed": "Sudo операцията не беше успешна", - "sudoAuthFailed": "Sudo удостоверяването не беше успешно", - "dragFilesToUpload": "Пуснете файлове тук, за да ги качите", - "emptyFolder": "Тази папка е празна", - "searchFiles": "Търсене на файлове...", - "upload": "Качване", - "selectHostToStart": "Изберете хост, за да започнете управлението на файлове", - "sshRequiredForFileManager": "Файловият мениджър изисква SSH. Този хост не е активиран за SSH.", - "failedToConnect": "Неуспешно свързване към SSH", - "failedToLoadDirectory": "Неуспешно зареждане на директорията", - "noSSHConnection": "Няма налична SSH връзка", - "copy": "Копиране", - "cut": "Изрязване", - "paste": "Паста", - "copyPath": "Копиране на пътя", - "copyPaths": "Копиране на пътища", - "delete": "Изтриване", - "properties": "Имоти", - "refresh": "Обновяване", - "downloadFiles": "Изтегляне на {{count}} файлове в браузъра", - "copyFiles": "Копиране на {{count}} елементи", - "cutFiles": "Изрязване на {{count}} елементи", - "deleteFiles": "Изтриване на {{count}} елементи", - "filesCopiedToClipboard": "{{count}} елементи са копирани в клипборда", - "filesCutToClipboard": "{{count}} елементи, изрязани в клипборда", - "pathCopiedToClipboard": "Пътят е копиран в клипборда", - "pathsCopiedToClipboard": "{{count}} пътища, копирани в клипборда", - "failedToCopyPath": "Копирането на пътя в клипборда не бе успешно", - "movedItems": "Преместени {{count}} елементи", - "failedToDeleteItem": "Изтриването на елемента не бе успешно", - "itemRenamedSuccessfully": "{{type}} преименуван успешно", - "failedToRenameItem": "Преименуването на елемента не бе успешно", - "download": "Изтегляне", - "permissions": "Разрешения", - "size": "Размер", - "modified": "Модифицирано", - "path": "Път", - "confirmDelete": "Сигурни ли сте, че искате да изтриете {{name}}?", - "permissionDenied": "Разрешението е отказано", - "serverError": "Грешка на сървъра", - "fileSavedSuccessfully": "Файлът е запазен успешно", - "failedToSaveFile": "Запазването на файла не бе успешно", - "confirmDeleteSingleItem": "Сигурни ли сте, че искате да изтриете за постоянно „{{name}}“?", - "confirmDeleteMultipleItems": "Сигурни ли сте, че искате да изтриете за постоянно {{count}} елементи?", - "confirmDeleteMultipleItemsWithFolders": "Наистина ли искате да изтриете за постоянно {{count}} елементи? Това включва папки и тяхното съдържание.", - "confirmDeleteFolder": "Сигурни ли сте, че искате да изтриете окончателно папката „{{name}}“ и цялото ѝ съдържание?", - "permanentDeleteWarning": "Това действие не може да бъде отменено. Елементът(ите) ще бъдат изтрити за постоянно от сървъра.", - "recent": "Скорошни", - "pinned": "Закачено", - "folderShortcuts": "Преки пътища към папки", - "failedToReconnectSSH": "Неуспешно повторно свързване на SSH сесията", - "openTerminalHere": "Отворете терминала тук", - "run": "Бягане", - "openTerminalInFolder": "Отвори терминала в тази папка", - "openTerminalInFileLocation": "Отваряне на терминала на местоположението на файла", - "runningFile": "Бягане - {{file}}", - "onlyRunExecutableFiles": "Може да изпълнява само изпълними файлове", - "directories": "Директории", - "removedFromRecentFiles": "Премахнато е „{{name}}“ от последните файлове", - "removeFailed": "Премахването не бе успешно", - "unpinnedSuccessfully": "Откачено е успешно „{{name}}“", - "unpinFailed": "Откачването не бе успешно", - "removedShortcut": "Премахнат е пряк път „{{name}}“", - "removeShortcutFailed": "Премахването на пряк път не бе успешно", - "clearedAllRecentFiles": "Изчистени са всички скорошни файлове", - "clearFailed": "Изчистването не бе успешно", - "removeFromRecentFiles": "Премахване от скорошни файлове", - "clearAllRecentFiles": "Изчистване на всички скорошни файлове", - "unpinFile": "Откачи файл", - "removeShortcut": "Премахване на пряк път", - "pinFile": "Закачи файл", - "addToShortcuts": "Добавяне към преките пътища", - "pasteFailed": "Поставянето не бе успешно", - "noUndoableActions": "Няма отменими действия", - "undoCopySuccess": "Отменено копиране: Изтрити {{count}} копирани файлове", - "undoCopyFailedDelete": "Отмяната не бе успешна: Не можаха да бъдат изтрити копирани файлове", - "undoCopyFailedNoInfo": "Отмяната не бе успешна: Не можа да бъде намерена информация за копирания файл", - "undoMoveSuccess": "Отменена операция по преместване: {{count}} файлове са преместени обратно в оригиналното местоположение", - "undoMoveFailedMove": "Отмяната не бе успешна: Не можах да преместя файловете обратно", - "undoMoveFailedNoInfo": "Отмяната не бе успешна: Не можа да бъде намерена информация за преместения файл", - "undoDeleteNotSupported": "Операцията по изтриване не може да бъде отменена: Файловете са изтрити за постоянно от сървъра", - "undoTypeNotSupported": "Неподдържан тип операция за отмяна", - "undoOperationFailed": "Отмяната не бе успешна", - "unknownError": "Неизвестна грешка", - "confirm": "Потвърди", - "find": "Намерете...", - "replace": "Замяна", - "downloadInstead": "Изтегляне вместо това", - "keyboardShortcuts": "Клавишни комбинации", - "searchAndReplace": "Търсене и замяна", - "editing": "Редактиране", - "search": "Търсене", - "findNext": "Намери следващото", - "findPrevious": "Намери предишното", - "save": "Запазване", - "selectAll": "Избери всички", - "undo": "Отмяна", - "redo": "Преработи", - "moveLineUp": "Премести линията нагоре", - "moveLineDown": "Премести реда надолу", - "toggleComment": "Превключване на коментара", - "autoComplete": "Автоматично завършване", - "imageLoadError": "Зареждането на изображението не бе успешно", - "startTyping": "Започнете да пишете...", - "unknownSize": "Неизвестен размер", - "fileIsEmpty": "Файлът е празен", - "largeFileWarning": "Предупреждение за голям файл", - "largeFileWarningDesc": "Размерът на този файл е {{size}} , което може да причини проблеми с производителността, когато се отвори като текст.", - "fileNotFoundAndRemoved": "Файлът „{{name}}“ не е намерен и е премахнат от скорошни/закачени файлове", - "failedToLoadFile": "Неуспешно зареждане на файл: {{error}}", - "serverErrorOccurred": "Възникна грешка в сървъра. Моля, опитайте отново по-късно.", - "autoSaveFailed": "Автоматичното запазване не бе успешно", - "fileAutoSaved": "Файлът е автоматично запазен", - "moveFileFailed": "Преместването не бе успешно {{name}}", - "moveOperationFailed": "Операцията по преместване не бе успешна", - "canOnlyCompareFiles": "Може да се сравняват само два файла", - "comparingFiles": "Сравняване на файлове: {{file1}} и {{file2}}", - "dragFailed": "Плъзгането не беше успешно", - "filePinnedSuccessfully": "Файлът „{{name}}“ е успешно закачен", - "pinFileFailed": "Закачването на файла не бе успешно", - "fileUnpinnedSuccessfully": "Файлът „{{name}}“ е откачен успешно", - "unpinFileFailed": "Откачването на файла не бе успешно", - "shortcutAddedSuccessfully": "Прякият път към папка „{{name}}“ е добавен успешно", - "addShortcutFailed": "Добавянето на пряк път не бе успешно", - "operationCompletedSuccessfully": "{{operation}} {{count}} елементи успешно", - "operationCompleted": "{{operation}} {{count}} артикули", - "downloadFileSuccess": "Файлът {{name}} е изтеглен успешно", - "downloadFileFailed": "Изтеглянето не бе успешно", - "moveTo": "Премести се в {{name}}", - "diffCompareWith": "Разлика в сравнение с {{name}}", - "dragOutsideToDownload": "Плъзнете извън прозореца, за да изтеглите ({{count}} файлове)", - "newFolderDefault": "НоваПапка", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Успешно преместени {{count}} елементи в {{target}}", - "move": "Преместване", - "searchInFile": "Търсене във файл (Ctrl+F)", - "showKeyboardShortcuts": "Показване на клавишни комбинации", - "startWritingMarkdown": "Започнете да пишете съдържанието си за markdown...", - "loadingFileComparison": "Зареждане на сравнението на файлове...", - "reload": "Презареждане", - "compare": "Сравни", - "sideBySide": "Рамо до рамо", - "inline": "Вграден", - "fileComparison": "Сравнение на файлове: {{file1}} срещу {{file2}}", - "fileTooLarge": "Файлът е твърде голям: {{error}}", - "sshConnectionFailed": "SSH връзката не бе успешна. Моля, проверете връзката си с {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Неуспешно зареждане на файл: {{error}}", - "connecting": "Свързване...", - "connectedSuccessfully": "Свързаването е осъществено успешно", - "totpVerificationFailed": "Проверката на TOTP не бе успешна", - "warpgateVerificationFailed": "Удостоверяването на Warpgate не беше успешно", - "authenticationFailed": "Удостоверяването не бе успешно", - "verificationCodePrompt": "Код за потвърждение:", - "changePermissions": "Промяна на разрешенията", - "currentPermissions": "Текущи разрешения", - "owner": "Собственик", - "group": "Група", - "others": "Други", - "read": "Прочетете", - "write": "Пишете", - "execute": "Изпълнение", - "permissionsChangedSuccessfully": "Разрешенията са променени успешно", - "failedToChangePermissions": "Промяната на разрешенията не бе успешна", - "name": "Име", - "sortByName": "Име", - "sortByDate": "Дата на промяна", - "sortBySize": "Размер", - "ascending": "Възходящ", - "descending": "Низходящо", - "root": "Корен", - "new": "Ново", - "sortBy": "Сортиране по", - "items": "Елементи", - "selected": "Избрано", - "editor": "Редактор", - "octal": "Осмичен", - "storage": "Съхранение", - "disk": "Диск", - "used": "Използван", - "of": "от", - "toggleSidebar": "Превключване на страничната лента", - "cannotLoadPdf": "Не може да се зареди PDF файл", - "pdfLoadError": "Възникна грешка при зареждането на този PDF файл.", - "loadingPdf": "Зареждане на PDF файл...", - "loadingPage": "Зареждане на страницата..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Копиране към хост…", - "moveToHost": "Преместване към хост…", - "copyItemsToHost": "Копиране на {{count}} елементи за хостване на…", - "moveItemsToHost": "Преместете {{count}} елементи, за да хоствате…", - "noHostsConnected": "Няма други налични хостове за файлови мениджъри.", - "noHostsConnectedHint": "Добавете друг SSH хост с активиран файлов мениджър в Host Manager.", - "selectDestinationHost": "Изберете целеви хост", - "destinationPath": "Път на местоназначението", - "recentDestinations": "Последни дестинации", - "collapseRecentDestinations": "Свиване на скорошни дестинации", - "expandRecentDestinations": "Разгъване на скорошни дестинации", - "browseFolders": "Преглед на целевите папки", - "browseDestination": "Преглед или въвеждане на път", - "confirmCopy": "Копиране", - "confirmMove": "Преместване", - "transferring": "Прехвърляне…", - "compressing": "Компресиране…", - "extracting": "Извличане…", - "transferringItems": "Прехвърляне на {{current}} от {{total}} елементи…", - "transferSuccess": "Прехвърлянето е завършено", - "transferError": "Прехвърлянето не бе успешно", - "transferPartial": "Прехвърлянето е завършено с {{count}} грешки", - "transferPartialHint": "Не можа да се прехвърли: {{paths}}", - "itemsSummary": "{{count}} артикули", - "destMustBeDirectory": "Дестинацията трябва да е директория за прехвърляне на множество елементи", - "selectThisFolder": "Изберете тази папка", - "browsePathWillBeCreated": "Тази папка все още не съществува. Тя ще бъде създадена, когато започне прехвърлянето.", - "browsePathError": "Не можа да се отвори този път на целевия хост.", - "goUp": "Качи се нагоре", - "copyFolderToHost": "Копиране на папката на хост…", - "moveFolderToHost": "Преместване на папката на хост…", - "hostReady": "Готов", - "hostConnecting": "Свързване…", - "hostDisconnected": "Няма връзка", - "hostAuthRequired": "Изисква се удостоверяване — първо отворете Файловия мениджър на този хост", - "hostConnectionFailed": "Връзката не бе успешна", - "metricsTitle": "Времетраене на трансферите", - "metricsPrepare": "Подготовка на дестинацията: {{duration}}", - "metricsCompress": "Компресиране при източника: {{duration}}", - "metricsHopSourceRead": "Източник → сървър: {{throughput}}", - "metricsHopDestSftpWrite": "Сървър → дестинация (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Сървър → дестинация (локална): {{throughput}}", - "metricsTransfer": "От край до край: {{throughput}} ({{duration}})", - "metricsExtract": "Извличане в местоназначението: {{duration}}", - "metricsSourceDelete": "Премахване от източника: {{duration}}", - "metricsTotal": "Общо: {{duration}}", - "progressCompressing": "Компресиране на изходния хост…", - "progressExtracting": "Извличане в местоназначението…", - "progressTransferring": "Прехвърляне на данни…", - "progressReconnecting": "Повторно свързване…", - "parallelSegmentsLabel": "Паралелни трансферни ленти", - "parallelSegmentsOption": "{{count}} ленти", - "parallelSegmentsHint": "Големите файлове се разделят на парчета от 256 MB. Няколко канала използват отделни връзки (като стартиране на няколко трансфера) за по-висока обща пропускателна способност.", - "progressTotalSpeed": "{{speed}} общо ({{lanes}} ленти)", - "progressTransferringItems": "Прехвърляне на файлове ({{current}} от {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} файлове", - "sourceNotDeletedPartial": "Изходните файлове са запазени (частично прехвърляне)", - "jumpHostLimitation": "И двата хоста трябва да са достъпни от Termix сървъра. Директното маршрутизиране от хост към хост не се поддържа.", - "cancel": "Отказ", - "methodLabel": "Метод на прехвърляне", - "methodAuto": "Автоматично", - "methodTar": "Тар архив", - "methodItemSftp": "SFTP за всеки файл", - "methodAutoHint": "Избира tar или SFTP за всеки файл въз основа на броя, размера и компресируемостта на файловете. Отделните файлове винаги използват стрийминг SFTP.", - "methodTarHint": "Компресиране при източника, прехвърляне на един архив, разархивиране при местоназначението. Изисква tar и на двата Unix хоста.", - "methodItemSftpHint": "Прехвърляйте всеки файл поотделно през SFTP. Работи на всички хостове, включително Windows.", - "methodPreviewLoading": "Метод за изчисляване на трансфер…", - "methodPreviewError": "Не можа да се прегледа методът за прехвърляне. Сървърът все пак ще избере метод, когато стартирате.", - "methodPreviewWillUseTar": "Ще използвам: Tar архив", - "methodPreviewWillUseItemSftp": "Ще използва: SFTP за всеки файл", - "methodPreviewScanSummary": "{{fileCount}} файлове, {{totalSize}} общо (сканирани на изходния хост).", - "methodItemSftpLimitation": "Всеки файл използва същия SFTP поток като копие на един файл, следвани един след друг. Напредъкът се комбинира във всички файлове, така че лентата се движи бавно при големи файлове.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Избрали сте SFTP за всеки файл.", - "user_tar": "Избрахте tar архив.", - "tar_unavailable": "Tar не е наличен на единия или и на двата хоста — вместо това ще се използва SFTP за всеки файл.", - "windows_host": "Включен е хост на Windows — tar не се използва.", - "auto_multi_large": "Автоматично: множество файлове, включително голям файл ({{largestSize}}) със свиваеми данни — tar пакети в един трансфер.", - "auto_single_large_in_archive": "Автоматично: един голям файл ({{largestSize}}) в този набор — SFTP за всеки файл.", - "auto_many_incompressible": "Автоматично: предимно некомпресируеми данни — SFTP за всеки файл.", - "auto_many_files": "Автоматично: много файлове ({{fileCount}}) — tar намалява натоварването на всеки файл.", - "auto_default": "Автоматично: SFTP за всеки файл за този набор." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Отказ", - "progressCancelling": "Анулиране…", - "progressStalled": "Застоял", - "resumedHint": "Възстановена е връзка с активно прехвърляне, започнато в друг прозорец.", - "transferCancelled": "Прехвърлянето е анулирано", - "transferCancelledCopyHint": "Частични файлове може да останат в местоназначението.", - "transferCancelledMoveHint": "Възможно е част от файловете да са останали в местоназначението. Изходните файлове не са премахнати.", - "cleanupDestFiles": "Почистване на дестинацията", - "cleanupDestFilesSuccess": "Премахнати частични файлове от местоназначението", - "cleanupDestFilesPartial": "Някои частични файлове не можаха да бъдат премахнати", - "cleanupDestFilesNothing": "Няма нищо за почистване на дестинацията", - "cleanupDestFilesError": "Почистването не бе успешно", - "retryTransfer": "Опитай отново", - "retryTransferError": "Повторният опит не бе успешен", - "transferFailedRetryHint": "В местоназначението са запазени частични данни. Повторният опит ще се възобнови, когато връзката се възстанови." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Няма SSH тунели", - "createFirstTunnelMessage": "Все още не сте създали SSH тунели. Конфигурирайте тунелни връзки в Host Manager, за да започнете.", - "connected": "Свързан", - "disconnected": "Изключен", - "connecting": "Свързване...", - "error": "Грешка", - "canceling": "Анулиране...", - "connect": "Свържете се", - "disconnect": "Изключване", - "cancel": "Отказ", - "port": "Порт", - "localPort": "Локален порт", - "remotePort": "Отдалечен порт", - "currentHostPort": "Текущ хост порт", - "endpointPort": "Порт за крайна точка", - "bindIp": "Локален IP адрес", - "endpointSshConfig": "SSH конфигурация на крайна точка", - "endpointSshHost": "SSH хост за крайна точка", - "endpointSshHostPlaceholder": "Изберете конфигуриран хост", - "endpointSshHostRequired": "Изберете SSH хост за крайна точка за всеки клиентски тунел.", - "attempt": "Опит {{current}} от {{max}}", - "nextRetryIn": "Следващ опит след {{seconds}} секунди", - "clientTunnels": "Клиентски тунели", - "clientTunnel": "Клиентски тунел", - "addClientTunnel": "Добавяне на клиентски тунел", - "noClientTunnels": "Няма конфигурирани клиентски тунели на този работен плот.", - "tunnelName": "Име на тунела", - "remoteHost": "Отдалечен хост", - "autoStart": "Автоматично стартиране", - "clientAutoStartDesc": "Стартира, когато този настолен клиент се отвори и остане свързан.", - "clientManualStartDesc": "Използвайте „Старт“ и „Стоп“ от този ред. Termix няма да го отвори автоматично.", - "clientRemoteServerNote": "Отдалеченото пренасочване може да изисква AllowTcpForwarding и GatewayPorts на SSH сървъра на крайната точка. Отдалеченият порт се затваря, когато този работен плот се изключи.", - "clientTunnelStarted": "Тунелът на клиента е стартиран", - "clientTunnelStopped": "Тунелът на клиента е спрян", - "tunnelTestSucceeded": "Тестът в тунела е успешен", - "tunnelTestFailed": "Тестът на тунела не беше успешен", - "localSaved": "Клиентските тунели са запазени", - "localSaveError": "Запазването на тунелите на локалните клиенти не бе успешно", - "invalidBindIp": "Локалният IP адрес трябва да е валиден IPv4 адрес.", - "invalidLocalTargetIp": "Локалният целеви IP адрес трябва да е валиден IPv4 адрес.", - "invalidLocalPort": "Локалният порт трябва да е между 1 и 65535.", - "invalidRemotePort": "Отдалеченият порт трябва да е между 1 и 65535.", - "invalidLocalTargetPort": "Локалният целеви порт трябва да е между 1 и 65535.", - "invalidEndpointPort": "Портът на крайната точка трябва да е между 1 и 65535.", - "duplicateAutoStartBind": "Само един тунел за автоматично стартиране на клиент може да използва {{bind}}.", - "manualControlError": "Актуализирането на състоянието на тунела не бе успешно.", - "active": "Активен", - "start": "Старт", - "stop": "Спри", - "test": "Тест", - "type": "Тип тунел", - "typeLocal": "Местен (-L)", - "typeRemote": "Дистанционно (-R)", - "typeDynamic": "Динамичен (-D)", - "typeServerLocalDesc": "Текущ хост към крайна точка.", - "typeServerRemoteDesc": "Крайна точка обратно към текущия хост.", - "typeClientLocalDesc": "Локален компютър към крайна точка.", - "typeClientRemoteDesc": "Крайна точка обратно към локалния компютър.", - "typeClientDynamicDesc": "SOCKS на локален компютър.", - "typeDynamicDesc": "Пренасочване на SOCKS5 CONNECT трафик през SSH", - "forwardDescriptionServerLocal": "Текущ хост {{sourcePort}} → крайна точка {{endpointPort}}.", - "forwardDescriptionServerRemote": "Крайна точка {{endpointPort}} → текущ хост {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS на текущия хост {{sourcePort}}.", - "forwardDescriptionClientLocal": "Локален {{sourcePort}} → отдалечен {{endpointPort}}.", - "forwardDescriptionClientRemote": "Отдалечено {{sourcePort}} → локално {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS на локален порт {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → ЧОРАПИ чрез {{endpoint}}", - "autoNameClientLocal": "Местно {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → местен {{localPort}}", - "autoNameClientDynamic": "ЧОРАПИ {{localPort}} чрез {{endpoint}}", - "route": "Маршрут:", - "lastStarted": "Последно стартирано", - "lastTested": "Последно тествано", - "lastError": "Последна грешка", - "maxRetries": "Максимален брой повторни опити", - "maxRetriesDescription": "Максимален брой опити за повторно опитване.", - "retryInterval": "Интервал на повторен опит (секунди)", - "retryIntervalDescription": "Време за изчакване между опитите за повторен опит.", - "local": "Местно", - "remote": "Дистанционно", - "destination": "Дестинация", - "host": "Домакин", - "mode": "Режим", - "noHostSelected": "Няма избран хост", - "working": "Работа..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Процесор", - "memory": "Памет", - "disk": "Диск", - "network": "Мрежа", - "uptime": "Време на работа", - "processes": "Процеси", - "available": "Налично", - "free": "Безплатно", - "connecting": "Свързване...", - "connectionFailed": "Неуспешно свързване със сървъра", - "naCpus": "Няма процесор(и)", - "cpuCores_one": "{{count}} Ядро", - "cpuCores_other": "{{count}} Ядра", - "cpuUsage": "Използване на процесора", - "memoryUsage": "Използване на паметта", - "diskUsage": "Използване на диска", - "failedToFetchHostConfig": "Неуспешно извличане на конфигурацията на хоста", - "serverOffline": "Сървърът е офлайн", - "cannotFetchMetrics": "Не могат да се извлекат показатели от офлайн сървър", - "totpFailed": "Проверката на TOTP не бе успешна", - "noneAuthNotSupported": "Статистиката на сървъра не поддържа тип удостоверяване „няма“.", - "load": "Зареждане", - "systemInfo": "Системна информация", - "hostname": "Име на хост", - "operatingSystem": "Операционна система", - "kernel": "Ядро", - "seconds": "секунди", - "networkInterfaces": "Мрежови интерфейси", - "noInterfacesFound": "Не са намерени мрежови интерфейси", - "noProcessesFound": "Не са намерени процеси", - "loginStats": "Статистика за SSH вход", - "noRecentLoginData": "Няма скорошни данни за вход", - "executingQuickAction": "Изпълнява се {{name}}...", - "quickActionSuccess": "{{name}} завършено успешно", - "quickActionFailed": "{{name}} неуспешно", - "quickActionError": "Неуспешно изпълнение на {{name}}", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Портове за слушане", - "protocol": "Протокол", - "port": "Порт", - "address": "Адрес", - "process": "Процес", - "noData": "Няма данни за портове за слушане" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Защитна стена", - "inactive": "Неактивен", - "policy": "Политика", - "rules": "правила", - "noData": "Няма налични данни за защитната стена", - "action": "Действие", - "protocol": "Прото", - "port": "Порт", - "source": "Източник", - "anywhere": "Навсякъде" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Зареждане на средно", - "swap": "Размяна", - "architecture": "Архитектура", - "refresh": "Обновяване", - "retry": "Опитай отново" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Самостоятелно хоствано SSH и управление на отдалечен работен плот", - "loginTitle": "Вход в Termix", - "registerTitle": "Създаване на акаунт", - "forgotPassword": "Забравена парола?", - "rememberMe": "Запомни устройството за 30 дни (включва TOTP)", - "noAccount": "Нямате акаунт?", - "hasAccount": "Вече имате акаунт?", - "twoFactorAuth": "Двуфакторно удостоверяване", - "enterCode": "Въведете код за потвърждение", - "backupCode": "Или използвайте резервен код", - "verifyCode": "Код за проверка", - "redirectingToApp": "Пренасочване към приложението...", - "sshAuthenticationRequired": "Изисква се SSH удостоверяване", - "sshNoKeyboardInteractive": "Интерактивно удостоверяване с клавиатура не е налично", - "sshAuthenticationFailed": "Удостоверяването не бе успешно", - "sshAuthenticationTimeout": "Време за изчакване на удостоверяване", - "sshNoKeyboardInteractiveDescription": "Сървърът не поддържа интерактивно удостоверяване с клавиатура. Моля, въведете паролата или SSH ключа си.", - "sshAuthFailedDescription": "Предоставените идентификационни данни бяха неправилни. Моля, опитайте отново с валидни идентификационни данни.", - "sshTimeoutDescription": "Времето за изчакване на опита за удостоверяване изтече. Моля, опитайте отново.", - "sshProvideCredentialsDescription": "Моля, предоставете вашите SSH идентификационни данни, за да се свържете с този сървър.", - "sshPasswordDescription": "Въведете паролата за тази SSH връзка.", - "sshKeyPasswordDescription": "Ако вашият SSH ключ е криптиран, въведете паролата тук.", - "passphraseRequired": "Изисква се парола", - "passphraseRequiredDescription": "SSH ключът е криптиран. Моля, въведете паролата, за да го отключите.", - "back": "Обратно", - "firstUser": "Първи потребител", - "firstUserMessage": "Вие сте първият потребител и ще бъдете назначен за администратор. Можете да видите настройките на администратора в падащото меню за потребители в страничната лента. Ако смятате, че това е грешка, проверете лог файловете на Docker или създайте проблем в GitHub.", - "external": "Външен", - "loginWithExternal": "Вход с външен доставчик", - "loginWithExternalDesc": "Влезте, използвайки вашия конфигуриран външен доставчик на самоличност", - "externalNotSupportedInElectron": "Външното удостоверяване все още не се поддържа в приложението Electron. Моля, използвайте уеб версията за вход в OIDC.", - "resetPasswordButton": "Нулиране на парола", - "sendResetCode": "Изпрати код за нулиране", - "resetCodeDesc": "Въведете потребителското си име, за да получите код за нулиране на паролата. Кодът ще бъде записан в лог файловете на Docker контейнера.", - "resetCode": "Код за нулиране", - "verifyCodeButton": "Код за проверка", - "enterResetCode": "Въведете 6-цифрения код от лог файловете на Docker контейнера за потребителя:", - "newPassword": "Нова парола", - "confirmNewPassword": "Потвърдете паролата", - "enterNewPassword": "Въведете новата си парола за потребителя:", - "signUp": "Регистрация", - "desktopApp": "Настолно приложение", - "loggingInToDesktopApp": "Влизане в настолното приложение", - "loadingServer": "Зареждане на сървъра...", - "dataLossWarning": "Нулирането на паролата ви по този начин ще изтрие всички ваши запазени SSH хостове, идентификационни данни и други криптирани данни. Това действие не може да бъде отменено. Използвайте това само ако сте забравили паролата си и не сте влезли в системата.", - "authenticationDisabled": "Удостоверяването е деактивирано", - "authenticationDisabledDesc": "Всички методи за удостоверяване са деактивирани. Моля, свържете се с вашия администратор.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Проверете SSH хост ключа", - "keyChangedWarning": "SSH ключът на хоста е променен", - "firstConnectionTitle": "Първо свързване с този хост", - "firstConnectionDescription": "Автентичността на този хост не може да бъде установена. Проверете дали пръстовият отпечатък съответства на очакванията ви.", - "keyChangedDescription": "Ключът на хоста за този сървър се е променил от последното ви свързване. Това може да показва проблем със сигурността.", - "previousKey": "Предишен ключ", - "newFingerprint": "Нов пръстов отпечатък", - "fingerprint": "Пръстов отпечатък", - "verifyInstructions": "Ако имате доверие на този хост, щракнете върху „Приемам“, за да продължите и да запазите този пръстов отпечатък за бъдещи връзки.", - "securityWarning": "Предупреждение за сигурност", - "acceptAndContinue": "Приемам и продължавам", - "acceptNewKey": "Приемете новия ключ и продължете" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Не можа да се свърже с базата данни", - "unknownError": "Неизвестна грешка", - "loginFailed": "Влизането не бе успешно", - "failedPasswordReset": "Неуспешно стартиране на нулиране на паролата", - "failedVerifyCode": "Неуспешно потвърждаване на кода за нулиране", - "failedCompleteReset": "Неуспешно завършване на нулирането на паролата", - "invalidTotpCode": "Невалиден TOTP код", - "failedOidcLogin": "Стартирането на входа в OIDC не бе успешно", - "silentSigninOidcUnavailable": "Заявено е безшумно влизане, но влизането в OIDC не е налично.", - "failedUserInfo": "Неуспешно получаване на потребителска информация след влизане", - "oidcAuthFailed": "OIDC удостоверяването не беше успешно", - "invalidAuthUrl": "Невалиден URL адрес за оторизация, получен от бекенд системата", - "requiredField": "Това поле е задължително", - "minLength": "Минималната дължина е {{min}}", - "passwordMismatch": "Паролите не съвпадат", - "passwordLoginDisabled": "Входът с потребителско име/парола е деактивиран в момента", - "sessionExpired": "Сесията е изтекла - моля, влезте отново", - "totpRateLimited": "Ограничена скорост: Твърде много опити за проверка на TOTP. Моля, опитайте отново по-късно.", - "totpRateLimitedWithTime": "Ограничена скорост: Твърде много опити за проверка на TOTP. Моля, изчакайте {{time}} секунди, преди да опитате отново.", - "resetCodeRateLimited": "Ограничена скорост: Твърде много опити за потвърждение. Моля, опитайте отново по-късно.", - "resetCodeRateLimitedWithTime": "Ограничена скорост: Твърде много опити за потвърждение. Моля, изчакайте {{time}} секунди, преди да опитате отново.", - "authTokenSaveFailed": "Запазването на токена за удостоверяване не бе успешно", - "failedToLoadServer": "Зареждането на сървъра не бе успешно" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Регистрацията на нов акаунт в момента е деактивирана от администратор. Моля, влезте или се свържете с администратор.", - "userNotAllowed": "Вашият акаунт не е оторизиран за регистрация. Моля, свържете се с администратор.", - "databaseConnectionFailed": "Неуспешно свързване със сървъра на базата данни", - "resetCodeSent": "Кодът за нулиране е изпратен до лог файловете на Docker", - "codeVerified": "Кодът е успешно потвърден", - "passwordResetSuccess": "Успешно възстановяване на паролата", - "loginSuccess": "Влизането е успешно", - "registrationSuccess": "Регистрацията е успешна" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Локални тунели за настолни компютри, насочени към конфигурирани SSH хостове.", - "c2sTunnelPresets": "Предварителни настройки на клиентския тунел", - "c2sTunnelPresetsDesc": "Запазете локалния списък с тунели на този настолен клиент като именуван предварително зададен сървър или заредете предварително зададена настройка обратно в този клиент.", - "c2sTunnelPresetsUnavailable": "Предварителните настройки на клиентските тунели са налични само в настолния клиент.", - "c2sPresetName": "Име на предварително зададена настройка", - "c2sPresetNamePlaceholder": "Име на предварително зададена настройка на клиента", - "c2sPresetToLoad": "Предварително зададено зареждане", - "c2sNoPresetSelected": "Няма избрана предварително зададена настройка", - "c2sNoPresets": "Няма запазени предварително зададени настройки", - "c2sLoadPreset": "Зареждане", - "c2sCurrentLocalConfig": "{{count}} локални клиентски тунели, конфигурирани на този работен плот.", - "c2sPresetSyncNote": "Предварителните настройки са изрични снимки; зареждането на такава замества списъка с локални клиентски тунели на този настолен клиент.", - "c2sPresetSaved": "Предварително зададена настройка за тунел на клиента е запазена", - "c2sPresetLoaded": "Предварително зададена настройка за тунел на клиента, заредена локално", - "c2sPresetRenamed": "Предварителното преименуване на клиентския тунел", - "c2sPresetDeleted": "Предварително зададената настройка за тунел на клиента е изтрита", - "c2sPresetLoadError": "Неуспешно зареждане на предварително зададени настройки за тунел на клиента" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Език", - "keyPassword": "ключова парола", - "pastePrivateKey": "Поставете личния си ключ тук...", - "localListenerHost": "127.0.0.1 (слушане локално)", - "localTargetHost": "127.0.0.1 (цел на този компютър)", - "socksListenerHost": "127.0.0.1 (SOCKS слушател)", - "enterPassword": "Въведете паролата си", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Табло за управление", - "loading": "Зареждане на таблото за управление...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Поддръжка", - "discord": "Дискорд", - "serverOverview": "Преглед на сървъра", - "version": "Версия", - "upToDate": "Актуално", - "updateAvailable": "Налична е актуализация", - "beta": "Бета", - "uptime": "Време на работа", - "database": "База данни", - "healthy": "Здравословен", - "error": "Грешка", - "totalHosts": "Общо хостове", - "totalTunnels": "Общо тунели", - "totalCredentials": "Общо пълномощия", - "recentActivity": "Последна активност", - "reset": "Нулиране", - "loadingRecentActivity": "Зареждане на скорошна активност...", - "noRecentActivity": "Няма скорошна активност", - "quickActions": "Бързи действия", - "addHost": "Добавяне на хост", - "addCredential": "Добавяне на идентификационни данни", - "adminSettings": "Административни настройки", - "userProfile": "Потребителски профил", - "serverStats": "Статистика на сървъра", - "loadingServerStats": "Зареждане на статистиката на сървъра...", - "noServerData": "Няма налични данни от сървъра", - "cpu": "Процесор", - "ram": "RAM памет", - "customizeLayout": "Персонализиране на таблото за управление", - "dashboardSettings": "Настройки на таблото за управление", - "enableDisableCards": "Активиране/деактивиране на карти", - "resetLayout": "Възстановяване на фабричните настройки", - "serverOverviewCard": "Преглед на сървъра", - "recentActivityCard": "Последна активност", - "networkGraphCard": "Мрежова графика", - "networkGraph": "Мрежова графика", - "quickActionsCard": "Бързи действия", - "serverStatsCard": "Статистика на сървъра", - "panelMain": "Главно", - "panelSide": "Страна", - "justNow": "точно сега" + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "СТАБИЛЕН", - "hostsOnline": "Домакините онлайн", - "activeTunnels": "Активни тунели", - "registerNewServer": "Регистрирайте нов сървър", - "storeSshKeysOrPasswords": "Съхранявайте SSH ключове или пароли", - "manageUsersAndRoles": "Управление на потребители и роли", - "manageYourAccount": "Управлявайте профила си", - "hostStatus": "Състояние на хоста", - "noHostsConfigured": "Няма конфигурирани хостове", - "online": "ОНЛАЙН", - "offline": "ОФЛАЙН", - "onlineLower": "Онлайн", - "nodes": "{{count}} възли", - "add": "Добави:", - "commandPalette": "Палитра с команди", - "done": "Готово", - "editModeInstructions": "Плъзнете картите, за да ги пренаредите · Плъзнете разделителя на колоните, за да промените размера на колоните · Плъзнете долния ръб на картата, за да промените размера на височината ѝ · Изтрийте картата от кошчето", - "empty": "Празно", - "clear": "Изчисти" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Добавяне на хост", - "addGroup": "Добавяне на група", - "addLink": "Добавяне на връзка", - "zoomIn": "Увеличаване на мащаба", - "zoomOut": "Намаляване на мащаба", - "resetView": "Нулиране на изгледа", - "selectHost": "Изберете хост", - "chooseHost": "Изберете хост...", - "parentGroup": "Родителска група", - "noGroup": "Няма група", - "groupName": "Име на групата", - "color": "Цвят", - "source": "Източник", - "target": "Цел", - "moveToGroup": "Преместване в група", - "selectGroup": "Изберете група...", - "addConnection": "Добавяне на връзка", - "hostDetails": "Детайли за хоста", - "removeFromGroup": "Премахване от групата", - "addHostHere": "Добавете хост тук", - "editGroup": "Редактиране на група", - "delete": "Изтриване", - "add": "Добавяне", - "create": "Създаване", - "move": "Преместване", - "connect": "Свържете се", - "createGroup": "Създаване на група", - "selectSourcePlaceholder": "Изберете източник...", - "selectTargetPlaceholder": "Изберете цел...", - "invalidFile": "Невалиден файл", - "hostAlreadyExists": "Хостът вече е в топологията", - "connectionExists": "Връзката вече съществува", - "unknown": "Неизвестен", - "name": "Име", - "ip": "ИП", - "status": "Статус", - "failedToAddNode": "Добавянето на възел не бе успешно", - "sourceDifferentFromTarget": "Източникът и целта трябва да са различни", - "exportJSON": "Експортиране на JSON", - "importJSON": "Импортиране на JSON", - "terminal": "Терминал", - "fileManager": "Файлов мениджър", - "tunnel": "Тунел", - "docker": "Докер", - "serverStats": "Статистика на сървъра", - "noNodes": "Все още няма възли" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker не е активиран за този хост", - "validating": "Валидиране на Docker...", - "connecting": "Свързване...", - "error": "Грешка", - "version": "Докер {{version}}", - "connectionFailed": "Неуспешно свързване с Docker", - "containerStarted": "Контейнерът {{name}} е стартиран", - "failedToStartContainer": "Неуспешно стартиране на контейнера {{name}}", - "containerStopped": "Контейнерът {{name}} е спрян", - "failedToStopContainer": "Неуспешно спиране на контейнера {{name}}", - "containerRestarted": "Контейнерът {{name}} е рестартиран", - "failedToRestartContainer": "Рестартирането на контейнера не бе успешно {{name}}", - "containerPaused": "Контейнерът {{name}} е на пауза", - "containerUnpaused": "Контейнерът {{name}} е възстановен", - "failedToTogglePauseContainer": "Неуспешно превключване на състоянието на пауза за контейнер {{name}}", - "containerRemoved": "Контейнерът {{name}} е премахнат", - "failedToRemoveContainer": "Неуспешно премахване на контейнера {{name}}", - "image": "Изображение", - "ports": "Портове", - "noPorts": "Няма портове", - "start": "Старт", - "confirmRemoveContainer": "Сигурни ли сте, че искате да премахнете контейнера „{{name}}“? Това действие не може да бъде отменено.", - "runningContainerWarning": "Предупреждение: Този контейнер в момента работи. Премахването му първо ще спре контейнера.", - "loadingContainers": "Зареждане на контейнери...", - "manager": "Докер мениджър", - "autoRefresh": "Автоматично опресняване", - "timestamps": "Времеви печати", - "lines": "Линии", - "filterLogs": "Филтриране на лог файлове...", - "refresh": "Обновяване", - "download": "Изтегляне", - "clear": "Изчисти", - "logsDownloaded": "Логовете са изтеглени успешно", - "last50": "Последните 50", - "last100": "Последните 100", - "last500": "Последните 500", - "last1000": "Последни 1000", - "allLogs": "Всички лог файлове", - "noLogsMatching": "Няма регистрационни файлове, съответстващи на „{{query}}“", - "noLogsAvailable": "Няма налични лог файлове", - "noContainersFound": "Не са намерени контейнери", - "noContainersFoundHint": "На този хост няма налични Docker контейнери", - "searchPlaceholder": "Търсене на контейнери...", - "allStatuses": "Всички статуси", - "stateRunning": "Бягане", - "statePaused": "На пауза", - "stateExited": "Излязох", - "stateRestarting": "Рестартиране", - "noContainersMatchFilters": "Няма контейнери, които да отговарят на вашите филтри", - "noContainersMatchFiltersHint": "Опитайте да коригирате критериите си за търсене или филтриране", - "failedToFetchStats": "Извличането на статистически данни за контейнера не бе успешно", - "containerNotRunning": "Контейнерът не работи", - "startContainerToViewStats": "Стартирайте контейнера, за да видите статистиката", - "loadingStats": "Зареждане на статистика...", - "errorLoadingStats": "Грешка при зареждане на статистиката", - "noStatsAvailable": "Няма налична статистика", - "cpuUsage": "Използване на процесора", - "current": "Текущ", - "memoryUsage": "Използване на паметта", - "networkIo": "Мрежов вход/изход", - "input": "Вход", - "output": "Изход", - "blockIo": "Блокиране на входно/изходни данни", - "read": "Прочетете", - "write": "Пишете", - "pids": "PID-ове", - "containerInformation": "Информация за контейнера", - "name": "Име", - "id": "Идентификационен номер", - "state": "Щат", - "containerMustBeRunning": "Контейнерът трябва да е работещ, за да има достъп до конзолата", - "verificationCodePrompt": "Въведете код за потвърждение", - "totpVerificationFailed": "Проверката на TOTP не бе успешна. Моля, опитайте отново.", - "warpgateVerificationFailed": "Удостоверяването на Warpgate не бе успешно. Моля, опитайте отново.", - "connectedTo": "Свързано с {{containerName}}", - "disconnected": "Изключен", - "consoleError": "Грешка в конзолата", - "errorMessage": "Грешка: {{message}}", - "failedToConnect": "Неуспешно свързване с контейнера", - "console": "Конзола", - "selectShell": "Изберете черупка", - "bash": "Баш", - "sh": "ш", - "ash": "пепел", - "connect": "Свържете се", - "disconnect": "Изключване", - "notConnected": "Няма връзка", - "clickToConnect": "Щракнете върху „Свързване“, за да стартирате сесия на shell", - "connectingTo": "Свързване към {{containerName}}...", - "containerNotFound": "Контейнерът не е намерен", - "backToList": "Обратно към списъка", - "logs": "Дневници", - "stats": "Статистика", - "consoleTab": "Конзола", - "startContainerToAccess": "Стартирайте контейнера, за да получите достъп до конзолата" + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Общи", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Потребители", - "sectionSessions": "Сесии", - "sectionRoles": "Роли", - "sectionDatabase": "База данни", - "sectionApiKeys": "API ключове", - "allowRegistration": "Разрешаване на регистрация на потребител", - "allowRegistrationDesc": "Позволете на новите потребители да се регистрират сами", - "allowPasswordLogin": "Разрешаване на вход с парола", - "allowPasswordLoginDesc": "Вход с потребителско име/парола", - "oidcAutoProvision": "Автоматично осигуряване на OIDC", - "oidcAutoProvisionDesc": "Автоматично създаване на акаунти за потребители на OIDC, дори когато регистрацията е деактивирана", - "allowPasswordReset": "Разрешаване на нулиране на паролата", - "allowPasswordResetDesc": "Нулиране на кода чрез Docker логове", - "sessionTimeout": "Време за изчакване на сесията", - "hours": "часове", - "sessionTimeoutRange": "Мин. 1 ч. · Макс. 720 ч.", - "monitoringDefaults": "Мониторинг на настройки по подразбиране", - "statusCheck": "Проверка на състоянието", - "metrics": "Метрики", - "sec": "сек", - "logLevel": "Ниво на лога", - "enableGuacamole": "Активиране на гуакамоле", - "enableGuacamoleDesc": "RDP/VNC отдалечен работен плот", - "guacdUrl": "URL адрес на GUACD", - "oidcDescription": "Конфигурирайте OpenID Connect за SSO. Полетата, маркирани с *, са задължителни.", - "oidcClientId": "Идентификационен номер на клиента", - "oidcClientSecret": "Клиентска тайна", - "oidcAuthUrl": "URL адрес за оторизация", - "oidcIssuerUrl": "URL адрес на издателя", - "oidcTokenUrl": "URL адрес на токена", - "oidcUserIdentifier": "Път на потребителския идентификатор", - "oidcDisplayName": "Път на показваното име", - "oidcScopes": "Обхвати", - "oidcUserinfoUrl": "Замяна на URL адреса на потребителската информация", - "oidcAllowedUsers": "Разрешени потребители", - "oidcAllowedUsersDesc": "По един имейл на ред. Оставете празно, за да разрешите всички.", - "removeOidc": "Премахване", - "usersCount": "{{count}} потребители", - "createUser": "Създаване", - "newRole": "Нова роля", - "roleName": "Име", - "roleDisplayName": "Показвано име", - "roleDescription": "Описание", - "rolesCount": "{{count}} роли", - "createRole": "Създаване", - "creating": "Създаване...", - "exportDatabase": "Експортиране на база данни", - "exportDatabaseDesc": "Изтеглете резервно копие на всички хостове, идентификационни данни и настройки", - "export": "Експорт", - "exporting": "Експортиране...", - "importDatabase": "Импортиране на база данни", - "importDatabaseDesc": "Възстановяване от .sqlite архивен файл", - "importDatabaseSelected": "Избрано: {{name}}", - "selectFile": "Изберете файл", - "changeFile": "Промяна", - "import": "Внос", - "importing": "Импортиране...", - "apiKeysCount": "{{count}} клавиши", - "newApiKey": "Нов API ключ", - "apiKeyCreatedWarning": "Ключът е създаден - копирайте го сега, няма да се показва отново.", - "apiKeyName": "Име", - "apiKeyUser": "Потребител", - "apiKeySelectUser": "Изберете потребител...", - "apiKeyExpiresAt": "Изтича в", - "createKey": "Създаване на ключ", - "apiKeyNoExpiry": "Без срок на годност", - "revokedBadge": "ОТМЕНЕНО", - "authTypeDual": "Двойно удостоверяване", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Местно", - "adminStatusAdministrator": "Администратор", - "adminStatusRegularUser": "Редовен потребител", - "adminBadge": "АДМИНИСТРАТОР", - "systemBadge": "СИСТЕМА", - "customBadge": "ПЕРСОНАЛИЗИРАН", - "youBadge": "ТИ", - "sessionsActive": "{{count}} активен", - "sessionActive": "Активен: {{time}}", - "sessionExpires": "Валидност: {{time}}", - "revokeAll": "Всички", - "revokeAllSessionsSuccess": "Всички сесии за потребителя са отменени", - "revokeAllSessionsFailed": "Неуспешно отменяне на сесиите", - "revokeSessionFailed": "Неуспешно отменяне на сесията", - "addRole": "Добавяне на роля", - "noCustomRoles": "Няма дефинирани персонализирани роли", - "removeRoleFailed": "Премахването на ролята не бе успешно", - "assignRoleFailed": "Присвояването на роля не бе успешно", - "deleteRoleFailed": "Изтриването на ролята не бе успешно", - "userAdminAccess": "Администратор", - "userAdminAccessDesc": "Пълен достъп до всички администраторски настройки", - "userRoles": "Роли", - "revokeAllUserSessions": "Отмяна на всички сесии", - "revokeAllUserSessionsDesc": "Принудително повторно влизане на всички устройства", - "revoke": "Отмяна", - "deleteUserWarning": "Изтриването на този потребител е окончателно.", - "deleteUser": "Изтриване {{username}}", - "deleting": "Изтриване...", - "deleteUserFailed": "Изтриването на потребителя не бе успешно", - "deleteUserSuccess": "Потребителят „{{username}}“ е изтрит", - "deleteRoleSuccess": "Ролята „{{name}}“ е изтрита", - "revokeKeySuccess": "Ключ „{{name}}“ е отменен", - "revokeKeyFailed": "Неуспешно отменяне на ключа", - "copiedToClipboard": "Копирано в буферната памет", - "done": "Готово", - "createUserTitle": "Създаване на потребител", - "createUserDesc": "Създайте нов локален акаунт.", - "createUserUsername": "Потребителско име", - "createUserPassword": "Парола", - "createUserPasswordHint": "Минимум 6 знака.", - "createUserEnterUsername": "Въведете потребителско име", - "createUserEnterPassword": "Въведете парола", - "createUserSubmit": "Създаване на потребител", - "editUserTitle": "Управление на потребител: {{username}}", - "editUserDesc": "Редактиране на роли, администраторски статус, сесии и настройки на акаунта.", - "editUserUsername": "Потребителско име", - "editUserAuthType": "Тип оторизация", - "editUserAdminStatus": "Статус на администратора", - "editUserUserId": "Потребителски идентификатор", - "linkAccountTitle": "Свързване на OIDC с акаунт с парола", - "linkAccountDesc": "Обединете OIDC акаунта {{username}} със съществуващ локален акаунт.", - "linkAccountWarningTitle": "Това ще:", - "linkAccountEffect1": "Изтриване на акаунт само за OIDC", - "linkAccountEffect2": "Добавете OIDC вход към целевия акаунт", - "linkAccountEffect3": "Позволете влизане както с OIDC, така и с парола", - "linkAccountTargetUsername": "Целево потребителско име", - "linkAccountTargetPlaceholder": "Въведете потребителското име на локалния акаунт, към който да се свърже", - "linkAccounts": "Свързване на акаунти", - "linkAccountSuccess": "OIDC акаунт, свързан с „{{username}}“", - "linkAccountFailed": "Свързването на OIDC акаунт не бе успешно", - "linkAccountInProgress": "Свързване...", - "saving": "Запазване...", - "updateRegistrationFailed": "Актуализирането на настройката за регистрация не бе успешно", - "updatePasswordLoginFailed": "Актуализирането на настройката за вход с парола не бе успешно", - "updateOidcAutoProvisionFailed": "Актуализирането на настройката за автоматично предоставяне на OIDC не бе успешно", - "updatePasswordResetFailed": "Актуализирането на настройката за нулиране на паролата не бе успешно", - "sessionTimeoutRange2": "Времето за изчакване на сесията трябва да е между 1 и 720 часа", - "sessionTimeoutSaved": "Времето за изчакване на сесията е запазено", - "sessionTimeoutSaveFailed": "Запазването на времето за изчакване на сесията не бе успешно", - "monitoringIntervalInvalid": "Невалидни интервални стойности", - "monitoringSaved": "Настройките за наблюдение са запазени", - "monitoringSaveFailed": "Запазването на настройките за наблюдение не бе успешно", - "guacamoleSaved": "Настройките за гуакамоле са запазени", - "guacamoleSaveFailed": "Запазването на настройките за гуакамоле не бе успешно", - "guacamoleUpdateFailed": "Актуализирането на настройката за гуакамоле не бе успешно", - "logLevelUpdateFailed": "Актуализирането на нивото на лога не бе успешно", - "oidcSaved": "OIDC конфигурацията е запазена", - "oidcSaveFailed": "Запазването на OIDC конфигурацията не бе успешно", - "oidcRemoved": "OIDC конфигурацията е премахната", - "oidcRemoveFailed": "Неуспешно премахване на OIDC конфигурацията", - "createUserRequired": "Потребителско име и парола са задължителни", - "createUserPasswordTooShort": "Паролата трябва да е поне 6 знака", - "createUserSuccess": "Потребителят „{{username}}“ е създаден", - "createUserFailed": "Създаването на потребител не бе успешно", - "updateAdminStatusFailed": "Актуализирането на администраторския статус не бе успешно", - "allSessionsRevoked": "Всички сесии са отменени", - "revokeSessionsFailed": "Неуспешно отменяне на сесиите", - "createRoleRequired": "Името и показваното име са задължителни", - "createRoleSuccess": "Ролята „{{name}}“ е създадена", - "createRoleFailed": "Създаването на роля не бе успешно", - "apiKeyNameRequired": "Името на ключа е задължително", - "apiKeyUserRequired": "Изисква се потребителско име", - "apiKeyCreatedSuccess": "API ключът „{{name}}“ е създаден", - "apiKeyCreateFailed": "Създаването на API ключ не бе успешно", - "exportSuccess": "Базата данни е експортирана успешно", - "exportFailed": "Експортирането на базата данни не бе успешно", - "importSelectFile": "Моля, първо изберете файл", - "importCompleted": "Импортирането е завършено: {{total}} елемента са импортирани, {{skipped}} са пропуснати", - "importFailed": "Импортирането не бе успешно: {{error}}", - "importError": "Импортирането на базата данни не бе успешно" + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Домакин", - "hostPlaceholder": "192.168.1.1 или example.com", - "portLabel": "Порт", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Потребителско име", - "usernamePlaceholder": "потребителско име", - "authLabel": "Авторизация", - "passwordLabel": "Парола", - "passwordPlaceholder": "парола", - "privateKeyLabel": "Частен ключ", - "privateKeyPlaceholder": "Поставяне на личен ключ...", - "credentialLabel": "Пълномощия", - "credentialPlaceholder": "Изберете запазени идентификационни данни", - "connectToTerminal": "Свързване с терминала", - "connectToFiles": "Свързване с файлове" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Няма избран терминал", - "noTerminalSelectedHint": "Отворете раздел на SSH терминал, за да видите историята на командите му", - "searchPlaceholder": "История на търсенията...", - "clearAll": "Изчисти всички", - "noHistoryEntries": "Няма записи в историята", - "trackingDisabled": "Проследяването на историята е деактивирано", - "trackingDisabledHint": "Активирайте го в настройките на профила си, за да записвате команди." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Запис на ключове", - "recordToTerminals": "Записване към терминали", - "selectAll": "Всички", - "selectNone": "Няма", - "noTerminalTabsOpen": "Няма отворени раздели на терминала", - "selectTerminalsAbove": "Изберете терминали по-горе", - "broadcastInputPlaceholder": "Въведете тук, за да излъчвате натисканията на клавиши...", - "stopRecording": "Спиране на записа", - "startRecording": "Стартиране на запис", - "settingsTitle": "Настройки", - "enableRightClickCopyPaste": "Активиране на копиране/поставяне чрез щракване с десен бутон" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Оформление", - "selectLayoutAbove": "Изберете оформление по-горе", - "selectLayoutHint": "Изберете колко панела да се показват", - "panesTitle": "Прозорци", - "openTabsTitle": "Отворени раздели", - "dragTabsHint": "Плъзнете раздели в панелите по-горе или използвайте „Бързо присвояване“", - "dropHere": "Пуснете тук", - "emptyPane": "Празно", - "dashboard": "Табло за управление", - "clearSplitScreen": "Изчистване на разделения екран", - "quickAssign": "Бързо присвояване", - "alreadyAssigned": "Панел {{index}}", - "splitTab": "Разделяне на раздела", - "addToSplit": "Добави към Сплит", - "removeFromSplit": "Премахване от Сплит", - "assignToPane": "Присвояване към панела" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Създаване на фрагмент", - "createSnippetDescription": "Създайте нов фрагмент от команда за бързо изпълнение", - "nameLabel": "Име", - "namePlaceholder": "например, рестартирайте Nginx", - "descriptionLabel": "Описание", - "descriptionPlaceholder": "Незадължително описание", - "optional": "По избор", - "folderLabel": "Папка", - "noFolder": "Няма папка (Без категория)", - "commandLabel": "Команда", - "commandPlaceholder": "например, sudo systemctl рестартиране на nginx", - "cancel": "Отказ", - "createSnippetButton": "Създаване на фрагмент", - "createFolderTitle": "Създаване на папка", - "createFolderDescription": "Организирайте фрагментите си в папки", - "folderNameLabel": "Име на папката", - "folderNamePlaceholder": "напр. системни команди, Docker скриптове", - "folderColorLabel": "Цвят на папката", - "folderIconLabel": "Икона на папка", - "previewLabel": "Преглед", - "folderNameFallback": "Име на папката", - "createFolderButton": "Създаване на папка", - "targetTerminals": "Целеви терминали", - "selectAll": "Всички", - "selectNone": "Няма", - "noTerminalTabsOpen": "Няма отворени раздели на терминала", - "searchPlaceholder": "Търсене на фрагменти...", - "newSnippet": "Нов фрагмент", - "newFolder": "Нова папка", - "run": "Бягане", - "noSnippetsInFolder": "Няма фрагменти в тази папка", - "uncategorized": "Некатегоризирано", - "editSnippetTitle": "Редактиране на фрагмент", - "editSnippetDescription": "Актуализирайте този фрагмент от командата", - "saveSnippetButton": "Запазване на промените", - "createSuccess": "Фрагментът е създаден успешно", - "createFailed": "Създаването на фрагмент не бе успешно", - "updateSuccess": "Фрагментът е актуализиран успешно", - "updateFailed": "Актуализирането на фрагмента не бе успешно", - "deleteFailed": "Изтриването на фрагмента не бе успешно", - "folderCreateSuccess": "Папката е създадена успешно", - "folderCreateFailed": "Създаването на папка не бе успешно", - "editFolderTitle": "Редактиране на папка", - "editFolderDescription": "Преименувайте или променете външния вид на тази папка", - "saveFolderButton": "Запазване на промените", - "editFolder": "Редактиране на папка", - "deleteFolder": "Изтриване на папка", - "folderDeleteSuccess": "Папка „{{name}}“ е изтрита", - "folderDeleteFailed": "Изтриването на папката не бе успешно", - "folderEditSuccess": "Папката е актуализирана успешно", - "folderEditFailed": "Актуализирането на папката не бе успешно", - "confirmRunMessage": "Изпълни „{{name}}“?", - "confirmRunButton": "Бягане", - "runSuccess": "Изпълни „{{name}}“ в {{count}} терминал(и)", - "copySuccess": "Копирано „{{name}}“ в клипборда", - "shareTitle": "Споделяне на фрагмент", - "shareUser": "Потребител", - "shareRole": "Роля", - "selectUser": "Изберете потребител...", - "selectRole": "Изберете роля...", - "shareSuccess": "Фрагментът е споделен успешно", - "shareFailed": "Споделянето на фрагмента не бе успешно", - "revokeSuccess": "Достъпът е отменен", - "revokeFailed": "Неуспешно отменяне на достъпа", - "currentAccess": "Текущ достъп", - "shareLoadError": "Зареждането на данните за споделяне не бе успешно", - "loading": "Зареждане...", - "close": "Затвори", - "reorderFailed": "Запазването на реда на фрагмента не бе успешно" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Профил", - "sectionAppearance": "Външен вид", - "sectionSecurity": "Сигурност", - "sectionApiKeys": "API ключове", - "sectionC2sTunnels": "Тунели C2S", - "usernameLabel": "Потребителско име", - "roleLabel": "Роля", - "roleAdministrator": "Администратор", - "authMethodLabel": "Метод за удостоверяване", - "authMethodLocal": "Местно", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Включено", - "twoFaOff": "Изключено", - "versionLabel": "Версия", - "deleteAccount": "Изтриване на акаунт", - "deleteAccountDescription": "Изтрийте профила си завинаги", - "deleteButton": "Изтриване", - "deleteAccountPermanent": "Това действие е постоянно и не може да бъде отменено.", - "deleteAccountWarning": "Всички сесии, хостове, идентификационни данни и настройки ще бъдат изтрити за постоянно.", - "confirmPasswordDeletePlaceholder": "Въведете паролата си за потвърждение", - "languageLabel": "Език", - "themeLabel": "Тема", - "fontSizeLabel": "Размер на шрифта", - "accentColorLabel": "Акцентен цвят", - "settingsTerminal": "Терминал", - "commandAutocomplete": "Автоматично довършване на команди", - "commandAutocompleteDesc": "Показване на автоматично довършване при въвеждане", - "historyTracking": "Проследяване на историята", - "historyTrackingDesc": "Проследяване на команди на терминала", - "syntaxHighlighting": "Подчертаване на синтаксиса", - "syntaxHighlightingDesc": "Маркирайте изхода на терминала", - "commandPalette": "Палитра с команди", - "commandPaletteDesc": "Активиране на клавишна комбинация", - "reopenTabsOnLogin": "Отваряне на раздели отново при влизане", - "reopenTabsOnLoginDesc": "Възстановете отворените си раздели, когато влезете или обновите страницата, дори от друго устройство", - "confirmTabClose": "Потвърждаване на затваряне на раздела", - "confirmTabCloseDesc": "Попитай преди затваряне на раздели на терминала", - "settingsSidebar": "Странична лента", - "showHostTags": "Показване на етикети на хоста", - "showHostTagsDesc": "Показване на тагове в списъка с хостове", - "hostTrayOnClick": "Щракнете, за да разгънете действията на хоста", - "hostTrayOnClickDesc": "Винаги показвайте бутоните за връзка; щракнете, за да разгънете опциите за управление, вместо да задържите курсора на мишката върху тях.", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Поддържайте релсата на приложението в лявата странична лента винаги разгъната, вместо да се разширява при задържане на курсора на мишката", - "settingsSnippets": "Откъси", - "foldersCollapsed": "Папките са свити", - "foldersCollapsedDesc": "Свиване на папките по подразбиране", - "confirmExecution": "Потвърждаване на изпълнението", - "confirmExecutionDesc": "Потвърдете преди изпълнение на фрагменти", - "settingsUpdates": "Актуализации", - "disableUpdateChecks": "Деактивиране на проверките за актуализации", - "disableUpdateChecksDesc": "Спрете проверката за актуализации", - "totpAuthenticator": "TOTP удостоверител", - "totpEnabled": "2FA е активирана", - "totpDisabled": "Добавете допълнителна сигурност за вход", - "disable": "Деактивиране", - "enable": "Активиране", - "setupTotp": "Настройка на TOTP", - "qrCode": "QR код", - "totpInstructions": "Сканирайте QR кода или въведете секретния код в приложението си за удостоверяване, след което въведете 6-цифрения код", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Проверка", - "changePassword": "Промяна на паролата", - "currentPasswordLabel": "Текуща парола", - "currentPasswordPlaceholder": "Текуща парола", - "newPasswordLabel": "Нова парола", - "newPasswordPlaceholder": "Нова парола", - "confirmPasswordLabel": "Потвърдете новата парола", - "confirmPasswordPlaceholder": "Потвърдете новата парола", - "updatePassword": "Актуализиране на паролата", - "createApiKeyTitle": "Създаване на API ключ", - "createApiKeyDescription": "Генерирайте нов API ключ за програмен достъп.", - "apiKeyNameLabel": "Име", - "apiKeyNamePlaceholder": "напр. CI тръбопровод", - "expiryDateLabel": "Дата на изтичане", - "optional": "по избор", - "cancel": "Отказ", - "createKey": "Създаване на ключ", - "apiKeyCount": "{{count}} клавиши", - "newKey": "Нов ключ", - "noApiKeys": "Все още няма API ключове.", - "apiKeyActive": "Активен", - "apiKeyUsageHint": "Включете ключа си в", - "apiKeyUsageHintHeader": "заглавка.", - "apiKeyPermissionsHint": "Ключовете наследяват разрешенията на потребителя, който ги е създал.", - "roleUser": "Потребител", - "authMethodDual": "Двойно удостоверяване", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Неуспешно стартиране на настройката на TOTP", - "totpEnter6Digits": "Въведете 6-цифрен код", - "totpEnabledSuccess": "Двуфакторно удостоверяване е активирано", - "totpInvalidCode": "Невалиден код, моля опитайте отново", - "totpDisableInputRequired": "Въведете вашия TOTP код или парола", - "totpDisabledSuccess": "Двуфакторното удостоверяване е деактивирано", - "totpDisableFailed": "Деактивирането на 2FA не бе успешно", - "totpDisableTitle": "Деактивиране на 2FA", - "totpDisablePlaceholder": "Въведете TOTP код или парола", - "totpDisableConfirm": "Деактивиране на 2FA", - "totpContinueVerify": "Продължете с проверката", - "totpVerifyTitle": "Код за проверка", - "totpBackupTitle": "Резервни кодове", - "totpDownloadBackup": "Изтегляне на резервни кодове", - "done": "Готово", - "secretCopied": "Тайната е копирана в клипборда", - "apiKeyNameRequired": "Името на ключа е задължително", - "apiKeyCreated": "API ключът „{{name}}“ е създаден", - "apiKeyCreateFailed": "Създаването на API ключ не бе успешно", - "apiKeyUser": "Потребител", - "apiKeyExpires": "Изтича", - "apiKeyRevoked": "API ключът „{{name}}“ е отменен", - "apiKeyRevokeFailed": "Неуспешно отменяне на API ключа", - "passwordFieldsRequired": "Изискват се текуща и нова парола", - "passwordMismatch": "Паролите не съвпадат", - "passwordTooShort": "Паролата трябва да е поне 6 знака", - "passwordUpdated": "Паролата е актуализирана успешно", - "passwordUpdateFailed": "Актуализирането на паролата не бе успешно", - "deletePasswordRequired": "Изисква се парола за изтриване на акаунта ви", - "deleteFailed": "Изтриването на акаунта не бе успешно", - "deleting": "Изтриване...", - "colorPickerTooltip": "Отваряне на инструмента за избор на цвят", - "themeSystem": "Система", - "themeLight": "Светлина", - "themeDark": "Тъмно", - "themeDracula": "Дракула", - "themeCatppuccin": "Катпучин", - "themeNord": "Норд", - "themeSolarized": "Соларизиран", - "themeTokyoNight": "Токио Нощ", - "themeOneDark": "Една тъмнина", - "themeGruvbox": "Грувбокс" + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/bn_BD.json b/src/ui/locales/translated/bn_BD.json index 8e273fca..9bc5af32 100644 --- a/src/ui/locales/translated/bn_BD.json +++ b/src/ui/locales/translated/bn_BD.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "ফোল্ডার", - "folder": "ফোল্ডার", - "password": "পাসওয়ার্ড", - "key": "চাবি", - "sshPrivateKey": "SSH ব্যক্তিগত কী", - "upload": "আপলোড", - "keyPassword": "চাবি পাসওয়ার্ড", - "sshKey": "SSH কী", - "uploadPrivateKeyFile": "প্রাইভেট কী ফাইল আপলোড করুন", - "searchCredentials": "ক্রেডেনশিয়াল অনুসন্ধান করুন...", - "addCredential": "পরিচয়পত্র যোগ করুন", - "caCertificate": "সিএ সার্টিফিকেট (-cert.pub)", - "caCertificateDescription": "ঐচ্ছিক: CA-স্বাক্ষরিত সার্টিফিকেট ফাইলটি (যেমন id_ed25519-cert.pub) আপলোড বা পেস্ট করুন। আপনার SSH সার্ভার সার্টিফিকেট-ভিত্তিক অনুমোদন ব্যবহার করলে এটি আবশ্যক।", - "uploadCertFile": "আপলোড -cert.pub ফাইল", - "clearCert": "পরিষ্কার", - "certLoaded": "সার্টিফিকেট লোড করা হয়েছে", - "certPublicKeyLabel": "সিএ সার্টিফিকেট", - "certTypeLabel": "সার্টিফিকেটের ধরন", - "pasteOrUploadCert": "একটি -cert.pub সার্টিফিকেট পেস্ট বা আপলোড করুন...", - "hasCaCert": "সিএ সার্টিফিকেট আছে", - "noCaCert": "কোন CA সার্টিফিকেট নেই" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "সতর্কতা লোড করতে ব্যর্থ হয়েছে", - "failedToDismissAlert": "সতর্কতা খারিজ করতে ব্যর্থ হয়েছে" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "সার্ভার কনফিগারেশন", - "description": "আপনার ব্যাকএন্ড পরিষেবাগুলির সাথে সংযোগ করতে Termix সার্ভার URL কনফিগার করুন।", - "serverUrl": "সার্ভার ইউআরএল", - "enterServerUrl": "অনুগ্রহ করে একটি সার্ভার URL লিখুন", - "saveFailed": "কনফিগারেশন সংরক্ষণ করতে ব্যর্থ হয়েছে", - "saveError": "কনফিগারেশন সংরক্ষণ করতে ত্রুটি হয়েছে", - "saving": "সঞ্চয়...", - "saveConfig": "কনফিগারেশন সংরক্ষণ করুন", - "helpText": "আপনার Termix সার্ভারটি যেখানে চলছে সেই URL-টি লিখুন (যেমন, http://localhost:30001 অথবা https://your-server.com)", - "changeServer": "সার্ভার পরিবর্তন করুন", - "mustIncludeProtocol": "সার্ভার URL অবশ্যই http:// অথবা https:// দিয়ে শুরু হতে হবে।", - "allowInvalidCertificate": "অবৈধ সার্টিফিকেট অনুমোদন করুন", - "allowInvalidCertificateDesc": "শুধুমাত্র সেলফ-সাইন্ড বা আইপি-অ্যাড্রেস সার্টিফিকেটযুক্ত বিশ্বস্ত সেলফ-হোস্টেড সার্ভারের জন্য ব্যবহার করুন।", - "useEmbedded": "স্থানীয় সার্ভার ব্যবহার করুন", - "embeddedDesc": "অন্তর্নির্মিত স্থানীয় সার্ভার দিয়ে টারমিক্স চালান (কোন রিমোট সার্ভারের প্রয়োজন নেই)।", - "embeddedConnecting": "স্থানীয় সার্ভারের সাথে সংযোগ স্থাপন করা হচ্ছে...", - "embeddedNotReady": "স্থানীয় সার্ভার এখনও প্রস্তুত নয়। অনুগ্রহ করে কিছুক্ষণ অপেক্ষা করে আবার চেষ্টা করুন।", - "localServer": "স্থানীয় সার্ভার" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "সংস্করণ যাচাই ত্রুটি", - "checkFailed": "আপডেট পরীক্ষা করতে ব্যর্থ হয়েছে", - "upToDate": "অ্যাপটি হালনাগাদ করা হয়েছে", - "currentVersion": "আপনি {{version}} সংস্করণটি চালাচ্ছেন।", - "updateAvailable": "আপডেট উপলব্ধ", - "newVersionAvailable": "একটি নতুন সংস্করণ উপলব্ধ! আপনি {{current}}ব্যবহার করছেন, কিন্তু {{latest}} উপলব্ধ আছে।", - "betaVersion": "বিটা সংস্করণ", - "betaVersionDesc": "আপনি {{current}}ব্যবহার করছেন, যা সর্বশেষ স্থিতিশীল রিলিজ {{latest}} এর চেয়ে নতুন।", - "releasedOn": "{{date}} তারিখে প্রকাশিত।", - "downloadUpdate": "আপডেট ডাউনলোড করুন", - "checking": "আপডেট যাচাই করা হচ্ছে...", - "checkUpdates": "আপডেটগুলির জন্য পরীক্ষা করুন", - "checkingUpdates": "আপডেট যাচাই করা হচ্ছে...", - "updateRequired": "আপডেট প্রয়োজন" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "বন্ধ করুন", - "minimize": "ন্যূনতম করুন", - "online": "অনলাইন", - "offline": "অফলাইন", - "continue": "চালিয়ে যান", - "maintenance": "রক্ষণাবেক্ষণ", - "degraded": "অবনমিত", - "error": "ত্রুটি", - "warning": "সতর্কতা", - "unsavedChanges": "সংরক্ষিত নয় এমন পরিবর্তন", - "dismiss": "খারিজ করুন", - "loading": "লোড হচ্ছে...", - "optional": "ঐচ্ছিক", - "connect": "সংযোগ করুন", - "copied": "অনুলিপি করা হয়েছে", - "connecting": "সংযোগ স্থাপন...", - "updateAvailable": "আপডেট উপলব্ধ", - "appName": "টার্মিক্স", - "openInNewTab": "নতুন ট্যাবে খুলুন", - "noReleases": "কোন রিলিজ নেই", - "updatesAndReleases": "আপডেট এবং রিলিজ", - "newVersionAvailable": "একটি নতুন সংস্করণ ({{version}}) পাওয়া যাচ্ছে।", - "failedToFetchUpdateInfo": "আপডেট তথ্য আনতে ব্যর্থ হয়েছে", - "preRelease": "প্রি-রিলিজ", - "noReleasesFound": "কোনো রিলিজ খুঁজে পাওয়া যায়নি।", - "cancel": "বাতিল করুন", - "username": "ব্যবহারকারীর নাম", - "login": "লগইন", - "register": "নিবন্ধন করুন", - "password": "পাসওয়ার্ড", - "confirmPassword": "পাসওয়ার্ড নিশ্চিত করুন", - "back": "ফিরে যান", - "save": "সংরক্ষণ করুন", - "saving": "সঞ্চয়...", - "delete": "মুছে ফেলুন", - "rename": "নাম পরিবর্তন", - "edit": "সম্পাদনা", - "add": "যোগ করুন", - "confirm": "নিশ্চিত করুন", - "no": "না", - "or": "অথবা", - "next": "পরবর্তী", - "previous": "পূর্ববর্তী", - "refresh": "রিফ্রেশ", - "language": "ভাষা", - "checking": "যাচাই করা হচ্ছে...", - "checkingDatabase": "ডাটাবেস সংযোগ পরীক্ষা করা হচ্ছে...", - "checkingAuthentication": "প্রমাণীকরণ যাচাই করা হচ্ছে...", - "backendReconnected": "সার্ভার সংযোগ পুনরুদ্ধার করা হয়েছে", - "connectionDegraded": "সার্ভার সংযোগ বিচ্ছিন্ন, পুনরুদ্ধার করা হচ্ছে…", - "reload": "পুনরায় লোড করুন", - "remove": "অপসারণ করুন", - "create": "তৈরি করুন", - "update": "আপডেট", - "copy": "অনুলিপি", - "copyFailed": "ক্লিপবোর্ডে কপি করতে ব্যর্থ হয়েছে", - "maximize": "সর্বাধিক করুন", - "restore": "পুনরুদ্ধার করুন", - "of": "এর" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "হোম", - "terminal": "টার্মিনাল", - "docker": "ডকার", - "tunnels": "টানেল", - "fileManager": "ফাইল ম্যানেজার", - "serverStats": "সার্ভার পরিসংখ্যান", - "admin": "প্রশাসক", - "userProfile": "ব্যবহারকারীর প্রোফাইল", - "splitScreen": "স্প্লিট স্ক্রিন", - "confirmClose": "এই সক্রিয় সেশনটি বন্ধ করবেন?", - "close": "বন্ধ করুন", - "cancel": "বাতিল করুন", - "sshManager": "এসএসএইচ ম্যানেজার", - "cannotSplitTab": "এই ট্যাবটি ভাগ করা যাবে না", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "পাসওয়ার্ড কপি করুন", - "copySudoPassword": "Sudo পাসওয়ার্ড কপি করুন", - "passwordCopied": "পাসওয়ার্ড ক্লিপবোর্ডে কপি করা হয়েছে", - "noPasswordAvailable": "কোনো পাসওয়ার্ড উপলব্ধ নেই", - "failedToCopyPassword": "পাসওয়ার্ড কপি করতে ব্যর্থ হয়েছে", - "refreshTab": "সংযোগ রিফ্রেশ করুন", - "openFileManager": "ফাইল ম্যানেজার খুলুন", - "dashboard": "ড্যাশবোর্ড", - "networkGraph": "নেটওয়ার্ক গ্রাফ", - "quickConnect": "কুইক কানেক্ট", - "sshTools": "SSH টুলস", - "history": "ইতিহাস", - "hosts": "হোস্টরা", - "snippets": "খণ্ডাংশ", - "hostManager": "হোস্ট ম্যানেজার", - "credentials": "যোগ্যতা", - "connections": "সংযোগ", - "roleAdministrator": "প্রশাসক", - "roleUser": "ব্যবহারকারী" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "হোস্টরা", - "noHosts": "কোন SSH হোস্ট নেই", - "retry": "পুনরায় চেষ্টা করুন", - "refresh": "রিফ্রেশ", - "optional": "ঐচ্ছিক", - "downloadSample": "নমুনা ডাউনলোড করুন", - "failedToDeleteHost": "{{name}} মুছে ফেলতে ব্যর্থ হয়েছে", - "importSkipExisting": "আমদানি করুন (বিদ্যমানটি বাদ দিন)", - "connectionDetails": "সংযোগের বিবরণ", - "ssh": "এসএসএইচ", - "telnet": "টেলনেট", - "remoteDesktop": "রিমোট ডেস্কটপ", - "port": "বন্দর", - "username": "ব্যবহারকারীর নাম", - "folder": "ফোল্ডার", - "tags": "ট্যাগ", - "pin": "পিন", - "addHost": "হোস্ট যোগ করুন", - "editHost": "হোস্ট সম্পাদনা করুন", - "cloneHost": "ক্লোন হোস্ট", - "enableTerminal": "টার্মিনাল সক্রিয় করুন", - "enableTunnel": "টানেল সক্রিয় করুন", - "enableFileManager": "ফাইল ম্যানেজার সক্রিয় করুন", - "enableDocker": "ডকার সক্রিয় করুন", - "defaultPath": "ডিফল্ট পথ", - "connection": "সংযোগ", - "upload": "আপলোড", - "authentication": "প্রমাণীকরণ", - "password": "পাসওয়ার্ড", - "key": "চাবি", - "credential": "শংসাপত্র", - "none": "কোনোটিই না", - "sshPrivateKey": "SSH ব্যক্তিগত কী", - "keyType": "চাবির ধরন", - "uploadFile": "ফাইল আপলোড করুন", - "tabGeneral": "সাধারণ", - "tabSsh": "এসএসএইচ", - "tabRdp": "আরডিপি", - "tabVnc": "ভিএনসি", - "tabTunnels": "টানেল", - "tabDocker": "ডকার", - "tabFiles": "ফাইল", - "tabStats": "পরিসংখ্যান", - "tabTelnet": "টেলনেট", - "tabSharing": "শেয়ারিং", - "tabAuthentication": "প্রমাণীকরণ", - "terminal": "টার্মিনাল", - "tunnel": "টানেল", - "fileManager": "ফাইল ম্যানেজার", - "serverStats": "সার্ভার পরিসংখ্যান", - "status": "অবস্থা", - "folderRenamed": "\"{{oldName}}\" ফোল্ডারটির নাম সফলভাবে \"{{newName}}\" রাখা হয়েছে।", - "failedToRenameFolder": "ফোল্ডারের নাম পরিবর্তন করতে ব্যর্থ হয়েছে", - "movedToFolder": "\"{{folder}} \" এ স্থানান্তরিত হয়েছে", - "editHostTooltip": "হোস্ট সম্পাদনা করুন", - "statusChecks": "অবস্থা যাচাই", - "metricsCollection": "মেট্রিক্স সংগ্রহ", - "metricsInterval": "মেট্রিক্স সংগ্রহের ব্যবধান", - "metricsIntervalDesc": "কত ঘন ঘন সার্ভার পরিসংখ্যান সংগ্রহ করতে হবে (৫ সেকেন্ড - ১ ঘণ্টা)", - "behavior": "আচরণ", - "themePreview": "থিম প্রিভিউ", - "theme": "থিম", - "fontFamily": "ফন্ট পরিবার", - "fontSize": "ফন্ট সাইজ", - "letterSpacing": "চিঠির ব্যবধান", - "lineHeight": "লাইন উচ্চতা", - "cursorStyle": "কার্সার স্টাইল", - "cursorBlink": "কার্সার ব্লিঙ্ক", - "scrollbackBuffer": "স্ক্রোলব্যাক বাফার", - "bellStyle": "বেল স্টাইল", - "rightClickSelectsWord": "ডান ক্লিক করলে ওয়ার্ড নির্বাচিত হয়।", - "fastScrollModifier": "দ্রুত স্ক্রোল মডিফায়ার", - "fastScrollSensitivity": "দ্রুত স্ক্রোল সংবেদনশীলতা", - "sshAgentForwarding": "SSH এজেন্ট ফরওয়ার্ডিং", - "backspaceMode": "ব্যাকস্পেস মোড", - "startupSnippet": "স্টার্টআপ স্নিপেট", - "selectSnippet": "স্নিপেট নির্বাচন করুন", - "forceKeyboardInteractive": "ফোর্স কীবোর্ড-ইন্টারেক্টিভ", - "overrideCredentialUsername": "ক্রেডেনশিয়াল ব্যবহারকারীর নাম ওভাররাইড করুন", - "overrideCredentialUsernameDesc": "ক্রেডেনশিয়ালের ইউজারনেমের পরিবর্তে উপরে উল্লেখিত ইউজারনেমটি ব্যবহার করুন।", - "jumpHostChain": "জাম্প হোস্ট চেইন", - "portKnocking": "পোর্ট নকিং", - "addKnock": "পোর্ট যোগ করুন", - "addProxyNode": "নোড যোগ করুন", - "proxyNode": "প্রক্সি নোড", - "proxyType": "প্রক্সি টাইপ", - "quickActions": "দ্রুত পদক্ষেপ", - "sudoPasswordAutoFill": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ", - "sudoPassword": "সুডো পাসওয়ার্ড", - "keepaliveInterval": "কিপঅ্যালাইভ ব্যবধান (মিলিসেকেন্ড)", - "moshCommand": "মোশ কমান্ড", - "environmentVariables": "পরিবেশগত পরিবর্তনশীল", - "addVariable": "ভেরিয়েবল যোগ করুন", - "docker": "ডকার", - "copyTerminalUrl": "টার্মিনাল ইউআরএল কপি করুন", - "copyFileManagerUrl": "ফাইল ম্যানেজার ইউআরএল কপি করুন", - "copyRemoteDesktopUrl": "রিমোট ডেস্কটপ ইউআরএল কপি করুন", - "failedToConnect": "কনসোলে সংযোগ করতে ব্যর্থ হয়েছে", - "connect": "সংযোগ করুন", - "disconnect": "সংযোগ বিচ্ছিন্ন করুন", - "start": "শুরু করুন", - "enableStatusCheck": "স্ট্যাটাস চেক সক্ষম করুন", - "enableMetrics": "মেট্রিক্স সক্ষম করুন", - "bulkUpdateFailed": "বাল্ক আপডেট ব্যর্থ হয়েছে", - "selectAll": "সব নির্বাচন করুন", - "deselectAll": "সবগুলো নির্বাচন বাতিল করুন", - "protocols": "প্রোটোকল", - "secureShell": "সুরক্ষিত খোলস", - "virtualNetwork": "ভার্চুয়াল নেটওয়ার্ক", - "unencryptedShell": "এনক্রিপ্টবিহীন শেল", - "addressIp": "ঠিকানা / আইপি", - "friendlyName": "বন্ধুত্বপূর্ণ নাম", - "folderAndAdvanced": "ফোল্ডার এবং উন্নত", - "privateNotes": "ব্যক্তিগত নোট", - "privateNotesPlaceholder": "এই সার্ভার সম্পর্কে বিস্তারিত...", - "pinToTop": "শীর্ষে পিন করুন", - "pinToTopDesc": "এই হোস্টকে সর্বদা তালিকার শীর্ষে দেখান।", - "portKnockingSequence": "পোর্ট নকিং সিকোয়েন্স", - "addKnockBtn": "নক যোগ করুন", - "noPortKnocking": "কোন পোর্ট নকিং কনফিগার করা নেই।", - "knockPort": "নক পোর্ট", - "protocol": "প্রোটোকল", - "delayAfterMs": "বিলম্বের পর (মিলিসেকেন্ড)", - "useSocks5Proxy": "SOCKS5 প্রক্সি ব্যবহার করুন", - "useSocks5ProxyDesc": "প্রক্সি সার্ভারের মাধ্যমে সংযোগ স্থাপন করুন", - "proxyHost": "প্রক্সি হোস্ট", - "proxyPort": "প্রক্সি পোর্ট", - "proxyUsername": "প্রক্সি ব্যবহারকারীর নাম", - "proxyPassword": "প্রক্সি পাসওয়ার্ড", - "proxySingleMode": "একক প্রক্সি", - "proxyChainMode": "প্রক্সি চেইন", - "you": "তুমি", - "jumpHostChainLabel": "জাম্প হোস্ট চেইন", - "addJumpBtn": "জাম্প যোগ করুন", - "noJumpHosts": "কোনো জাম্প হোস্ট কনফিগার করা নেই।", - "selectAServer": "একটি সার্ভার নির্বাচন করুন...", - "sshPort": "SSH পোর্ট", - "authMethod": "প্রমাণীকরণ পদ্ধতি", - "storedCredential": "সংরক্ষিত পরিচয়পত্র", - "selectACredential": "একটি শংসাপত্র নির্বাচন করুন...", - "keyTypeLabel": "চাবির ধরন", - "keyTypeAuto": "স্বয়ংক্রিয়ভাবে সনাক্ত করুন", - "keyPasteTab": "পেস্ট", - "keyUploadTab": "আপলোড", - "keyFileLoaded": "মূল ফাইল লোড করা হয়েছে", - "keyUploadClick": ".pem / .key / .ppk আপলোড করতে ক্লিক করুন", - "clearKey": "পরিষ্কার চাবি", - "keySaved": "SSH কী সংরক্ষিত আছে", - "keyReplaceNotice": "এটি প্রতিস্থাপন করতে নিচে একটি নতুন কী পেস্ট করুন", - "keyPassphraseSaved": "পাসফ্রেজ সংরক্ষিত হয়েছে, পরিবর্তন করতে টাইপ করুন", - "replaceKey": "চাবি প্রতিস্থাপন করুন", - "forceKeyboardInteractiveLabel": "ফোর্স কীবোর্ড ইন্টারঅ্যাক্টিভ", - "forceKeyboardInteractiveShortDesc": "চাবি উপস্থিত থাকলেও ম্যানুয়াল পাসওয়ার্ড এন্ট্রি বাধ্যতামূলক করুন।", - "terminalAppearance": "টার্মিনাল চেহারা", - "colorTheme": "রঙের থিম", - "fontFamilyLabel": "ফন্ট পরিবার", - "fontSizeLabel": "ফন্ট সাইজ", - "cursorStyleLabel": "কার্সার স্টাইল", - "letterSpacingPx": "অক্ষরের ব্যবধান (পিক্সেল)", - "lineHeightLabel": "লাইন উচ্চতা", - "bellStyleLabel": "বেল স্টাইল", - "backspaceModeLabel": "ব্যাকস্পেস মোড", - "cursorBlinking": "কার্সার মিটমিট করা", - "cursorBlinkingDesc": "টার্মিনাল কার্সারের জন্য ব্লিংকিং অ্যানিমেশন সক্রিয় করুন।", - "rightClickSelectsWordLabel": "ডান-ক্লিক করলে Word নির্বাচিত হয়।", - "rightClickSelectsWordShortDesc": "কার্সরের নিচে থাকা শব্দটি ডান-ক্লিক করে নির্বাচন করুন।", - "behaviorAndAdvanced": "আচরণ ও উন্নত", - "scrollbackBufferLabel": "স্ক্রোলব্যাক বাফার", - "scrollbackMaxLines": "ইতিহাসে সংরক্ষিত সর্বাধিক সংখ্যক লাইন", - "sshAgentForwardingLabel": "SSH এজেন্ট ফরওয়ার্ডিং", - "sshAgentForwardingShortDesc": "আপনার স্থানীয় SSH কীগুলি এই হোস্টে পাঠান।", - "enableAutoMosh": "অটো-মোশ সক্রিয় করুন", - "enableAutoMoshDesc": "উপলব্ধ থাকলে SSH-এর চেয়ে Mosh ব্যবহার করা শ্রেয়।", - "enableAutoTmux": "অটো-Tmux সক্রিয় করুন", - "enableAutoTmuxDesc": "স্বয়ংক্রিয়ভাবে tmux সেশন চালু করুন বা সংযুক্ত করুন", - "sudoPasswordAutoFillLabel": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ", - "sudoPasswordAutoFillShortDesc": "অনুরোধ করা হলে স্বয়ংক্রিয়ভাবে sudo পাসওয়ার্ড প্রদান করুন।", - "sudoPasswordLabel": "সুডো পাসওয়ার্ড", - "environmentVariablesLabel": "পরিবেশগত পরিবর্তনশীল", - "addVariableBtn": "ভেরিয়েবল যোগ করুন", - "noEnvVars": "কোনো এনভায়রনমেন্ট ভেরিয়েবল কনফিগার করা নেই।", - "fastScrollModifierLabel": "দ্রুত স্ক্রোল মডিফায়ার", - "fastScrollSensitivityLabel": "দ্রুত স্ক্রোল সংবেদনশীলতা", - "moshCommandLabel": "মোশ কমান্ড", - "startupSnippetLabel": "স্টার্টআপ স্নিপেট", - "keepaliveIntervalLabel": "কিপঅ্যালাইভ ব্যবধান (সেকেন্ড)", - "maxKeepaliveMisses": "ম্যাক্স কিপঅ্যালাইভ মিস করে", - "tunnelSettings": "টানেল সেটিংস", - "enableTunneling": "টানেলিং সক্ষম করুন", - "enableTunnelingDesc": "এই হোস্টের জন্য SSH টানেল কার্যকারিতা সক্রিয় করুন", - "serverTunnelsSection": "সার্ভার টানেল", - "addTunnelBtn": "টানেল যোগ করুন", - "noTunnelsConfigured": "কোনো টানেল কনফিগার করা নেই।", - "tunnelLabel": "টানেল {{number}}", - "tunnelType": "টানেল টাইপ", - "tunnelModeLocalDesc": "একটি স্থানীয় পোর্টকে রিমোট সার্ভারের (বা সেখান থেকে পৌঁছানো যায় এমন কোনো হোস্টের) একটি পোর্টে ফরওয়ার্ড করুন।", - "tunnelModeRemoteDesc": "রিমোট সার্ভারের একটি পোর্ট আপনার মেশিনের লোকাল পোর্টে ফরওয়ার্ড করুন।", - "tunnelModeDynamicDesc": "ডাইনামিক পোর্ট ফরওয়ার্ডিংয়ের জন্য লোকাল পোর্টে একটি SOCKS5 প্রক্সি তৈরি করুন।", - "sameHost": "এই হোস্ট (সরাসরি টানেল)", - "endpointHost": "এন্ডপয়েন্ট হোস্ট", - "endpointPort": "এন্ডপয়েন্ট পোর্ট", - "bindHost": "হোস্টকে বাঁধুন", - "sourcePort": "উৎস বন্দর", - "maxRetries": "সর্বোচ্চ পুনঃপ্রচেষ্টা", - "retryIntervalS": "পুনরায় চেষ্টার ব্যবধান (সেকেন্ড)", - "autoStartLabel": "স্বয়ংক্রিয়-শুরু", - "autoStartDesc": "হোস্ট লোড হলে এই টানেলটি স্বয়ংক্রিয়ভাবে সংযুক্ত করুন।", - "tunnelConnecting": "সংযোগকারী টানেল...", - "tunnelDisconnected": "টানেল বিচ্ছিন্ন", - "failedToConnectTunnel": "সংযোগ করতে ব্যর্থ হয়েছে", - "failedToDisconnectTunnel": "সংযোগ বিচ্ছিন্ন করতে ব্যর্থ হয়েছে", - "dockerIntegration": "ডকার ইন্টিগ্রেশন", - "enableDockerMonitor": "ডকার সক্রিয় করুন", - "enableDockerMonitorDesc": "ডকারের মাধ্যমে এই হোস্টে কন্টেইনারগুলি নিরীক্ষণ ও পরিচালনা করুন।", - "enableFileManagerMonitor": "ফাইল ম্যানেজার সক্রিয় করুন", - "enableFileManagerMonitorDesc": "SFTP-এর মাধ্যমে এই হোস্টের ফাইলগুলি ব্রাউজ ও পরিচালনা করুন।", - "defaultPathLabel": "ডিফল্ট পথ", - "fileManagerPathHint": "এই হোস্টের জন্য ফাইল ম্যানেজার চালু হলে যে ডিরেক্টরিটি খুলবে।", - "statusChecksLabel": "অবস্থা যাচাই", - "enableStatusChecks": "স্ট্যাটাস চেক সক্ষম করুন", - "enableStatusChecksDesc": "প্রাপ্যতা যাচাই করতে পর্যায়ক্রমে এই হোস্টটিকে পিং করুন।", - "useGlobalInterval": "গ্লোবাল ইন্টারভাল ব্যবহার করুন", - "useGlobalIntervalDesc": "সার্ভার-ব্যাপী স্ট্যাটাস চেক ব্যবধান দিয়ে ওভাররাইড করুন", - "checkIntervalS": "চেক ব্যবধান (গুলি)", - "checkIntervalDesc": "প্রতিটি সংযোগ পিং এর মধ্যে সেকেন্ড", - "metricsCollectionLabel": "মেট্রিক্স সংগ্রহ", - "enableMetricsLabel": "মেট্রিক্স সক্ষম করুন", - "enableMetricsDesc": "এই হোস্ট থেকে সিপিইউ, র‍্যাম, ডিস্ক এবং নেটওয়ার্ক ব্যবহারের তথ্য সংগ্রহ করুন।", - "useGlobalMetrics": "গ্লোবাল ইন্টারভাল ব্যবহার করুন", - "useGlobalMetricsDesc": "সার্ভার-ব্যাপী মেট্রিক্স ব্যবধান দিয়ে ওভাররাইড করুন", - "metricsIntervalS": "মেট্রিক্স ব্যবধান (সেকেন্ড)", - "metricsIntervalDesc2": "মেট্রিক স্ন্যাপশটগুলির মধ্যে সেকেন্ডের ব্যবধান", - "visibleWidgets": "দৃশ্যমান উইজেট", - "cpuUsageLabel": "সিপিইউ ব্যবহার", - "cpuUsageDesc": "সিপিইউ শতাংশ, লোড গড়, স্পার্কলাইন গ্রাফ", - "memoryLabel": "মেমরি ব্যবহার", - "memoryDesc": "র‍্যাম ব্যবহার, সোয়াপ, ক্যাশড", - "storageLabel": "ডিস্ক ব্যবহার", - "storageDesc": "প্রতি মাউন্ট পয়েন্টে ডিস্কের ব্যবহার", - "networkLabel": "নেটওয়ার্ক ইন্টারফেস", - "networkDesc": "ইন্টারফেস তালিকা এবং ব্যান্ডউইথ", - "uptimeLabel": "আপটাইম", - "uptimeDesc": "সিস্টেম আপটাইম এবং বুট টাইম", - "systemInfoLabel": "সিস্টেমের তথ্য", - "systemInfoDesc": "ওএস, কার্নেল, হোস্টনেম, আর্কিটেকচার", - "recentLoginsLabel": "সাম্প্রতিক লগইন", - "recentLoginsDesc": "সফল এবং ব্যর্থ লগইন ইভেন্ট", - "topProcessesLabel": "শীর্ষ প্রক্রিয়া", - "topProcessesDesc": "পিআইডি, সিপিইউ%, মেম%, কমান্ড", - "listeningPortsLabel": "লিসেনিং পোর্ট", - "listeningPortsDesc": "প্রসেস এবং স্টেট দিয়ে পোর্টগুলো খুলুন।", - "firewallLabel": "ফায়ারওয়াল", - "firewallDesc": "ফায়ারওয়াল, অ্যাপআর্মর, এসইলিনাক্স স্ট্যাটাস", - "quickActionsLabel": "দ্রুত পদক্ষেপ", - "quickActionsToolbar": "এক ক্লিকে কমান্ড কার্যকর করার জন্য কুইক অ্যাকশনগুলো সার্ভার স্ট্যাটস টুলবারে বাটন হিসেবে প্রদর্শিত হয়।", - "noQuickActions": "এখনো কোনো দ্রুত পদক্ষেপ নেওয়া হয়নি।", - "buttonLabel": "বোতাম লেবেল", - "selectSnippetPlaceholder": "স্নিপেট নির্বাচন করুন...", - "addActionBtn": "অ্যাকশন যোগ করুন", - "hostSharedSuccessfully": "হোস্ট সফলভাবে শেয়ার করেছে", - "failedToShareHost": "হোস্ট শেয়ার করতে ব্যর্থ হয়েছে", - "accessRevoked": "প্রবেশাধিকার বাতিল করা হয়েছে", - "failedToRevokeAccess": "অ্যাক্সেস প্রত্যাহার করতে ব্যর্থ হয়েছে", - "cancelBtn": "বাতিল করুন", - "savingBtn": "সঞ্চয়...", - "addHostBtn": "হোস্ট যোগ করুন", - "hostUpdated": "হোস্ট আপডেট করা হয়েছে", - "hostCreated": "হোস্ট তৈরি করেছে", - "failedToSave": "হোস্ট সংরক্ষণ করতে ব্যর্থ হয়েছে", - "credentialUpdated": "পরিচয়পত্র আপডেট করা হয়েছে", - "credentialCreated": "পরিচয়পত্র তৈরি করা হয়েছে", - "failedToSaveCredential": "পরিচয়পত্র সংরক্ষণ করতে ব্যর্থ হয়েছে", - "backToHosts": "হোস্টদের কাছে ফিরে যান", - "backToCredentials": "পরিচয়পত্রে ফিরে যান", - "pinned": "পিন করা", - "noHostsFound": "কোন হোস্ট খুঁজে পাওয়া যায়নি", - "tryDifferentTerm": "অন্য একটি শব্দ ব্যবহার করে দেখুন", - "addFirstHost": "শুরু করার জন্য আপনার প্রথম হোস্ট যোগ করুন।", - "noCredentialsFound": "কোনো পরিচয়পত্র খুঁজে পাওয়া যায়নি", - "addCredentialBtn": "পরিচয়পত্র যোগ করুন", - "updateCredentialBtn": "পরিচয়পত্র আপডেট করুন", - "features": "বৈশিষ্ট্য", - "noFolder": "(কোন ফোল্ডার নেই)", - "deleteSelected": "মুছে ফেলুন", - "exitSelection": "প্রস্থান নির্বাচন", - "importSkip": "আমদানি করুন (বিদ্যমানটি বাদ দিন)", - "importOverwrite": "আমদানি (ওভাররাইট)", - "collapseBtn": "ভেঙে পড়া", - "importExportBtn": "আমদানি / রপ্তানি", - "hostStatusesRefreshed": "হোস্টের স্ট্যাটাস রিফ্রেশ করা হয়েছে", - "failedToRefreshHosts": "হোস্টগুলি রিফ্রেশ করতে ব্যর্থ হয়েছে", - "movedHostTo": "{{host}} কে \"{{folder}} \" এ সরানো হয়েছে", - "failedToMoveHost": "হোস্ট স্থানান্তর করতে ব্যর্থ হয়েছে", - "folderRenamedTo": "ফোল্ডারের নাম পরিবর্তন করে \"{{name}} \" রাখা হয়েছে", - "deletedFolder": "মুছে ফেলা ফোল্ডার \"{{name}}\"", - "failedToDeleteFolder": "ফোল্ডারটি মুছে ফেলা ব্যর্থ হয়েছে", - "deleteAllInFolder": "\"{{name}}\" এর মধ্যে থাকা সমস্ত হোস্ট মুছে ফেলবেন? এটি আর পূর্বাবস্থায় ফেরানো যাবে না।", - "deletedHost": "মুছে ফেলা হয়েছে {{name}}", - "copiedToClipboard": "ক্লিপবোর্ডে কপি করা হয়েছে", - "terminalUrlCopied": "টার্মিনাল ইউআরএল কপি করা হয়েছে", - "fileManagerUrlCopied": "ফাইল ম্যানেজার ইউআরএল কপি করা হয়েছে", - "tunnelUrlCopied": "টানেল ইউআরএল কপি করা হয়েছে", - "dockerUrlCopied": "ডকার ইউআরএল কপি করা হয়েছে", - "serverStatsUrlCopied": "সার্ভার পরিসংখ্যান ইউআরএল কপি করা হয়েছে", - "rdpUrlCopied": "RDP URL কপি করা হয়েছে", - "vncUrlCopied": "VNC URL কপি করা হয়েছে", - "telnetUrlCopied": "টেলনেট ইউআরএল কপি করা হয়েছে", - "remoteDesktopUrlCopied": "রিমোট ডেস্কটপ ইউআরএল কপি করা হয়েছে", - "expandActions": "ক্রিয়া প্রসারিত করুন", - "collapseActions": "ভেঙে পড়ার ক্রিয়া", - "wakeOnLanAction": "ওয়েক অন ল্যান", - "wakeOnLanSuccess": "{{name}}-এ জাদুর প্যাকেট পাঠানো হয়েছে", - "wakeOnLanError": "ম্যাজিক প্যাকেট পাঠাতে ব্যর্থ হয়েছে", - "cloneHostAction": "ক্লোন হোস্ট", - "copyAddress": "ঠিকানা কপি করুন", - "copyLink": "লিঙ্ক কপি করুন", - "copyTerminalUrlAction": "টার্মিনাল ইউআরএল কপি করুন", - "copyFileManagerUrlAction": "ফাইল ম্যানেজার ইউআরএল কপি করুন", - "copyTunnelUrlAction": "টানেল ইউআরএল কপি করুন", - "copyDockerUrlAction": "ডকার ইউআরএল কপি করুন", - "copyServerStatsUrlAction": "সার্ভার পরিসংখ্যান URL কপি করুন", - "copyRdpUrlAction": "RDP URL কপি করুন", - "copyVncUrlAction": "VNC URL কপি করুন", - "copyTelnetUrlAction": "টেলনেট ইউআরএল কপি করুন", - "copyRemoteDesktopUrlAction": "রিমোট ডেস্কটপ ইউআরএল কপি করুন", - "deleteCredentialConfirm": "ক্রেডেনশিয়াল \"{{name}} \" মুছে ফেলুন?", - "deletedCredential": "মুছে ফেলা হয়েছে {{name}}", - "deploySSHKeyTitle": "SSH কী স্থাপন করুন", - "deployingBtn": "মোতায়েন করা হচ্ছে...", - "deployBtn": "মোতায়েন করুন", - "failedToDeployKey": "কী স্থাপন করতে ব্যর্থ হয়েছে", - "deleteHostsConfirm": "{{count}} হোস্ট{{plural}}মুছে ফেলবেন? এটি পূর্বাবস্থায় ফেরানো যাবে না।", - "movedToRoot": "রুটে স্থানান্তরিত হয়েছে", - "failedToMoveHosts": "হোস্ট স্থানান্তর করতে ব্যর্থ হয়েছে", - "enableTerminalFeature": "টার্মিনাল সক্রিয় করুন", - "disableTerminalFeature": "টার্মিনাল নিষ্ক্রিয় করুন", - "enableFilesFeature": "ফাইলগুলি সক্ষম করুন", - "disableFilesFeature": "ফাইলগুলি নিষ্ক্রিয় করুন", - "enableTunnelsFeature": "টানেলগুলি সক্ষম করুন", - "disableTunnelsFeature": "টানেল নিষ্ক্রিয় করুন", - "enableDockerFeature": "ডকার সক্রিয় করুন", - "disableDockerFeature": "ডকার নিষ্ক্রিয় করুন", - "addTagsPlaceholder": "ট্যাগ যোগ করুন...", - "authDetails": "প্রমাণীকরণের বিবরণ", - "credType": "প্রকার", - "generateKeyPairDesc": "একটি নতুন কী পেয়ার তৈরি করুন, প্রাইভেট এবং পাবলিক কী উভয়ই স্বয়ংক্রিয়ভাবে পূরণ হয়ে যাবে।", - "generatingKey": "তৈরি হচ্ছে...", - "generateLabel": "{{label}} তৈরি করুন", - "uploadFileBtn": "ফাইল আপলোড করুন", - "keyPassphraseOptional": "মূল পাসফ্রেজ (ঐচ্ছিক)", - "sshPublicKeyOptional": "SSH পাবলিক কী (ঐচ্ছিক)", - "publicKeyGenerated": "পাবলিক কী তৈরি করা হয়েছে", - "failedToGeneratePublicKey": "পাবলিক কী বের করতে ব্যর্থ হয়েছে", - "publicKeyCopied": "পাবলিক কী কপি করা হয়েছে", - "keyPairGenerated": "{{label}} কী পেয়ার তৈরি হয়েছে", - "failedToGenerateKeyPair": "কী পেয়ার তৈরি করতে ব্যর্থ হয়েছে", - "searchHostsPlaceholder": "হোস্ট, ঠিকানা, ট্যাগ অনুসন্ধান করুন…", - "searchCredentialsPlaceholder": "অনুসন্ধানের প্রমাণপত্র…", - "refreshBtn": "রিফ্রেশ", - "addTag": "ট্যাগ যোগ করুন...", - "deleteConfirmBtn": "মুছে ফেলুন", - "tunnelRequirementsText": "SSH সার্ভারের /etc/ssh/sshd_config ফাইলে GatewayPorts yes, AllowTcpForwarding yes, এবং PermitRootLogin yes সেট করা থাকতে হবে।", - "deleteHostConfirm": "\"{{name}} \" মুছে ফেলুন?", - "enableAtLeastOneProtocol": "প্রমাণীকরণ এবং সংযোগ সেটিংস কনফিগার করতে উপরের প্রোটোকলগুলোর মধ্যে অন্তত একটি সক্রিয় করুন।", - "keyPassphrase": "মূল পাসফ্রেজ", - "connectBtn": "সংযোগ করুন", - "disconnectBtn": "সংযোগ বিচ্ছিন্ন করুন", - "basicInformation": "মৌলিক তথ্য", - "authDetailsSection": "প্রমাণীকরণের বিবরণ", - "credTypeLabel": "প্রকার", - "hostsTab": "হোস্টরা", - "credentialsTab": "যোগ্যতা", - "selectMultiple": "একাধিক নির্বাচন করুন", - "selectHosts": "হোস্ট নির্বাচন করুন", - "connectionLabel": "সংযোগ", - "authenticationLabel": "প্রমাণীকরণ", - "generateKeyPairTitle": "কী পেয়ার তৈরি করুন", - "generateKeyPairDescription": "একটি নতুন কী পেয়ার তৈরি করুন, প্রাইভেট এবং পাবলিক কী উভয়ই স্বয়ংক্রিয়ভাবে পূরণ হয়ে যাবে।", - "generateFromPrivateKey": "প্রাইভেট কী থেকে তৈরি করুন", - "refreshBtn2": "রিফ্রেশ", - "exitSelectionTitle": "প্রস্থান নির্বাচন", - "exportAll": "সব রপ্তানি করুন", - "addHostBtn2": "হোস্ট যোগ করুন", - "addCredentialBtn2": "পরিচয়পত্র যোগ করুন", - "checkingHostStatuses": "হোস্টের অবস্থা যাচাই করা হচ্ছে...", - "pinnedSection": "পিন করা", - "hostsExported": "হোস্টগুলি সফলভাবে রপ্তানি করা হয়েছে", - "exportFailed": "হোস্ট রপ্তানি করতে ব্যর্থ হয়েছে", - "sampleDownloaded": "নমুনা ফাইল ডাউনলোড করা হয়েছে", - "failedToDeleteCredential2": "ক্রেডেনশিয়াল মুছে ফেলতে ব্যর্থ হয়েছে", - "noFolderOption": "(কোন ফোল্ডার নেই)", - "nSelected": "{{count}} নির্বাচিত", - "featuresMenu": "বৈশিষ্ট্য", - "moveMenu": "স্থানান্তর", - "cancelSelection": "বাতিল করুন", - "deployDialogDesc": "একটি হোস্টের অনুমোদিত কী-গুলিতে {{name}} স্থাপন করুন।", - "targetHostLabel": "টার্গেট হোস্ট", - "selectHostOption": "একজন হোস্ট নির্বাচন করুন...", - "keyDeployedSuccess": "কী সফলভাবে স্থাপন করা হয়েছে", - "failedToDeployKey2": "কী স্থাপন করতে ব্যর্থ হয়েছে", - "deletedCount": "মুছে ফেলা {{count}} হোস্ট", - "failedToDeleteCount": "{{count}} হোস্ট মুছে ফেলা ব্যর্থ হয়েছে", - "duplicatedHost": "নকল \"{{name}}\"", - "failedToDuplicateHost": "হোস্ট নকল করতে ব্যর্থ হয়েছে", - "updatedCount": "আপডেট করা {{count}} হোস্ট", - "friendlyNameLabel": "বন্ধুত্বপূর্ণ নাম", - "descriptionLabel": "বর্ণনা", - "loadingHost": "হোস্ট লোড হচ্ছে...", - "loadingHosts": "হোস্ট লোড হচ্ছে...", - "loadingCredentials": "পরিচয়পত্র লোড হচ্ছে...", - "noHostsYet": "এখনো কোনো হোস্ট নেই", - "noHostsMatchSearch": "আপনার অনুসন্ধানের সাথে কোনো হোস্ট মেলে না।", - "hostNotFound": "হোস্ট খুঁজে পাওয়া যায়নি", - "searchHosts": "হোস্ট অনুসন্ধান করুন...", - "sortHosts": "হোস্ট বাছাই করুন", - "sortDefault": "ডিফল্ট অর্ডার", - "sortNameAsc": "নাম (A → Z)", - "sortNameDesc": "নাম (Z → A)", - "sortIpAsc": "আইপি ঠিকানা (আরোহী)", - "sortIpDesc": "আইপি ঠিকানা (বিবরণ)", - "sortOnlineFirst": "অনলাইন প্রথম", - "sortOfflineFirst": "অফলাইন প্রথম", - "sortPinnedFirst": "প্রথমে পিন করা হয়েছে", - "filterHosts": "হোস্ট ফিল্টার করুন", - "filterClearAll": "ফিল্টার পরিষ্কার করুন", - "filterStatusGroup": "অবস্থা", - "filterOnline": "অনলাইন", - "filterOffline": "অফলাইন", - "filterPinned": "পিন করা", - "filterAuthGroup": "প্রমাণীকরণ প্রকার", - "filterAuthPassword": "পাসওয়ার্ড", - "filterAuthKey": "SSH কী", - "filterAuthCredential": "শংসাপত্র", - "filterAuthNone": "কোনোটিই না", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", + "ssh": "SSH", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", + "tabSsh": "SSH", + "tabTerminal": "Terminal", + "tabRdp": "RDP", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "প্রোটোকল", - "filterProtocolSsh": "এসএসএইচ", - "filterProtocolRdp": "আরডিপি", - "filterProtocolVnc": "ভিএনসি", - "filterProtocolTelnet": "টেলনেট", - "filterFeaturesGroup": "বৈশিষ্ট্য", - "filterFeatureTerminal": "টার্মিনাল", - "filterFeatureFileManager": "ফাইল ম্যানেজার", - "filterFeatureTunnel": "টানেল", - "filterFeatureDocker": "ডকার", - "filterTagsGroup": "ট্যাগ", - "shareHost": "হোস্ট শেয়ার করুন", - "shareHostTitle": "শেয়ার করুন: {{name}}", + "filterProtocolGroup": "Protocol", + "filterProtocolSsh": "SSH", + "filterProtocolRdp": "RDP", + "filterProtocolVnc": "VNC", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "শেয়ারিং সক্ষম করতে এই হোস্টকে অবশ্যই একটি ক্রেডেনশিয়াল ব্যবহার করতে হবে। প্রথমে হোস্টটি সম্পাদনা করুন এবং একটি ক্রেডেনশিয়াল নির্ধারণ করুন।" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "সংযোগ", - "authentication": "প্রমাণীকরণ", - "connectionSettings": "সংযোগ সেটিংস", - "displaySettings": "ডিসপ্লে সেটিংস", - "audioSettings": "অডিও সেটিংস", - "rdpPerformance": "আরডিপি পারফরম্যান্স", - "deviceRedirection": "ডিভাইস পুনঃনির্দেশনা", - "session": "অধিবেশন", - "gateway": "গেটওয়ে", - "remoteApp": "রিমোটঅ্যাপ", - "clipboard": "ক্লিপবোর্ড", - "sessionRecording": "সেশন রেকর্ডিং", - "wakeOnLan": "ওয়েক-অন-ল্যান", - "vncSettings": "VNC সেটিংস", - "terminalSettings": "টার্মিনাল সেটিংস", - "rdpPort": "আরডিপি পোর্ট", - "username": "ব্যবহারকারীর নাম", - "password": "পাসওয়ার্ড", - "domain": "ডোমেইন", - "securityMode": "নিরাপত্তা মোড", - "colorDepth": "রঙের গভীরতা", - "width": "প্রস্থ", - "height": "উচ্চতা", - "dpi": "ডিপিআই", - "resizeMethod": "আকার পরিবর্তন পদ্ধতি", - "clientName": "ক্লায়েন্টের নাম", - "initialProgram": "প্রাথমিক প্রোগ্রাম", - "serverLayout": "সার্ভার লেআউট", - "timezone": "সময় অঞ্চল", - "gatewayHostname": "গেটওয়ে হোস্টনাম", - "gatewayPort": "গেটওয়ে পোর্ট", - "gatewayUsername": "গেটওয়ে ব্যবহারকারীর নাম", - "gatewayPassword": "গেটওয়ে পাসওয়ার্ড", - "gatewayDomain": "গেটওয়ে ডোমেইন", - "remoteAppProgram": "রিমোটঅ্যাপ প্রোগ্রাম", - "workingDirectory": "কর্ম ডিরেক্টরি", - "arguments": "যুক্তি", - "normalizeLineEndings": "লাইনের শেষাংশ স্বাভাবিক করুন", - "recordingPath": "রেকর্ডিং পথ", - "recordingName": "রেকর্ডিং নাম", - "macAddress": "MAC ঠিকানা", - "broadcastAddress": "সম্প্রচার ঠিকানা", - "udpPort": "ইউডিপি পোর্ট", - "waitTimeS": "অপেক্ষার সময় (সেকেন্ড)", - "driveName": "ড্রাইভের নাম", - "drivePath": "ড্রাইভ পাথ", - "ignoreCertificate": "সার্টিফিকেট উপেক্ষা করুন", - "ignoreCertificateDesc": "স্ব-স্বাক্ষরিত শংসাপত্র সহ হোস্টগুলিতে সংযোগের অনুমতি দিন", - "forceLossless": "বল ক্ষয়হীন", - "forceLosslessDesc": "লসলেস ইমেজ এনকোডিং বাধ্যতামূলক করুন (উচ্চতর গুণমান, বেশি ব্যান্ডউইথ)", - "disableAudio": "অডিও নিষ্ক্রিয় করুন", - "disableAudioDesc": "রিমোট সেশন থেকে সমস্ত অডিও মিউট করুন", - "enableAudioInput": "অডিও ইনপুট (মাইক্রোফোন) সক্রিয় করুন", - "enableAudioInputDesc": "স্থানীয় মাইক্রোফোনটি রিমোট সেশনে ফরোয়ার্ড করুন", - "wallpaper": "ওয়ালপেপার", - "wallpaperDesc": "ডেস্কটপ ওয়ালপেপার দেখান (এটি বন্ধ করলে পারফরম্যান্স উন্নত হয়)", - "theming": "থিমিং", - "themingDesc": "ভিজ্যুয়াল থিম এবং স্টাইল সক্রিয় করুন", - "fontSmoothing": "ফন্ট মসৃণকরণ", - "fontSmoothingDesc": "ক্লিয়ারটাইপ ফন্ট রেন্ডারিং সক্ষম করুন", - "fullWindowDrag": "সম্পূর্ণ উইন্ডো ড্র্যাগ", - "fullWindowDragDesc": "ড্র্যাগ করার সময় উইন্ডোর বিষয়বস্তু দেখান", - "desktopComposition": "ডেস্কটপ কম্পোজিশন", - "desktopCompositionDesc": "এরো গ্লাস এফেক্টস সক্রিয় করুন", - "menuAnimations": "মেনু অ্যানিমেশন", - "menuAnimationsDesc": "মেনু ফেড এবং স্লাইড অ্যানিমেশন সক্রিয় করুন", - "disableBitmapCaching": "বিটম্যাপ ক্যাশিং নিষ্ক্রিয় করুন", - "disableBitmapCachingDesc": "বিটম্যাপ ক্যাশে বন্ধ করুন (এটি গ্লিচ কমাতে সাহায্য করতে পারে)।", - "disableOffscreenCaching": "অফস্ক্রিন ক্যাশিং নিষ্ক্রিয় করুন", - "disableOffscreenCachingDesc": "অফস্ক্রিন ক্যাশে বন্ধ করুন", - "disableGlyphCaching": "গ্লিফ ক্যাশিং নিষ্ক্রিয় করুন", - "disableGlyphCachingDesc": "গ্লিফ ক্যাশে বন্ধ করুন", - "enableGfx": "GFX সক্ষম করুন", - "enableGfxDesc": "RemoteFX গ্রাফিক্স পাইপলাইন ব্যবহার করুন", - "enablePrinting": "প্রিন্টিং সক্ষম করুন", - "enablePrintingDesc": "স্থানীয় প্রিন্টারগুলিকে রিমোট সেশনে পুনঃনির্দেশিত করুন", - "enableDriveRedirection": "ড্রাইভ পুনঃনির্দেশনা সক্ষম করুন", - "enableDriveRedirectionDesc": "রিমোট সেশনে একটি স্থানীয় ফোল্ডারকে ড্রাইভ হিসেবে ম্যাপ করুন।", - "createDrivePath": "ড্রাইভ পাথ তৈরি করুন", - "createDrivePathDesc": "ফোল্ডারটি বিদ্যমান না থাকলে স্বয়ংক্রিয়ভাবে তৈরি করুন।", - "disableDownload": "ডাউনলোড নিষ্ক্রিয় করুন", - "disableDownloadDesc": "রিমোট সেশন থেকে ফাইল ডাউনলোড করা প্রতিরোধ করুন", - "disableUpload": "আপলোড নিষ্ক্রিয় করুন", - "disableUploadDesc": "রিমোট সেশনে ফাইল আপলোড করা প্রতিরোধ করুন", - "enableTouch": "টাচ সক্ষম করুন", - "enableTouchDesc": "টাচ ইনপুট ফরওয়ার্ডিং সক্ষম করুন", - "consoleSession": "কনসোল সেশন", - "consoleSessionDesc": "নতুন সেশন শুরু করার পরিবর্তে কনসোলে (সেশন ০) সংযোগ করুন।", - "sendWolPacket": "WOL প্যাকেট পাঠান", - "sendWolPacketDesc": "সংযোগ করার আগে এই হোস্টকে জাগিয়ে তুলতে একটি ম্যাজিক প্যাকেট পাঠান।", - "disableCopy": "কপি নিষ্ক্রিয় করুন", - "disableCopyDesc": "রিমোট সেশন থেকে টেক্সট কপি করা প্রতিরোধ করুন", - "disablePaste": "পেস্ট নিষ্ক্রিয় করুন", - "disablePasteDesc": "রিমোট সেশনে টেক্সট পেস্ট করা প্রতিরোধ করুন", - "createPathIfMissing": "অনুপস্থিত থাকলে পাথ তৈরি করুন", - "createPathIfMissingDesc": "স্বয়ংক্রিয়ভাবে রেকর্ডিং ডিরেক্টরি তৈরি করুন", - "excludeOutput": "আউটপুট বাদ দিন", - "excludeOutputDesc": "স্ক্রিন আউটপুট রেকর্ড করবেন না (শুধুমাত্র মেটাডেটা)", - "excludeMouse": "মাউস বাদ দিন", - "excludeMouseDesc": "মাউসের নড়াচড়া রেকর্ড করবেন না।", - "includeKeystrokes": "কীস্ট্রোক অন্তর্ভুক্ত করুন", - "includeKeystrokesDesc": "স্ক্রিন আউটপুটের পাশাপাশি সরাসরি কীস্ট্রোক রেকর্ড করুন।", - "vncPort": "ভিএনসি পোর্ট", - "vncPassword": "VNC পাসওয়ার্ড", - "vncUsernameOptional": "ব্যবহারকারীর নাম (ঐচ্ছিক)", - "vncLeaveBlank": "প্রয়োজন না হলে খালি রাখুন", - "cursorMode": "কার্সার মোড", - "swapRedBlue": "লাল/নীল অদলবদল করুন", - "swapRedBlueDesc": "লাল এবং নীল রঙের চ্যানেলগুলি অদলবদল করুন (কিছু রঙের সমস্যা সমাধান করে)", - "readOnly": "শুধুমাত্র পঠনযোগ্য", - "readOnlyDesc": "কোনো ইনপুট না পাঠিয়ে রিমোট স্ক্রিন দেখুন।", - "telnetPort": "টেলনেট পোর্ট", - "terminalType": "টার্মিনাল টাইপ", - "fontName": "ফন্টের নাম", - "fontSize": "ফন্ট সাইজ", - "colorScheme": "রঙের পরিকল্পনা", - "backspaceKey": "ব্যাকস্পেস কী", - "saveHostFirst": "প্রথমে হোস্টটি সংরক্ষণ করুন।", - "sharingOptionsAfterSave": "হোস্ট সংরক্ষণ করার পর শেয়ার করার বিকল্পগুলো পাওয়া যাবে।", - "sharingLoadError": "শেয়ারিং ডেটা লোড করা সম্ভব হয়নি। আপনার সংযোগ পরীক্ষা করে আবার চেষ্টা করুন।", - "shareHostSection": "হোস্ট শেয়ার করুন", - "shareWithUser": "ব্যবহারকারীর সাথে শেয়ার করুন", - "shareWithRole": "ভূমিকার সাথে শেয়ার করুন", - "selectUser": "ব্যবহারকারী নির্বাচন করুন", - "selectRole": "ভূমিকা নির্বাচন করুন", - "selectUserOption": "একজন ব্যবহারকারী নির্বাচন করুন...", - "selectRoleOption": "একটি ভূমিকা নির্বাচন করুন...", - "permissionLevel": "অনুমতি স্তর", - "expiresInHours": "(ঘন্টায়) মেয়াদ শেষ হবে", - "noExpiryPlaceholder": "মেয়াদ শেষ না হওয়ার জন্য খালি রাখুন।", - "shareBtn": "শেয়ার", - "currentAccess": "বর্তমান অ্যাক্সেস", - "typeHeader": "প্রকার", - "targetHeader": "লক্ষ্য", - "permissionHeader": "অনুমতি", - "grantedByHeader": "মঞ্জুর করেছেন", - "expiresHeader": "মেয়াদ শেষ", - "noAccessEntries": "এখনো কোনো প্রবেশাধিকার দেওয়া হয়নি।", - "expiredLabel": "মেয়াদোত্তীর্ণ", - "neverLabel": "কখনো না", - "revokeBtn": "বাতিল করুন", - "cancelBtn": "বাতিল করুন", - "savingBtn": "সঞ্চয়...", - "updateHostBtn": "হোস্ট আপডেট করুন", - "addHostBtn": "হোস্ট যোগ করুন" + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "হোস্ট, কমান্ড বা সেটিংস অনুসন্ধান করুন...", - "quickActions": "দ্রুত পদক্ষেপ", - "hostManager": "হোস্ট ম্যানেজার", - "hostManagerDesc": "হোস্ট পরিচালনা করুন, যোগ করুন বা সম্পাদনা করুন", - "addNewHost": "নতুন হোস্ট যোগ করুন", - "addNewHostDesc": "একজন নতুন হোস্ট নিবন্ধন করুন", - "adminSettings": "অ্যাডমিন সেটিংস", - "adminSettingsDesc": "সিস্টেম পছন্দ এবং ব্যবহারকারীদের কনফিগার করুন", - "userProfile": "ব্যবহারকারীর প্রোফাইল", - "userProfileDesc": "আপনার অ্যাকাউন্ট ও পছন্দসমূহ পরিচালনা করুন", - "addCredential": "পরিচয়পত্র যোগ করুন", - "addCredentialDesc": "SSH কী বা পাসওয়ার্ড সংরক্ষণ করুন", - "recentActivity": "সাম্প্রতিক কার্যকলাপ", - "serversAndHosts": "সার্ভার এবং হোস্ট", - "noHostsFound": "\"{{search}} \" এর সাথে মেলে এমন কোনো হোস্ট খুঁজে পাওয়া যায়নি।", - "links": "লিঙ্ক", - "navigate": "নেভিগেট করুন", - "select": "নির্বাচন করুন", - "toggleWith": "টগল করুন" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "প্যান {{index}} - খালি", - "noTabAssigned": "কোন ট্যাব বরাদ্দ করা হয়নি", - "focusedPane": "সক্রিয় প্যানে" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "কোন সংযোগ নেই", - "noConnectionsDesc": "এখানে সংযোগগুলি দেখতে একটি টার্মিনাল, ফাইল ম্যানেজার বা রিমোট ডেস্কটপ খুলুন।", - "connectedFor": "{{duration}} এর জন্য সংযুক্ত", - "connected": "সংযুক্ত", - "disconnected": "বিচ্ছিন্ন", - "closeTab": "ট্যাব বন্ধ করুন", - "closeConnection": "ঘনিষ্ঠ সংযোগ", - "forgetTab": "ভুলে যান", - "removeBackground": "অপসারণ করুন", - "reconnect": "পুনরায় সংযোগ করুন", - "reopenTab": "পুনরায় খুলুন", - "sectionOpen": "খোলা", - "sectionBackground": "পটভূমি", - "backgroundDesc": "সংযোগ বিচ্ছিন্ন হওয়ার পরেও সেশন ৩০ মিনিট পর্যন্ত স্থায়ী থাকে এবং পুনরায় সংযোগ করা যায়।", - "persisted": "পটভূমিতে অব্যাহত ছিল", - "expiresIn": "{{duration}}-এ মেয়াদ শেষ হবে", - "search": "সংযোগগুলি অনুসন্ধান করুন...", - "noSearchResults": "আপনার অনুসন্ধানের সাথে কোনো সংযোগ মেলেনি।" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "{{type}} সেশনে সংযোগ স্থাপন করা হচ্ছে...", - "connectionError": "সংযোগ ত্রুটি", - "connectionFailed": "সংযোগ ব্যর্থ হয়েছে", - "failedToConnect": "সংযোগ টোকেন পেতে ব্যর্থ হয়েছে", - "hostNotFound": "হোস্ট খুঁজে পাওয়া যায়নি", - "noHostSelected": "কোন হোস্ট নির্বাচন করা হয়নি", - "reconnect": "পুনরায় সংযোগ করুন", - "retry": "পুনরায় চেষ্টা করুন", - "guacdUnavailable": "রিমোট ডেস্কটপ সার্ভিস (guacd) উপলব্ধ নেই। অনুগ্রহ করে নিশ্চিত করুন যে guacd চালু ও অ্যাক্সেসযোগ্য আছে এবং অ্যাডমিন সেটিংসে সঠিকভাবে কনফিগার করা আছে।", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "উইন+এল (লক স্ক্রিন)", - "winKey": "উইন্ডোজ কী", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "বিকল্প", - "shift": "শিফট", - "win": "জয়", - "stickyActive": "{{key}} (আটকানো - খুলতে ক্লিক করুন)", - "stickyInactive": "{{key}} (আটকাতে ক্লিক করুন)", - "esc": "পালানো", - "tab": "ট্যাব", - "home": "হোম", - "end": "শেষ", - "pageUp": "পৃষ্ঠা উপরে", - "pageDown": "পৃষ্ঠা নিচে", - "arrowUp": "তীর ঊর্ধ্বমুখী", - "arrowDown": "অ্যারো ডাউন", - "arrowLeft": "বাম তীর", - "arrowRight": "ডান তীরচিহ্ন", - "fnToggle": "ফাংশন কী", - "reconnect": "পুনরায় সংযোগ সেশন", - "collapse": "টুলবার সংকুচিত করুন", - "expand": "টুলবার প্রসারিত করুন", - "dragHandle": "অবস্থান পরিবর্তন করতে টেনে আনুন" + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "হোস্টের সাথে সংযোগ করুন", - "clear": "পরিষ্কার", - "paste": "পেস্ট", - "reconnect": "পুনরায় সংযোগ করুন", - "connectionLost": "সংযোগ বিচ্ছিন্ন", - "connected": "সংযুক্ত", - "clipboardWriteFailed": "ক্লিপবোর্ডে কপি করা সম্ভব হয়নি। নিশ্চিত করুন যে পৃষ্ঠাটি HTTPS বা লোকালহোস্টের মাধ্যমে পরিবেশিত হচ্ছে।", - "clipboardReadFailed": "ক্লিপবোর্ড থেকে পড়তে ব্যর্থ হয়েছে। নিশ্চিত করুন যে ক্লিপবোর্ডের অনুমতি দেওয়া আছে।", - "clipboardHttpWarning": "পেস্ট করার জন্য HTTPS প্রয়োজন। Ctrl+Shift+V ব্যবহার করুন অথবা HTTPS-এর মাধ্যমে Termix পরিবেশন করুন।", - "unknownError": "অজানা ত্রুটি ঘটেছে", - "websocketError": "ওয়েবসকেট সংযোগ ত্রুটি", - "connecting": "সংযোগ স্থাপন...", - "noHostSelected": "কোন হোস্ট নির্বাচন করা হয়নি", - "reconnecting": "পুনরায় সংযোগ স্থাপন করা হচ্ছে... ({{attempt}}/{{max}})", - "reconnected": "সফলভাবে পুনরায় সংযুক্ত হয়েছে", - "tmuxSessionCreated": "tmux সেশন তৈরি হয়েছে: {{name}}", - "tmuxSessionAttached": "tmux সেশন সংযুক্ত হয়েছে: {{name}}", - "tmuxUnavailable": "রিমোট হোস্টে tmux ইনস্টল করা নেই, তাই স্ট্যান্ডার্ড শেল ব্যবহার করা হচ্ছে।", - "tmuxSessionPickerTitle": "tmux সেশন", - "tmuxSessionPickerDesc": "এই হোস্টে বিদ্যমান tmux সেশন পাওয়া গেছে। পুনরায় সংযুক্ত হতে বা একটি নতুন সেশন তৈরি করতে যেকোনো একটি নির্বাচন করুন।", - "tmuxWindows": "উইন্ডোজ", - "tmuxWindowCount": "{{count}} জানালা", - "tmuxAttached": "সংযুক্ত ক্লায়েন্ট", - "tmuxAttachedCount": "{{count}} সংযুক্ত", - "tmuxLastActivity": "শেষ কার্যকলাপ", - "tmuxTimeJustNow": "এইমাত্র", - "tmuxTimeMinutes": "{{count}}মিনিট আগে", - "tmuxTimeHours": "{{count}}ঘণ্টা আগে", - "tmuxTimeDays": "{{count}}দিন আগে", - "tmuxCreateNew": "নতুন সেশন শুরু করুন", - "tmuxCopyHint": "নির্বাচনটি ঠিক করুন এবং ক্লিপবোর্ডে কপি করতে এন্টার চাপুন।", - "tmuxDetach": "tmux সেশন থেকে বিচ্ছিন্ন করুন", - "tmuxDetached": "tmux সেশন থেকে বিচ্ছিন্ন", - "maxReconnectAttemptsReached": "পুনঃসংযোগের সর্বোচ্চ প্রচেষ্টা শেষ হয়েছে", - "closeTab": "বন্ধ করুন", - "connectionTimeout": "সংযোগের সময়সীমা শেষ", - "terminalTitle": "টার্মিনাল - {{host}}", - "terminalWithPath": "টার্মিনাল - {{host}}:{{path}}", - "runTitle": "দৌড়ানো {{command}} - {{host}}", - "totpRequired": "দ্বি-স্তরীয় প্রমাণীকরণ আবশ্যক", - "totpCodeLabel": "যাচাইকরণ কোড", - "totpVerify": "যাচাই করুন", - "warpgateAuthRequired": "ওয়ার্পগেট প্রমাণীকরণ আবশ্যক", - "warpgateSecurityKey": "নিরাপত্তা চাবি", - "warpgateAuthUrl": "প্রমাণীকরণ ইউআরএল", - "warpgateOpenBrowser": "ব্রাউজারে খুলুন", - "warpgateContinue": "আমি প্রমাণীকরণ সম্পন্ন করেছি।", - "opksshAuthRequired": "OPKSSH প্রমাণীকরণ আবশ্যক", - "opksshAuthDescription": "চালিয়ে যাওয়ার জন্য আপনার ব্রাউজারে প্রমাণীকরণ সম্পন্ন করুন। এই সেশনটি ২৪ ঘণ্টার জন্য বৈধ থাকবে।", - "opksshOpenBrowser": "প্রমাণীকরণের জন্য ব্রাউজার খুলুন", - "opksshWaitingForAuth": "ব্রাউজারে প্রমাণীকরণের জন্য অপেক্ষা করা হচ্ছে...", - "opksshAuthenticating": "প্রমাণীকরণ প্রক্রিয়া চলছে...", - "opksshTimeout": "প্রমাণীকরণের সময়সীমা শেষ হয়ে গেছে। অনুগ্রহ করে আবার চেষ্টা করুন।", - "opksshAuthFailed": "প্রমাণীকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আপনার পরিচয়পত্র যাচাই করে আবার চেষ্টা করুন।", - "opksshSignInWith": "{{provider}} দিয়ে সাইন ইন করুন", - "sudoPasswordPopupTitle": "পাসওয়ার্ড দিন?", - "websocketAbnormalClose": "সংযোগটি অপ্রত্যাশিতভাবে বন্ধ হয়ে গেছে। এটি রিভার্স প্রক্সি বা SSL কনফিগারেশন সংক্রান্ত সমস্যার কারণে হতে পারে। অনুগ্রহ করে সার্ভার লগ পরীক্ষা করুন।", - "connectionLogTitle": "সংযোগ লগ", - "connectionLogCopy": "লগগুলি ক্লিপবোর্ডে কপি করুন", - "connectionLogEmpty": "এখনো কোনো সংযোগ লগ নেই", - "connectionLogWaiting": "সংযোগ লগগুলির জন্য অপেক্ষা করা হচ্ছে...", - "connectionLogCopied": "সংযোগ লগগুলি ক্লিপবোর্ডে কপি করা হয়েছে", - "connectionLogCopyFailed": "লগগুলি ক্লিপবোর্ডে কপি করতে ব্যর্থ হয়েছে", - "connectionRejected": "সার্ভার কর্তৃক সংযোগ প্রত্যাখ্যাত হয়েছে। অনুগ্রহ করে আপনার প্রমাণীকরণ এবং নেটওয়ার্ক কনফিগারেশন যাচাই করুন।", - "hostKeyRejected": "SSH হোস্ট কী যাচাইকরণ প্রত্যাখ্যাত হয়েছে। সংযোগ বাতিল করা হয়েছে।", - "sessionTakenOver": "সেশনটি অন্য একটি ট্যাবে খোলা হয়েছিল। পুনরায় সংযোগ করা হচ্ছে..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "কোন হোস্ট নির্বাচন করা হয়নি", - "initializingEditor": "এডিটর চালু করা হচ্ছে...", - "file": "ফাইল", - "folder": "ফোল্ডার", - "uploadFile": "ফাইল আপলোড করুন", - "downloadFile": "ডাউনলোড করুন", - "extractArchive": "আর্কাইভ নিষ্কাশন করুন", - "extractingArchive": "{{name}} নিষ্কাশন করা হচ্ছে ...", - "archiveExtractedSuccessfully": "{{name}} সফলভাবে নিষ্কাশন করা হয়েছে", - "extractFailed": "নিষ্কাশন ব্যর্থ হয়েছে", - "compressFile": "ফাইল সংকুচিত করুন", - "compressFiles": "ফাইলগুলি সংকুচিত করুন", - "compressFilesDesc": "{{count}} টি আইটেমকে একটি আর্কাইভে সংকুচিত করুন", - "archiveName": "আর্কাইভের নাম", - "enterArchiveName": "আর্কাইভের নাম লিখুন...", - "compressionFormat": "কম্প্রেশন ফরম্যাট", - "selectedFiles": "নির্বাচিত ফাইল", - "andMoreFiles": "এবং {{count}} আরও...", - "compress": "সংকুচিত করুন", - "compressingFiles": "{{count}} টি আইটেমকে {{name}} টি আইটেমে সংকুচিত করা হচ্ছে...", - "filesCompressedSuccessfully": "{{name}} সফলভাবে তৈরি হয়েছে", - "compressFailed": "সংকোচন ব্যর্থ হয়েছে", - "edit": "সম্পাদনা", - "preview": "প্রিভিউ", - "previous": "পূর্ববর্তী", - "next": "পরবর্তী", - "pageXOfY": "{{current}} এর {{total}} পৃষ্ঠা", - "zoomOut": "জুম আউট", - "zoomIn": "জুম ইন", - "newFile": "নতুন ফাইল", - "newFolder": "নতুন ফোল্ডার", - "rename": "নাম পরিবর্তন", - "uploading": "আপলোড হচ্ছে...", - "uploadingFile": "{{name}} আপলোড হচ্ছে ...", - "fileName": "ফাইলের নাম", - "folderName": "ফোল্ডারের নাম", - "fileUploadedSuccessfully": "ফাইল \"{{name}}\" সফলভাবে আপলোড হয়েছে", - "failedToUploadFile": "ফাইল আপলোড করতে ব্যর্থ হয়েছে", - "fileDownloadedSuccessfully": "ফাইল \"{{name}}\" সফলভাবে ডাউনলোড হয়েছে", - "failedToDownloadFile": "ফাইল ডাউনলোড করতে ব্যর্থ হয়েছে", - "fileCreatedSuccessfully": "ফাইল \"{{name}}\" সফলভাবে তৈরি হয়েছে", - "folderCreatedSuccessfully": "\"{{name}}\" ফোল্ডারটি সফলভাবে তৈরি করা হয়েছে", - "failedToCreateItem": "আইটেম তৈরি করতে ব্যর্থ হয়েছে", - "operationFailed": "{{operation}} অপারেশনটি {{name}}এর জন্য ব্যর্থ হয়েছে: {{error}}", - "failedToResolveSymlink": "সিমলিঙ্ক সমাধান করতে ব্যর্থ হয়েছে", - "itemsDeletedSuccessfully": "{{count}} আইটেম সফলভাবে মুছে ফেলা হয়েছে", - "failedToDeleteItems": "আইটেমগুলি মুছে ফেলতে ব্যর্থ হয়েছে", - "sudoPasswordRequired": "প্রশাসক পাসওয়ার্ড আবশ্যক", - "enterSudoPassword": "এই অপারেশনটি চালিয়ে যেতে sudo পাসওয়ার্ড দিন।", - "sudoPassword": "সুডো পাসওয়ার্ড", - "sudoOperationFailed": "Sudo অপারেশন ব্যর্থ হয়েছে", - "sudoAuthFailed": "Sudo প্রমাণীকরণ ব্যর্থ হয়েছে", - "dragFilesToUpload": "আপলোড করার জন্য ফাইলগুলো এখানে রাখুন।", - "emptyFolder": "এই ফোল্ডারটি খালি", - "searchFiles": "ফাইলগুলি অনুসন্ধান করুন...", - "upload": "আপলোড", - "selectHostToStart": "ফাইল ব্যবস্থাপনা শুরু করতে একটি হোস্ট নির্বাচন করুন", - "sshRequiredForFileManager": "ফাইল ম্যানেজারের জন্য SSH প্রয়োজন। এই হোস্টে SSH সক্রিয় করা নেই।", - "failedToConnect": "SSH-এর সাথে সংযোগ করতে ব্যর্থ হয়েছে", - "failedToLoadDirectory": "ডিরেক্টরি লোড করতে ব্যর্থ হয়েছে", - "noSSHConnection": "কোন SSH সংযোগ উপলব্ধ নেই", - "copy": "অনুলিপি", - "cut": "কাটা", - "paste": "পেস্ট", - "copyPath": "কপি পাথ", - "copyPaths": "কপি পাথ", - "delete": "মুছে ফেলুন", - "properties": "বৈশিষ্ট্য", - "refresh": "রিফ্রেশ", - "downloadFiles": "ব্রাউজারে {{count}} ফাইলগুলো ডাউনলোড করুন", - "copyFiles": "{{count}} আইটেম কপি করুন", - "cutFiles": "{{count}} টি আইটেম কাটুন", - "deleteFiles": "{{count}} টি আইটেম মুছুন", - "filesCopiedToClipboard": "{{count}} আইটেম ক্লিপবোর্ডে কপি করা হয়েছে", - "filesCutToClipboard": "{{count}} আইটেমগুলো ক্লিপবোর্ডে কাটা হয়েছে", - "pathCopiedToClipboard": "পাথটি ক্লিপবোর্ডে কপি করা হয়েছে", - "pathsCopiedToClipboard": "{{count}} পাথগুলো ক্লিপবোর্ডে কপি করা হয়েছে", - "failedToCopyPath": "ক্লিপবোর্ডে পাথ কপি করতে ব্যর্থ হয়েছে", - "movedItems": "{{count}} টি আইটেম সরানো হয়েছে", - "failedToDeleteItem": "আইটেমটি মুছে ফেলতে ব্যর্থ হয়েছে", - "itemRenamedSuccessfully": "{{type}} সফলভাবে নাম পরিবর্তন করা হয়েছে", - "failedToRenameItem": "আইটেমের নাম পরিবর্তন করতে ব্যর্থ হয়েছে", - "download": "ডাউনলোড করুন", - "permissions": "অনুমতি", - "size": "আকার", - "modified": "পরিবর্তিত", - "path": "পথ", - "confirmDelete": "আপনি কি {{name}} মুছে ফেলতে নিশ্চিত?", - "permissionDenied": "অনুমতি প্রত্যাখ্যান করা হয়েছে", - "serverError": "সার্ভার ত্রুটি", - "fileSavedSuccessfully": "ফাইল সফলভাবে সংরক্ষিত হয়েছে।", - "failedToSaveFile": "ফাইল সংরক্ষণ করতে ব্যর্থ হয়েছে", - "confirmDeleteSingleItem": "আপনি কি \"{{name}} \" স্থায়ীভাবে মুছে ফেলতে চান?", - "confirmDeleteMultipleItems": "আপনি কি {{count}} টি আইটেম স্থায়ীভাবে মুছে ফেলতে চান?", - "confirmDeleteMultipleItemsWithFolders": "আপনি কি {{count}} টি আইটেম স্থায়ীভাবে মুছে ফেলতে নিশ্চিত? এর মধ্যে ফোল্ডার এবং তার ভেতরের সবকিছু অন্তর্ভুক্ত।", - "confirmDeleteFolder": "আপনি কি \"{{name}}\" ফোল্ডারটি এবং এর ভেতরের সবকিছু স্থায়ীভাবে মুছে ফেলতে চান?", - "permanentDeleteWarning": "এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না। আইটেম(গুলি) সার্ভার থেকে স্থায়ীভাবে মুছে ফেলা হবে।", - "recent": "সাম্প্রতিক", - "pinned": "পিন করা", - "folderShortcuts": "ফোল্ডার শর্টকাট", - "failedToReconnectSSH": "SSH সেশন পুনরায় সংযোগ করতে ব্যর্থ হয়েছে", - "openTerminalHere": "এখানে টার্মিনাল খুলুন", - "run": "দৌড়", - "openTerminalInFolder": "এই ফোল্ডারে টার্মিনাল খুলুন", - "openTerminalInFileLocation": "ফাইল লোকেশনে টার্মিনাল খুলুন", - "runningFile": "দৌড়ানো - {{file}}", - "onlyRunExecutableFiles": "শুধুমাত্র এক্সিকিউটেবল ফাইল চালানো যাবে", - "directories": "ডিরেক্টরি", - "removedFromRecentFiles": "সাম্প্রতিক ফাইলগুলি থেকে \"{{name}}\" সরানো হয়েছে", - "removeFailed": "ব্যর্থ অপসারণ", - "unpinnedSuccessfully": "\"{{name}}\" সফলভাবে আনপিন করা হয়েছে", - "unpinFailed": "আনপিন ব্যর্থ হয়েছে", - "removedShortcut": "\"{{name}} \" শর্টকাটটি সরানো হয়েছে", - "removeShortcutFailed": "শর্টকাট অপসারণ ব্যর্থ হয়েছে", - "clearedAllRecentFiles": "সাম্প্রতিক সমস্ত ফাইল মুছে ফেলা হয়েছে", - "clearFailed": "পরিষ্কার করতে ব্যর্থ হয়েছে", - "removeFromRecentFiles": "সাম্প্রতিক ফাইলগুলি থেকে সরান", - "clearAllRecentFiles": "সাম্প্রতিক সমস্ত ফাইল মুছে ফেলুন", - "unpinFile": "ফাইলটি আনপিন করুন", - "removeShortcut": "শর্টকাট সরান", - "pinFile": "পিন ফাইল", - "addToShortcuts": "শর্টকাটে যোগ করুন", - "pasteFailed": "পেস্ট ব্যর্থ হয়েছে", - "noUndoableActions": "কোনো পূর্বাবস্থায় ফেরানো যায় না এমন কাজ নেই।", - "undoCopySuccess": "কপি অপারেশন বাতিল করা হয়েছে: {{count}} টি কপি করা ফাইল মুছে ফেলা হয়েছে", - "undoCopyFailedDelete": "পূর্বাবস্থায় ফেরানো ব্যর্থ হয়েছে: কপি করা কোনো ফাইল মুছে ফেলা সম্ভব হয়নি", - "undoCopyFailedNoInfo": "পূর্বাবস্থায় ফেরানো ব্যর্থ হয়েছে: কপি করা ফাইলের তথ্য খুঁজে পাওয়া যায়নি", - "undoMoveSuccess": "স্থানান্তর প্রক্রিয়া বাতিল করা হয়েছে: {{count}} টি ফাইল মূল অবস্থানে ফিরিয়ে আনা হয়েছে", - "undoMoveFailedMove": "পূর্বাবস্থায় ফেরানো ব্যর্থ হয়েছে: কোনো ফাইল আগের জায়গায় সরানো সম্ভব হয়নি", - "undoMoveFailedNoInfo": "পূর্বাবস্থায় ফেরানো ব্যর্থ হয়েছে: স্থানান্তরিত ফাইলের তথ্য খুঁজে পাওয়া যায়নি", - "undoDeleteNotSupported": "মুছে ফেলার প্রক্রিয়াটি পূর্বাবস্থায় ফেরানো যাবে না: ফাইলগুলো সার্ভার থেকে স্থায়ীভাবে মুছে ফেলা হয়েছে।", - "undoTypeNotSupported": "অসমর্থিত পূর্বাবস্থায় ফেরানোর অপারেশনের ধরণ", - "undoOperationFailed": "পূর্বাবস্থায় ফেরানো ব্যর্থ হয়েছে", - "unknownError": "অজানা ত্রুটি", - "confirm": "নিশ্চিত করুন", - "find": "খুঁজুন...", - "replace": "প্রতিস্থাপন করুন", - "downloadInstead": "পরিবর্তে ডাউনলোড করুন", - "keyboardShortcuts": "কীবোর্ড শর্টকাট", - "searchAndReplace": "অনুসন্ধান ও প্রতিস্থাপন", - "editing": "সম্পাদনা", - "search": "অনুসন্ধান", - "findNext": "পরবর্তী খুঁজুন", - "findPrevious": "পূর্ববর্তী খুঁজুন", - "save": "সংরক্ষণ করুন", - "selectAll": "সব নির্বাচন করুন", - "undo": "পূর্বাবস্থায় ফেরান", - "redo": "পুনরায় করুন", - "moveLineUp": "লাইন আপ সরান", - "moveLineDown": "লাইন নিচে সরান", - "toggleComment": "মন্তব্য টগল করুন", - "autoComplete": "স্বয়ংক্রিয়ভাবে সম্পূর্ণ", - "imageLoadError": "ছবি লোড করতে ব্যর্থ হয়েছে", - "startTyping": "টাইপ করা শুরু করুন...", - "unknownSize": "অজানা আকার", - "fileIsEmpty": "ফাইলটি খালি", - "largeFileWarning": "বড় ফাইলের সতর্কতা", - "largeFileWarningDesc": "এই ফাইলটির আকার {{size}} , যা টেক্সট হিসেবে খুললে পারফরম্যান্সের সমস্যা সৃষ্টি করতে পারে।", - "fileNotFoundAndRemoved": "\"{{name}}\" ফাইলটি খুঁজে পাওয়া যায়নি এবং সাম্প্রতিক/পিন করা ফাইলগুলো থেকে সরিয়ে ফেলা হয়েছে।", - "failedToLoadFile": "ফাইল লোড করতে ব্যর্থ: {{error}}", - "serverErrorOccurred": "সার্ভার ত্রুটি ঘটেছে। অনুগ্রহ করে পরে আবার চেষ্টা করুন।", - "autoSaveFailed": "স্বয়ংক্রিয় সংরক্ষণ ব্যর্থ হয়েছে", - "fileAutoSaved": "ফাইল স্বয়ংক্রিয়ভাবে সংরক্ষিত হয়েছে", - "moveFileFailed": "{{name}} সরাতে ব্যর্থ", - "moveOperationFailed": "স্থানান্তর প্রক্রিয়া ব্যর্থ হয়েছে", - "canOnlyCompareFiles": "শুধুমাত্র দুটি ফাইল তুলনা করা যাবে।", - "comparingFiles": "ফাইলগুলোর তুলনা: {{file1}} এবং {{file2}}", - "dragFailed": "ড্র্যাগ অপারেশন ব্যর্থ হয়েছে", - "filePinnedSuccessfully": "ফাইল \"{{name}}\" সফলভাবে পিন করা হয়েছে", - "pinFileFailed": "ফাইল পিন করতে ব্যর্থ হয়েছে", - "fileUnpinnedSuccessfully": "ফাইল \"{{name}}\" সফলভাবে আনপিন করা হয়েছে", - "unpinFileFailed": "ফাইল আনপিন করতে ব্যর্থ হয়েছে", - "shortcutAddedSuccessfully": "ফোল্ডার শর্টকাট \"{{name}}\" সফলভাবে যোগ করা হয়েছে", - "addShortcutFailed": "শর্টকাট যোগ করতে ব্যর্থ হয়েছে", - "operationCompletedSuccessfully": "{{operation}} {{count}} আইটেম সফলভাবে", - "operationCompleted": "{{operation}} {{count}} আইটেম", - "downloadFileSuccess": "ফাইল {{name}} সফলভাবে ডাউনলোড হয়েছে", - "downloadFileFailed": "ডাউনলোড ব্যর্থ হয়েছে", - "moveTo": "{{name}} এ যান", - "diffCompareWith": "{{name}} এর সাথে পার্থক্য তুলনা করুন", - "dragOutsideToDownload": "ডাউনলোড করতে উইন্ডোর বাইরে টেনে আনুন ({{count}} ফাইলগুলো)", - "newFolderDefault": "নতুন ফোল্ডার", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} টি আইটেম সফলভাবে {{target}} টি আইটেমে সরানো হয়েছে", - "move": "স্থানান্তর", - "searchInFile": "ফাইলে অনুসন্ধান করুন (Ctrl+F)", - "showKeyboardShortcuts": "কিবোর্ড শর্টকাট দেখান", - "startWritingMarkdown": "আপনার মার্কডাউন কন্টেন্ট লেখা শুরু করুন...", - "loadingFileComparison": "ফাইল তুলনা লোড হচ্ছে...", - "reload": "পুনরায় লোড করুন", - "compare": "তুলনা করুন", - "sideBySide": "পাশাপাশি", - "inline": "ইনলাইন", - "fileComparison": "ফাইল তুলনা: {{file1}} বনাম {{file2}}", - "fileTooLarge": "ফাইলের আকার অনেক বড়: {{error}}", - "sshConnectionFailed": "SSH সংযোগ ব্যর্থ হয়েছে। অনুগ্রহ করে {{name}} ({{ip}}:{{port}} ) -এ আপনার সংযোগ পরীক্ষা করুন।", - "loadFileFailed": "ফাইল লোড করতে ব্যর্থ: {{error}}", - "connecting": "সংযোগ স্থাপন...", - "connectedSuccessfully": "সফলভাবে সংযুক্ত হয়েছে", - "totpVerificationFailed": "TOTP যাচাইকরণ ব্যর্থ হয়েছে", - "warpgateVerificationFailed": "ওয়ার্পগেট প্রমাণীকরণ ব্যর্থ হয়েছে", - "authenticationFailed": "প্রমাণীকরণ ব্যর্থ হয়েছে", - "verificationCodePrompt": "যাচাইকরণ কোড:", - "changePermissions": "অনুমতি পরিবর্তন করুন", - "currentPermissions": "বর্তমান অনুমতি", - "owner": "মালিক", - "group": "গ্রুপ", - "others": "অন্যান্য", - "read": "পড়ুন", - "write": "লিখুন", - "execute": "কার্যকর করুন", - "permissionsChangedSuccessfully": "অনুমতি সফলভাবে পরিবর্তন করা হয়েছে", - "failedToChangePermissions": "অনুমতি পরিবর্তন করতে ব্যর্থ হয়েছে", - "name": "নাম", - "sortByName": "নাম", - "sortByDate": "পরিবর্তিত তারিখ", - "sortBySize": "আকার", - "ascending": "আরোহী", - "descending": "অবতরণ", - "root": "মূল", - "new": "নতুন", - "sortBy": "অনুসারে সাজান", - "items": "আইটেম", - "selected": "নির্বাচিত", - "editor": "সম্পাদক", - "octal": "অক্টাল", - "storage": "স্টোরেজ", - "disk": "ডিস্ক", - "used": "ব্যবহৃত", - "of": "এর", - "toggleSidebar": "সাইডবার টগল করুন", - "cannotLoadPdf": "পিডিএফ লোড করা যাচ্ছে না", - "pdfLoadError": "এই পিডিএফ ফাইলটি লোড করতে ত্রুটি হয়েছে।", - "loadingPdf": "পিডিএফ লোড হচ্ছে...", - "loadingPage": "পৃষ্ঠা লোড হচ্ছে..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "হোস্টে কপি করুন…", - "moveToHost": "হোস্টে যান…", - "copyItemsToHost": "{{count}} টি আইটেম হোস্টে কপি করুন…", - "moveItemsToHost": "{{count}} টি আইটেম… টি হোস্টে সরান", - "noHostsConnected": "অন্য কোনো ফাইল-ম্যানেজার হোস্ট উপলব্ধ নেই।", - "noHostsConnectedHint": "হোস্ট ম্যানেজারে ফাইল ম্যানেজার সক্রিয় করা আছে এমন আরেকটি SSH হোস্ট যোগ করুন।", - "selectDestinationHost": "গন্তব্য হোস্ট নির্বাচন করুন", - "destinationPath": "গন্তব্য পথ", - "recentDestinations": "সাম্প্রতিক গন্তব্যস্থল", - "collapseRecentDestinations": "সাম্প্রতিক গন্তব্যগুলি সংকুচিত করুন", - "expandRecentDestinations": "সাম্প্রতিক গন্তব্যস্থলগুলি প্রসারিত করুন", - "browseFolders": "গন্তব্য ফোল্ডারগুলি ব্রাউজ করুন", - "browseDestination": "ব্রাউজ করুন অথবা পথ লিখুন", - "confirmCopy": "অনুলিপি", - "confirmMove": "স্থানান্তর", - "transferring": "… স্থানান্তর করা হচ্ছে", - "compressing": "… সংকুচিত করা হচ্ছে", - "extracting": "… নিষ্কাশন করা হচ্ছে", - "transferringItems": "{{current}} টি {{total}} টি আইটেম… টি স্থানান্তর করা হচ্ছে", - "transferSuccess": "স্থানান্তর সম্পন্ন হয়েছে", - "transferError": "স্থানান্তর ব্যর্থ হয়েছে", - "transferPartial": "{{count}} টি ত্রুটি সহ স্থানান্তর সম্পন্ন হয়েছে।", - "transferPartialHint": "স্থানান্তর করা যায়নি: {{paths}}", - "itemsSummary": "{{count}} আইটেম", - "destMustBeDirectory": "একাধিক আইটেম স্থানান্তরের জন্য গন্তব্য অবশ্যই একটি ডিরেক্টরি হতে হবে।", - "selectThisFolder": "এই ফোল্ডারটি নির্বাচন করুন", - "browsePathWillBeCreated": "এই ফোল্ডারটি এখনও তৈরি হয়নি। স্থানান্তর শুরু হলে এটি তৈরি হয়ে যাবে।", - "browsePathError": "গন্তব্য হোস্টে এই পাথটি খোলা সম্ভব হয়নি।", - "goUp": "উপরে যান", - "copyFolderToHost": "ফোল্ডারটি হোস্টে কপি করুন…", - "moveFolderToHost": "ফোল্ডারটি হোস্টে সরান…", - "hostReady": "প্রস্তুত", - "hostConnecting": "… সংযোগ করা হচ্ছে", - "hostDisconnected": "সংযুক্ত নয়", - "hostAuthRequired": "প্রমাণীকরণ আবশ্যক — প্রথমে এই হোস্টে ফাইল ম্যানেজার খুলুন।", - "hostConnectionFailed": "সংযোগ ব্যর্থ হয়েছে", - "metricsTitle": "স্থানান্তর সময়", - "metricsPrepare": "গন্তব্য প্রস্তুত করুন: {{duration}}", - "metricsCompress": "উৎস থেকে সংকুচিত করুন: {{duration}}", - "metricsHopSourceRead": "উৎস → সার্ভার: {{throughput}}", - "metricsHopDestSftpWrite": "সার্ভার → গন্তব্য (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "সার্ভার → গন্তব্য (স্থানীয়): {{throughput}}", - "metricsTransfer": "প্রান্ত থেকে প্রান্ত: {{throughput}} ({{duration}})", - "metricsExtract": "গন্তব্যে নিষ্কাশন: {{duration}}", - "metricsSourceDelete": "উৎস থেকে সরান: {{duration}}", - "metricsTotal": "মোট: {{duration}}", - "progressCompressing": "উৎস হোস্টে সংকুচিত করা হচ্ছে…", - "progressExtracting": "গন্তব্য… থেকে নিষ্কাশন করা হচ্ছে", - "progressTransferring": "ডেটা স্থানান্তর করা হচ্ছে…", - "progressReconnecting": "পুনরায় সংযোগ স্থাপন…", - "parallelSegmentsLabel": "সমান্তরাল স্থানান্তর লেন", - "parallelSegmentsOption": "{{count}} লেন", - "parallelSegmentsHint": "বড় ফাইলগুলোকে ২৫৬ মেগাবাইটের খণ্ডে ভাগ করা হয়। মোট থ্রুপুট বাড়ানোর জন্য একাধিক লেন আলাদা সংযোগ ব্যবহার করে (যেমন কয়েকটি ট্রান্সফার শুরু করা)।", - "progressTotalSpeed": "{{speed}} মোট ({{lanes}} লেন)", - "progressTransferringItems": "ফাইল স্থানান্তর করা হচ্ছে ({{current}} {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} ফাইল", - "sourceNotDeletedPartial": "উৎস ফাইলগুলি রাখা হয়েছে (আংশিক স্থানান্তর)", - "jumpHostLimitation": "উভয় হোস্টকেই টারমিক্স সার্ভার থেকে পৌঁছানো সম্ভব হতে হবে। সরাসরি হোস্ট-টু-হোস্ট রাউটিং সমর্থিত নয়।", - "cancel": "বাতিল করুন", - "methodLabel": "স্থানান্তর পদ্ধতি", - "methodAuto": "অটো", - "methodTar": "টার আর্কাইভ", - "methodItemSftp": "প্রতি-ফাইল এসএফটিপি", - "methodAutoHint": "ফাইলের সংখ্যা, আকার এবং সংকোচনযোগ্যতার উপর ভিত্তি করে tar অথবা ফাইল-ভিত্তিক SFTP নির্বাচন করে। একক ফাইলের ক্ষেত্রে সর্বদা স্ট্রিমিং SFTP ব্যবহৃত হয়।", - "methodTarHint": "উৎসস্থলে কম্প্রেস করুন, একটি আর্কাইভ স্থানান্তর করুন, এবং গন্তব্যে এক্সট্র্যাক্ট করুন। উভয় ইউনিক্স হোস্টে tar প্রয়োজন।", - "methodItemSftpHint": "SFTP-এর মাধ্যমে প্রতিটি ফাইল আলাদাভাবে স্থানান্তর করুন। উইন্ডোজ সহ সকল হোস্টে কাজ করে।", - "methodPreviewLoading": "স্থানান্তর পদ্ধতি গণনা করা হচ্ছে…", - "methodPreviewError": "স্থানান্তর পদ্ধতির প্রিভিউ দেখা সম্ভব হয়নি। আপনি চালু করার সময় সার্ভার নিজেই একটি পদ্ধতি বেছে নেবে।", - "methodPreviewWillUseTar": "ব্যবহৃত হবে: টার আর্কাইভ", - "methodPreviewWillUseItemSftp": "ব্যবহার করা হবে: প্রতি-ফাইল এসএফটিপি", - "methodPreviewScanSummary": "{{fileCount}} টি ফাইল, {{totalSize}} টি মোট (উৎস হোস্টে স্ক্যান করা হয়েছে)।", - "methodItemSftpLimitation": "প্রতিটি ফাইল একই SFTP স্ট্রিম ব্যবহার করে একটির পর একটি একক-ফাইল কপি হিসেবে তৈরি হয়। সমস্ত ফাইলের অগ্রগতি একত্রিত করা হয়, তাই বড় ফাইলের ক্ষেত্রে বারটি ধীরে চলে।", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "আপনি ফাইল-ভিত্তিক এসএফটিপি বেছে নিয়েছেন।", - "user_tar": "আপনি tar আর্কাইভটি বেছে নিয়েছেন।", - "tar_unavailable": "এক বা উভয় হোস্টে Tar উপলব্ধ না থাকায়, এর পরিবর্তে ফাইল-ভিত্তিক SFTP ব্যবহার করা হবে।", - "windows_host": "এখানে একটি উইন্ডোজ হোস্ট জড়িত — tar ব্যবহৃত হয় না।", - "auto_multi_large": "স্বয়ংক্রিয়: সংকোচনযোগ্য ডেটা সহ একটি বড় ফাইল ({{largestSize}}) সহ একাধিক ফাইল — tar বান্ডেল করে একটি একক স্থানান্তরে পাঠানো হয়।", - "auto_single_large_in_archive": "স্বয়ংক্রিয়: এই সেটে একটি বড় ফাইল ({{largestSize}}) — ফাইল-ভিত্তিক SFTP।", - "auto_many_incompressible": "স্বয়ংক্রিয়: প্রধানত অসংকোচনযোগ্য ডেটা — ফাইল-ভিত্তিক এসএফটিপি।", - "auto_many_files": "স্বয়ংক্রিয়: একাধিক ফাইল ({{fileCount}}) — tar প্রতি-ফাইল ওভারহেড কমায়।", - "auto_default": "স্বয়ংক্রিয়: এই সেটের জন্য প্রতি-ফাইল এসএফটিপি।" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "বাতিল করুন", - "progressCancelling": "বাতিল করা হচ্ছে…", - "progressStalled": "স্থবির", - "resumedHint": "অন্য একটি উইন্ডোতে শুরু হওয়া একটি সক্রিয় ট্রান্সফারের সাথে পুনরায় সংযুক্ত হওয়া গেছে।", - "transferCancelled": "স্থানান্তর বাতিল করা হয়েছে", - "transferCancelledCopyHint": "গন্তব্যে আংশিক ফাইল থেকে যেতে পারে।", - "transferCancelledMoveHint": "গন্তব্যে আংশিক ফাইল থেকে যেতে পারে। মূল ফাইলগুলো সরানো হয়নি।", - "cleanupDestFiles": "পরিষ্কার করার গন্তব্য", - "cleanupDestFilesSuccess": "গন্তব্য থেকে আংশিক ফাইল সরানো হয়েছে", - "cleanupDestFilesPartial": "কিছু আংশিক ফাইল সরানো যায়নি", - "cleanupDestFilesNothing": "গন্তব্যে পরিষ্কার করার কিছু নেই।", - "cleanupDestFilesError": "পরিষ্কার করা ব্যর্থ হয়েছে", - "retryTransfer": "পুনরায় চেষ্টা করুন", - "retryTransferError": "পুনরায় চেষ্টা ব্যর্থ হয়েছে", - "transferFailedRetryHint": "গন্তব্যে আংশিক ডেটা সংরক্ষিত হয়েছে। সংযোগ ফিরে এলে পুনরায় চেষ্টা শুরু হবে।" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "কোন SSH টানেল নেই", - "createFirstTunnelMessage": "আপনি এখনও কোনো SSH টানেল তৈরি করেননি। শুরু করার জন্য হোস্ট ম্যানেজারে টানেল সংযোগগুলি কনফিগার করুন।", - "connected": "সংযুক্ত", - "disconnected": "বিচ্ছিন্ন", - "connecting": "সংযোগ স্থাপন...", - "error": "ত্রুটি", - "canceling": "বাতিল করা হচ্ছে...", - "connect": "সংযোগ করুন", - "disconnect": "সংযোগ বিচ্ছিন্ন করুন", - "cancel": "বাতিল করুন", - "port": "বন্দর", - "localPort": "স্থানীয় বন্দর", - "remotePort": "রিমোট পোর্ট", - "currentHostPort": "বর্তমান হোস্ট পোর্ট", - "endpointPort": "এন্ডপয়েন্ট পোর্ট", - "bindIp": "স্থানীয় আইপি", - "endpointSshConfig": "এন্ডপয়েন্ট SSH কনফিগারেশন", - "endpointSshHost": "এন্ডপয়েন্ট SSH হোস্ট", - "endpointSshHostPlaceholder": "একটি কনফিগার করা হোস্ট নির্বাচন করুন", - "endpointSshHostRequired": "প্রতিটি ক্লায়েন্ট টানেলের জন্য একটি এন্ডপয়েন্ট SSH হোস্ট নির্বাচন করুন।", - "attempt": "{{max}} এর {{current}} প্রচেষ্টা", - "nextRetryIn": "{{seconds}} সেকেন্ড পর আবার চেষ্টা করা হবে।", - "clientTunnels": "ক্লায়েন্ট টানেল", - "clientTunnel": "ক্লায়েন্ট টানেল", - "addClientTunnel": "ক্লায়েন্ট টানেল যোগ করুন", - "noClientTunnels": "এই ডেস্কটপে কোনো ক্লায়েন্ট টানেল কনফিগার করা নেই।", - "tunnelName": "টানেলের নাম", - "remoteHost": "রিমোট হোস্ট", - "autoStart": "অটো স্টার্ট", - "clientAutoStartDesc": "এই ডেস্কটপ ক্লায়েন্টটি খুললে এটি চালু হয় এবং সংযুক্ত থাকে।", - "clientManualStartDesc": "এই সারি থেকে স্টার্ট এবং স্টপ ব্যবহার করুন। টারমিক্স এটি স্বয়ংক্রিয়ভাবে খুলবে না।", - "clientRemoteServerNote": "রিমোট ফরওয়ার্ডিংয়ের জন্য এন্ডপয়েন্ট SSH সার্ভারে AllowTcpForwarding এবং GatewayPorts চালু করার প্রয়োজন হতে পারে। এই ডেস্কটপটি সংযোগ বিচ্ছিন্ন করলে রিমোট পোর্টটি বন্ধ হয়ে যায়।", - "clientTunnelStarted": "ক্লায়েন্ট টানেল শুরু হয়েছে", - "clientTunnelStopped": "ক্লায়েন্ট টানেল বন্ধ হয়ে গেছে", - "tunnelTestSucceeded": "টানেল পরীক্ষা সফল হয়েছে", - "tunnelTestFailed": "টানেল পরীক্ষা ব্যর্থ হয়েছে", - "localSaved": "ক্লায়েন্ট টানেলগুলি সংরক্ষিত হয়েছে", - "localSaveError": "স্থানীয় ক্লায়েন্ট টানেলগুলি সংরক্ষণ করতে ব্যর্থ হয়েছে", - "invalidBindIp": "লোকাল আইপি অবশ্যই একটি বৈধ IPv4 অ্যাড্রেস হতে হবে।", - "invalidLocalTargetIp": "স্থানীয় টার্গেট আইপি অবশ্যই একটি বৈধ IPv4 অ্যাড্রেস হতে হবে।", - "invalidLocalPort": "স্থানীয় পোর্ট অবশ্যই ১ থেকে ৬৫৫৩৫-এর মধ্যে হতে হবে।", - "invalidRemotePort": "রিমোট পোর্ট অবশ্যই ১ থেকে ৬৫৫৩৫-এর মধ্যে হতে হবে।", - "invalidLocalTargetPort": "স্থানীয় টার্গেট পোর্ট অবশ্যই ১ থেকে ৬৫৫৩৫-এর মধ্যে হতে হবে।", - "invalidEndpointPort": "এন্ডপয়েন্ট পোর্ট অবশ্যই ১ থেকে ৬৫৫৩৫-এর মধ্যে হতে হবে।", - "duplicateAutoStartBind": "শুধুমাত্র একটি অটো-স্টার্ট ক্লায়েন্ট টানেল {{bind}} ব্যবহার করতে পারে।", - "manualControlError": "টানেলের অবস্থা আপডেট করতে ব্যর্থ হয়েছে।", - "active": "সক্রিয়", - "start": "শুরু করুন", - "stop": "থামুন", - "test": "পরীক্ষা", - "type": "টানেল টাইপ", - "typeLocal": "স্থানীয় (-L)", - "typeRemote": "রিমোট (-R)", - "typeDynamic": "গতিশীল (-D)", - "typeServerLocalDesc": "বর্তমান হোস্ট থেকে এন্ডপয়েন্ট।", - "typeServerRemoteDesc": "এন্ডপয়েন্টটি বর্তমান হোস্টে ফিরে যাচ্ছে।", - "typeClientLocalDesc": "স্থানীয় কম্পিউটার থেকে এন্ডপয়েন্ট পর্যন্ত।", - "typeClientRemoteDesc": "এন্ডপয়েন্টটি স্থানীয় কম্পিউটারে ফিরে যাচ্ছে।", - "typeClientDynamicDesc": "স্থানীয় কম্পিউটারে SOCKS।", - "typeDynamicDesc": "SSH এর মাধ্যমে SOCKS5 CONNECT ট্র্যাফিক ফরওয়ার্ড করুন", - "forwardDescriptionServerLocal": "বর্তমান হোস্ট {{sourcePort}} → শেষ বিন্দু {{endpointPort}}.", - "forwardDescriptionServerRemote": "এন্ডপয়েন্ট {{endpointPort}} → বর্তমান হোস্ট {{sourcePort}}.", - "forwardDescriptionServerDynamic": "বর্তমান হোস্টে SOCKS {{sourcePort}}।", - "forwardDescriptionClientLocal": "স্থানীয় {{sourcePort}} → দূরবর্তী {{endpointPort}}.", - "forwardDescriptionClientRemote": "রিমোট {{sourcePort}} → লোকাল {{endpointPort}}.", - "forwardDescriptionClientDynamic": "স্থানীয় পোর্ট {{sourcePort}}-এ SOCKS।", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "স্থানীয় {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → স্থানীয় {{localPort}}", - "autoNameClientDynamic": "মোজা {{localPort}} {{endpoint}} এর মাধ্যমে", - "route": "পথ:", - "lastStarted": "সর্বশেষ শুরু হয়েছে", - "lastTested": "সর্বশেষ পরীক্ষা করা হয়েছে", - "lastError": "শেষ ত্রুটি", - "maxRetries": "সর্বোচ্চ পুনঃপ্রচেষ্টা", - "maxRetriesDescription": "পুনরায় চেষ্টার সর্বোচ্চ সংখ্যা।", - "retryInterval": "পুনরায় চেষ্টার ব্যবধান (সেকেন্ড)", - "retryIntervalDescription": "পুনরায় চেষ্টার মাঝে অপেক্ষার সময়।", - "local": "স্থানীয়", - "remote": "দূরবর্তী", - "destination": "গন্তব্য", - "host": "হোস্ট", - "mode": "মোড", - "noHostSelected": "কোন হোস্ট নির্বাচন করা হয়নি", - "working": "কাজ চলছে..." + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "সিপিইউ", - "memory": "স্মৃতি", - "disk": "ডিস্ক", - "network": "নেটওয়ার্ক", - "uptime": "আপটাইম", - "processes": "প্রক্রিয়া", - "available": "উপলব্ধ", - "free": "বিনামূল্যে", - "connecting": "সংযোগ স্থাপন...", - "connectionFailed": "সার্ভারের সাথে সংযোগ করতে ব্যর্থ হয়েছে", - "naCpus": "প্রযোজ্য নয় সিপিইউ(গুলি)", - "cpuCores_one": "{{count}} কোর", - "cpuCores_other": "{{count}} কোর", - "cpuUsage": "সিপিইউ ব্যবহার", - "memoryUsage": "মেমরি ব্যবহার", - "diskUsage": "ডিস্ক ব্যবহার", - "failedToFetchHostConfig": "হোস্ট কনফিগারেশন আনতে ব্যর্থ হয়েছে", - "serverOffline": "সার্ভার অফলাইন", - "cannotFetchMetrics": "অফলাইন সার্ভার থেকে মেট্রিক্স সংগ্রহ করা যাচ্ছে না", - "totpFailed": "TOTP যাচাইকরণ ব্যর্থ হয়েছে", - "noneAuthNotSupported": "সার্ভার স্ট্যাটস 'none' অথেনটিকেশন টাইপ সমর্থন করে না।", - "load": "লোড", - "systemInfo": "সিস্টেমের তথ্য", - "hostname": "হোস্টনাম", - "operatingSystem": "অপারেটিং সিস্টেম", - "kernel": "কার্নেল", - "seconds": "সেকেন্ড", - "networkInterfaces": "নেটওয়ার্ক ইন্টারফেস", - "noInterfacesFound": "কোন নেটওয়ার্ক ইন্টারফেস খুঁজে পাওয়া যায়নি", - "noProcessesFound": "কোন প্রক্রিয়া খুঁজে পাওয়া যায়নি", - "loginStats": "SSH লগইন পরিসংখ্যান", - "noRecentLoginData": "সাম্প্রতিক কোনো লগইন ডেটা নেই", - "executingQuickAction": "{{name}} কার্যকর করা হচ্ছে ...", - "quickActionSuccess": "{{name}} সফলভাবে সম্পন্ন হয়েছে", - "quickActionFailed": "{{name}} ব্যর্থ হয়েছে", - "quickActionError": "{{name}} কার্যকর করতে ব্যর্থ হয়েছে", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "লিসেনিং পোর্ট", - "protocol": "প্রোটোকল", - "port": "বন্দর", - "address": "ঠিকানা", - "process": "প্রক্রিয়া", - "noData": "কোন লিসেনিং পোর্ট ডেটা নেই" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "ফায়ারওয়াল", - "inactive": "নিষ্ক্রিয়", - "policy": "নীতি", - "rules": "নিয়ম", - "noData": "কোন ফায়ারওয়াল ডেটা উপলব্ধ নেই", - "action": "পদক্ষেপ", - "protocol": "প্রোটো", - "port": "বন্দর", - "source": "উৎস", - "anywhere": "যেকোনো জায়গায়" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "গড় লোড", - "swap": "অদলবদল", - "architecture": "স্থাপত্য", - "refresh": "রিফ্রেশ", - "retry": "পুনরায় চেষ্টা করুন" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "সেলফ-হোস্টেড SSH এবং রিমোট ডেস্কটপ ম্যানেজমেন্ট", - "loginTitle": "Termix-এ লগইন করুন", - "registerTitle": "অ্যাকাউন্ট তৈরি করুন", - "forgotPassword": "পাসওয়ার্ড ভুলে গেছেন?", - "rememberMe": "৩০ দিনের জন্য ডিভাইস মনে রাখুন (TOTP অন্তর্ভুক্ত)", - "noAccount": "আপনার কি অ্যাকাউন্ট নেই?", - "hasAccount": "আপনার কি ইতিমধ্যেই একটি অ্যাকাউন্ট আছে?", - "twoFactorAuth": "দ্বি-স্তরীয় প্রমাণীকরণ", - "enterCode": "যাচাইকরণ কোড প্রবেশ করান", - "backupCode": "অথবা ব্যাকআপ কোড ব্যবহার করুন", - "verifyCode": "কোড যাচাই করুন", - "redirectingToApp": "অ্যাপে পুনঃনির্দেশিত করা হচ্ছে...", - "sshAuthenticationRequired": "SSH প্রমাণীকরণ আবশ্যক", - "sshNoKeyboardInteractive": "কিবোর্ড-ভিত্তিক প্রমাণীকরণ অনুপলব্ধ", - "sshAuthenticationFailed": "প্রমাণীকরণ ব্যর্থ হয়েছে", - "sshAuthenticationTimeout": "প্রমাণীকরণ সময়সীমা", - "sshNoKeyboardInteractiveDescription": "সার্ভারটি কিবোর্ড-ভিত্তিক প্রমাণীকরণ সমর্থন করে না। অনুগ্রহ করে আপনার পাসওয়ার্ড অথবা SSH কী প্রদান করুন।", - "sshAuthFailedDescription": "প্রদত্ত তথ্য ভুল ছিল। অনুগ্রহ করে সঠিক তথ্য দিয়ে আবার চেষ্টা করুন।", - "sshTimeoutDescription": "প্রমাণীকরণের প্রচেষ্টাটির সময়সীমা শেষ হয়ে গেছে। অনুগ্রহ করে আবার চেষ্টা করুন।", - "sshProvideCredentialsDescription": "এই সার্ভারে সংযোগ করতে অনুগ্রহ করে আপনার SSH ক্রেডেনশিয়াল প্রদান করুন।", - "sshPasswordDescription": "এই SSH সংযোগের জন্য পাসওয়ার্ডটি প্রবেশ করান।", - "sshKeyPasswordDescription": "আপনার SSH কী এনক্রিপ্ট করা থাকলে, এখানে পাসফ্রেজটি লিখুন।", - "passphraseRequired": "পাসফ্রেজ আবশ্যক", - "passphraseRequiredDescription": "SSH কী-টি এনক্রিপ্ট করা আছে। এটি আনলক করতে অনুগ্রহ করে পাসফ্রেজটি প্রবেশ করান।", - "back": "ফিরে যান", - "firstUser": "প্রথম ব্যবহারকারী", - "firstUserMessage": "আপনি প্রথম ব্যবহারকারী এবং আপনাকে অ্যাডমিন করা হবে। আপনি সাইডবারের ইউজার ড্রপডাউনে অ্যাডমিন সেটিংস দেখতে পারেন। যদি আপনার মনে হয় এটি একটি ভুল, তাহলে ডকার লগ চেক করুন, অথবা একটি গিটহাব ইস্যু তৈরি করুন।", - "external": "বাহ্যিক", - "loginWithExternal": "বাহ্যিক সরবরাহকারী দিয়ে লগইন করুন", - "loginWithExternalDesc": "আপনার কনফিগার করা বাহ্যিক পরিচয় প্রদানকারী ব্যবহার করে লগইন করুন।", - "externalNotSupportedInElectron": "ইলেকট্রন অ্যাপে এখনও বাহ্যিক প্রমাণীকরণ সমর্থিত নয়। OIDC লগইনের জন্য অনুগ্রহ করে ওয়েব সংস্করণ ব্যবহার করুন।", - "resetPasswordButton": "পাসওয়ার্ড রিসেট করুন", - "sendResetCode": "রিসেট কোড পাঠান", - "resetCodeDesc": "পাসওয়ার্ড রিসেট কোড পেতে আপনার ইউজারনেম লিখুন। কোডটি ডকার কন্টেইনার লগে নথিভুক্ত করা হবে।", - "resetCode": "রিসেট কোড", - "verifyCodeButton": "কোড যাচাই করুন", - "enterResetCode": "ব্যবহারকারীর জন্য ডকার কন্টেইনার লগ থেকে ৬-সংখ্যার কোডটি প্রবেশ করান:", - "newPassword": "নতুন পাসওয়ার্ড", - "confirmNewPassword": "পাসওয়ার্ড নিশ্চিত করুন", - "enterNewPassword": "ব্যবহারকারীর জন্য আপনার নতুন পাসওয়ার্ড লিখুন:", - "signUp": "সাইন আপ করুন", - "desktopApp": "ডেস্কটপ অ্যাপ", - "loggingInToDesktopApp": "ডেস্কটপ অ্যাপে লগ ইন করা", - "loadingServer": "সার্ভার লোড হচ্ছে...", - "dataLossWarning": "এইভাবে আপনার পাসওয়ার্ড রিসেট করলে আপনার সেভ করা সমস্ত SSH হোস্ট, ক্রেডেনশিয়াল এবং অন্যান্য এনক্রিপ্টেড ডেটা মুছে যাবে। এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না। শুধুমাত্র তখনই এটি ব্যবহার করুন যখন আপনি আপনার পাসওয়ার্ড ভুলে গেছেন এবং লগ ইন করা অবস্থায় নেই।", - "authenticationDisabled": "প্রমাণীকরণ নিষ্ক্রিয় করা হয়েছে", - "authenticationDisabledDesc": "সকল প্রমাণীকরণ পদ্ধতি বর্তমানে নিষ্ক্রিয় রয়েছে। অনুগ্রহ করে আপনার প্রশাসকের সাথে যোগাযোগ করুন।", - "attemptsRemaining": "{{count}} টি প্রচেষ্টা বাকি" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH হোস্ট কী যাচাই করুন", - "keyChangedWarning": "SSH হোস্ট কী পরিবর্তন করা হয়েছে", - "firstConnectionTitle": "এই হোস্টে প্রথমবার সংযোগ করছি", - "firstConnectionDescription": "এই হোস্টের সত্যতা যাচাই করা যাচ্ছে না। ফিঙ্গারপ্রিন্টটি আপনার প্রত্যাশার সাথে মেলে কিনা তা যাচাই করুন।", - "keyChangedDescription": "আপনার শেষ সংযোগের পর এই সার্ভারের হোস্ট কী পরিবর্তিত হয়েছে। এটি একটি নিরাপত্তা সমস্যার ইঙ্গিত হতে পারে।", - "previousKey": "পূর্ববর্তী চাবি", - "newFingerprint": "নতুন আঙুলের ছাপ", - "fingerprint": "আঙুলের ছাপ", - "verifyInstructions": "আপনি যদি এই হোস্টকে বিশ্বাস করেন, তবে চালিয়ে যেতে 'স্বীকার করুন' (Accept) এ ক্লিক করুন এবং ভবিষ্যতের সংযোগের জন্য এই ফিঙ্গারপ্রিন্টটি সংরক্ষণ করুন।", - "securityWarning": "নিরাপত্তা সতর্কতা", - "acceptAndContinue": "গ্রহণ করুন এবং চালিয়ে যান", - "acceptNewKey": "নতুন কী গ্রহণ করুন এবং চালিয়ে যান" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "ডাটাবেসের সাথে সংযোগ করা যায়নি", - "unknownError": "অজানা ত্রুটি", - "loginFailed": "লগইন ব্যর্থ হয়েছে", - "failedPasswordReset": "পাসওয়ার্ড রিসেট শুরু করতে ব্যর্থ হয়েছে", - "failedVerifyCode": "রিসেট কোড যাচাই করতে ব্যর্থ হয়েছে", - "failedCompleteReset": "পাসওয়ার্ড রিসেট সম্পন্ন করা যায়নি", - "invalidTotpCode": "অবৈধ TOTP কোড", - "failedOidcLogin": "OIDC লগইন শুরু করতে ব্যর্থ হয়েছে", - "silentSigninOidcUnavailable": "নীরব সাইন-ইন করার অনুরোধ করা হয়েছিল, কিন্তু OIDC লগইন উপলব্ধ নেই।", - "failedUserInfo": "লগইন করার পর ব্যবহারকারীর তথ্য পেতে ব্যর্থ হয়েছে", - "oidcAuthFailed": "OIDC প্রমাণীকরণ ব্যর্থ হয়েছে", - "invalidAuthUrl": "ব্যাকএন্ড থেকে প্রাপ্ত অনুমোদন URLটি অবৈধ।", - "requiredField": "এই ক্ষেত্রটি আবশ্যক", - "minLength": "সর্বনিম্ন দৈর্ঘ্য হল {{min}}", - "passwordMismatch": "পাসওয়ার্ডগুলো মিলছে না।", - "passwordLoginDisabled": "ইউজারনেম/পাসওয়ার্ড লগইন বর্তমানে নিষ্ক্রিয় রয়েছে।", - "sessionExpired": "সেশনের মেয়াদ শেষ হয়ে গেছে - অনুগ্রহ করে আবার লগ ইন করুন।", - "totpRateLimited": "রেট সীমিত: অনেক বেশি TOTP যাচাইকরণের চেষ্টা করা হয়েছে। অনুগ্রহ করে পরে আবার চেষ্টা করুন।", - "totpRateLimitedWithTime": "রেট সীমিত: TOTP যাচাইকরণের জন্য অনেক বেশিবার চেষ্টা করা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করার আগে {{time}} সেকেন্ড অপেক্ষা করুন।", - "resetCodeRateLimited": "রেট সীমিত: অনেক বেশি যাচাইকরণ প্রচেষ্টা করা হয়েছে। অনুগ্রহ করে পরে আবার চেষ্টা করুন।", - "resetCodeRateLimitedWithTime": "রেট সীমিত: অনেক বেশি যাচাইকরণ প্রচেষ্টা করা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করার আগে {{time}} সেকেন্ড অপেক্ষা করুন।", - "authTokenSaveFailed": "প্রমাণীকরণ টোকেন সংরক্ষণ করতে ব্যর্থ হয়েছে", - "failedToLoadServer": "সার্ভার লোড করতে ব্যর্থ হয়েছে" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "অ্যাডমিন কর্তৃক নতুন অ্যাকাউন্ট নিবন্ধন বর্তমানে বন্ধ রাখা হয়েছে। অনুগ্রহ করে লগ ইন করুন অথবা কোনো প্রশাসকের সাথে যোগাযোগ করুন।", - "userNotAllowed": "আপনার অ্যাকাউন্টটি নিবন্ধনের জন্য অনুমোদিত নয়। অনুগ্রহ করে একজন প্রশাসকের সাথে যোগাযোগ করুন।", - "databaseConnectionFailed": "ডাটাবেস সার্ভারের সাথে সংযোগ স্থাপন করা যায়নি", - "resetCodeSent": "ডকার লগে রিসেট কোড পাঠানো হয়েছে", - "codeVerified": "কোড সফলভাবে যাচাই করা হয়েছে", - "passwordResetSuccess": "পাসওয়ার্ড সফলভাবে রিসেট করা হয়েছে", - "loginSuccess": "লগইন সফল হয়েছে", - "registrationSuccess": "নিবন্ধন সফল হয়েছে" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "কনফিগার করা SSH হোস্টগুলোকে লক্ষ্য করে স্থানীয় ডেস্কটপ টানেল।", - "c2sTunnelPresets": "ক্লায়েন্ট টানেল প্রিসেট", - "c2sTunnelPresetsDesc": "এই ডেস্কটপ ক্লায়েন্টের স্থানীয় টানেল তালিকাটিকে একটি নামযুক্ত সার্ভার প্রিসেট হিসাবে সংরক্ষণ করুন, অথবা এই ক্লায়েন্টে একটি প্রিসেট পুনরায় লোড করুন।", - "c2sTunnelPresetsUnavailable": "ক্লায়েন্ট টানেল প্রিসেটগুলো শুধুমাত্র ডেস্কটপ ক্লায়েন্টে পাওয়া যায়।", - "c2sPresetName": "পূর্বনির্ধারিত নাম", - "c2sPresetNamePlaceholder": "ক্লায়েন্টের পূর্বনির্ধারিত নাম", - "c2sPresetToLoad": "লোড করার জন্য প্রিসেট", - "c2sNoPresetSelected": "কোনো পূর্বনির্ধারিত সেট নির্বাচন করা হয়নি", - "c2sNoPresets": "কোন প্রিসেট সংরক্ষণ করা হয়নি", - "c2sLoadPreset": "লোড", - "c2sCurrentLocalConfig": "এই ডেস্কটপে {{count}} টি স্থানীয় ক্লায়েন্ট টানেল কনফিগার করা হয়েছে।", - "c2sPresetSyncNote": "প্রিসেটগুলি হলো সুনির্দিষ্ট স্ন্যাপশট; এর কোনো একটি লোড করলে এই ডেস্কটপ ক্লায়েন্টের স্থানীয় ক্লায়েন্ট টানেল তালিকা প্রতিস্থাপিত হয়।", - "c2sPresetSaved": "ক্লায়েন্ট টানেল প্রিসেট সংরক্ষিত", - "c2sPresetLoaded": "ক্লায়েন্ট টানেল প্রিসেট স্থানীয়ভাবে লোড করা হয়েছে", - "c2sPresetRenamed": "ক্লায়েন্ট টানেল প্রিসেটের নাম পরিবর্তন করা হয়েছে", - "c2sPresetDeleted": "ক্লায়েন্ট টানেল প্রিসেট মুছে ফেলা হয়েছে", - "c2sPresetLoadError": "ক্লায়েন্ট টানেল প্রিসেট লোড করতে ব্যর্থ হয়েছে" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { - "maxRetries": "৩", - "retryInterval": "১০", - "language": "ভাষা", - "keyPassword": "কী পাসওয়ার্ড", - "pastePrivateKey": "আপনার প্রাইভেট কী এখানে পেস্ট করুন...", - "localListenerHost": "১২৭.০.০.১ (স্থানীয়ভাবে শুনুন)", - "localTargetHost": "১২৭.০.০.১ (এই কম্পিউটারে লক্ষ্যবস্তু)", - "socksListenerHost": "১২৭.০.০.১ (SOCKS লিসেনার)", - "enterPassword": "আপনার পাসওয়ার্ড লিখুন", - "defaultPort": "২২", - "defaultEndpointPort": "২২৪" + "maxRetries": "3", + "retryInterval": "10", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", + "defaultPort": "22", + "defaultEndpointPort": "224" }, "dashboard": { - "title": "ড্যাশবোর্ড", - "loading": "ড্যাশবোর্ড লোড হচ্ছে...", - "github": "গিটহাব", - "support": "সমর্থন", - "discord": "ডিসকর্ড", - "serverOverview": "সার্ভার ওভারভিউ", - "version": "সংস্করণ", - "upToDate": "হালনাগাদ", - "updateAvailable": "আপডেট উপলব্ধ", - "beta": "বিটা", - "uptime": "আপটাইম", - "database": "ডাটাবেস", - "healthy": "স্বাস্থ্যকর", - "error": "ত্রুটি", - "totalHosts": "মোট হোস্ট", - "totalTunnels": "মোট টানেল", - "totalCredentials": "মোট যোগ্যতা", - "recentActivity": "সাম্প্রতিক কার্যকলাপ", - "reset": "রিসেট", - "loadingRecentActivity": "সাম্প্রতিক কার্যকলাপ লোড হচ্ছে...", - "noRecentActivity": "সাম্প্রতিক কোনো কার্যকলাপ নেই", - "quickActions": "দ্রুত পদক্ষেপ", - "addHost": "হোস্ট যোগ করুন", - "addCredential": "পরিচয়পত্র যোগ করুন", - "adminSettings": "অ্যাডমিন সেটিংস", - "userProfile": "ব্যবহারকারীর প্রোফাইল", - "serverStats": "সার্ভার পরিসংখ্যান", - "loadingServerStats": "সার্ভারের পরিসংখ্যান লোড হচ্ছে...", - "noServerData": "কোন সার্ভার ডেটা উপলব্ধ নেই", - "cpu": "সিপিইউ", - "ram": "রাম", - "customizeLayout": "ড্যাশবোর্ড কাস্টমাইজ করুন", - "dashboardSettings": "ড্যাশবোর্ড সেটিংস", - "enableDisableCards": "কার্ডগুলি সক্রিয়/নিষ্ক্রিয় করুন", - "resetLayout": "ডিফল্টে রিসেট করুন", - "serverOverviewCard": "সার্ভার ওভারভিউ", - "recentActivityCard": "সাম্প্রতিক কার্যকলাপ", - "networkGraphCard": "নেটওয়ার্ক গ্রাফ", - "networkGraph": "নেটওয়ার্ক গ্রাফ", - "quickActionsCard": "দ্রুত পদক্ষেপ", - "serverStatsCard": "সার্ভার পরিসংখ্যান", - "panelMain": "প্রধান", - "panelSide": "পাশ", - "justNow": "এইমাত্র" + "title": "Dashboard", + "loading": "Loading dashboard...", + "github": "GitHub", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "স্থিতিশীল", - "hostsOnline": "হোস্ট অনলাইন", - "activeTunnels": "সক্রিয় টানেল", - "registerNewServer": "একটি নতুন সার্ভার নিবন্ধন করুন", - "storeSshKeysOrPasswords": "SSH কী বা পাসওয়ার্ড সংরক্ষণ করুন", - "manageUsersAndRoles": "ব্যবহারকারী এবং ভূমিকা পরিচালনা করুন", - "manageYourAccount": "আপনার অ্যাকাউন্ট পরিচালনা করুন", - "hostStatus": "হোস্ট স্ট্যাটাস", - "noHostsConfigured": "কোন হোস্ট কনফিগার করা নেই", - "online": "অনলাইন", - "offline": "অফলাইন", - "onlineLower": "অনলাইন", - "nodes": "{{count}} নোড", - "add": "যোগ করুন:", - "commandPalette": "কমান্ড প্যালেট", - "done": "সম্পন্ন হয়েছে", - "editModeInstructions": "কার্ডগুলোর ক্রম পরিবর্তন করতে টেনে আনুন · কলামের আকার পরিবর্তন করতে কলাম বিভাজকটি টেনে আনুন · কার্ডের উচ্চতার পরিবর্তন করতে এর নিচের প্রান্তটি টেনে আনুন · মুছে ফেলতে ট্র্যাশ ব্যবহার করুন", - "empty": "খালি", - "clear": "পরিষ্কার" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "হোস্ট যোগ করুন", - "addGroup": "গ্রুপ যোগ করুন", - "addLink": "লিঙ্ক যোগ করুন", - "zoomIn": "জুম ইন", - "zoomOut": "জুম আউট", - "resetView": "দৃশ্য রিসেট করুন", - "selectHost": "হোস্ট নির্বাচন করুন", - "chooseHost": "একজন হোস্ট বেছে নিন...", - "parentGroup": "অভিভাবক গোষ্ঠী", - "noGroup": "কোন গ্রুপ নেই", - "groupName": "গ্রুপের নাম", - "color": "রঙ", - "source": "উৎস", - "target": "লক্ষ্য", - "moveToGroup": "গ্রুপে যান", - "selectGroup": "দল নির্বাচন করুন...", - "addConnection": "সংযোগ যোগ করুন", - "hostDetails": "হোস্টের বিবরণ", - "removeFromGroup": "গ্রুপ থেকে অপসারণ করুন", - "addHostHere": "এখানে হোস্ট যোগ করুন", - "editGroup": "সম্পাদনা গ্রুপ", - "delete": "মুছে ফেলুন", - "add": "যোগ করুন", - "create": "তৈরি করুন", - "move": "স্থানান্তর", - "connect": "সংযোগ করুন", - "createGroup": "গ্রুপ তৈরি করুন", - "selectSourcePlaceholder": "উৎস নির্বাচন করুন...", - "selectTargetPlaceholder": "লক্ষ্য নির্বাচন করুন...", - "invalidFile": "অবৈধ ফাইল", - "hostAlreadyExists": "হোস্ট ইতিমধ্যে টপোলজিতে আছে", - "connectionExists": "সংযোগটি ইতিমধ্যেই বিদ্যমান আছে", - "unknown": "অজানা", - "name": "নাম", - "ip": "আইপি", - "status": "অবস্থা", - "failedToAddNode": "নোড যোগ করতে ব্যর্থ হয়েছে", - "sourceDifferentFromTarget": "উৎস এবং লক্ষ্য অবশ্যই ভিন্ন হতে হবে", - "exportJSON": "JSON রপ্তানি করুন", - "importJSON": "JSON আমদানি করুন", - "terminal": "টার্মিনাল", - "fileManager": "ফাইল ম্যানেজার", - "tunnel": "টানেল", - "docker": "ডকার", - "serverStats": "সার্ভার পরিসংখ্যান", - "noNodes": "এখনো কোনো নোড নেই" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "এই হোস্টের জন্য ডকার সক্রিয় করা নেই।", - "validating": "ডকার যাচাই করা হচ্ছে...", - "connecting": "সংযোগ স্থাপন...", - "error": "ত্রুটি", - "version": "ডকার {{version}}", - "connectionFailed": "ডকারের সাথে সংযোগ করতে ব্যর্থ হয়েছে", - "containerStarted": "কন্টেইনার {{name}} চালু হয়েছে", - "failedToStartContainer": "{{name}} কন্টেইনারটি শুরু করতে ব্যর্থ হয়েছে", - "containerStopped": "কন্টেইনার {{name}} থেমে গেছে", - "failedToStopContainer": "কন্টেইনার বন্ধ করতে ব্যর্থ {{name}}", - "containerRestarted": "কন্টেইনার {{name}} পুনরায় চালু হয়েছে", - "failedToRestartContainer": "কন্টেইনার পুনরায় চালু করতে ব্যর্থ {{name}}", - "containerPaused": "কন্টেইনার {{name}} থামানো হয়েছে", - "containerUnpaused": "কন্টেইনার {{name}} বিরতিহীন", - "failedToTogglePauseContainer": "কন্টেইনার {{name}} এর জন্য বিরতি অবস্থা টগল করতে ব্যর্থ হয়েছে", - "containerRemoved": "কন্টেইনার {{name}} সরানো হয়েছে", - "failedToRemoveContainer": "কন্টেইনার অপসারণ করা সম্ভব হয়নি {{name}}", - "image": "ছবি", - "ports": "বন্দর", - "noPorts": "কোন পোর্ট নেই", - "start": "শুরু করুন", - "confirmRemoveContainer": "আপনি কি '{{name}}' কন্টেইনারটি সরাতে নিশ্চিত? এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না।", - "runningContainerWarning": "সতর্কীকরণ: এই কন্টেইনারটি বর্তমানে চালু আছে। এটি অপসারণ করলে প্রথমে কন্টেইনারটি বন্ধ হবে।", - "loadingContainers": "কন্টেইনার লোড করা হচ্ছে...", - "manager": "ডকার ম্যানেজার", - "autoRefresh": "স্বয়ংক্রিয় রিফ্রেশ", - "timestamps": "টাইমস্ট্যাম্প", - "lines": "লাইন", - "filterLogs": "লগ ফিল্টার করুন...", - "refresh": "রিফ্রেশ", - "download": "ডাউনলোড করুন", - "clear": "পরিষ্কার", - "logsDownloaded": "লগগুলি সফলভাবে ডাউনলোড করা হয়েছে।", - "last50": "শেষ ৫০", - "last100": "শেষ ১০০", - "last500": "শেষ ৫০০", - "last1000": "শেষ ১০০০", - "allLogs": "সমস্ত লগ", - "noLogsMatching": "\"{{query}} \" এর সাথে মেলে এমন কোনও লগ নেই", - "noLogsAvailable": "কোন লগ উপলব্ধ নেই", - "noContainersFound": "কোন কন্টেইনার পাওয়া যায়নি", - "noContainersFoundHint": "এই হোস্টে কোনো ডকার কন্টেইনার উপলব্ধ নেই।", - "searchPlaceholder": "কন্টেইনারগুলি অনুসন্ধান করুন...", - "allStatuses": "সমস্ত স্ট্যাটাস", - "stateRunning": "দৌড়ানো", - "statePaused": "বিরতি দেওয়া হয়েছে", - "stateExited": "উত্তেজিত", - "stateRestarting": "পুনরায় চালু করা", - "noContainersMatchFilters": "আপনার ফিল্টারগুলির সাথে কোনও কন্টেইনার মেলে না", - "noContainersMatchFiltersHint": "আপনার অনুসন্ধান বা ফিল্টারের শর্তাবলী পরিবর্তন করে দেখুন।", - "failedToFetchStats": "কন্টেইনার পরিসংখ্যান আনতে ব্যর্থ হয়েছে", - "containerNotRunning": "কন্টেইনারটি চলছে না", - "startContainerToViewStats": "পরিসংখ্যান দেখতে কন্টেইনারটি চালু করুন।", - "loadingStats": "পরিসংখ্যান লোড হচ্ছে...", - "errorLoadingStats": "পরিসংখ্যান লোড করতে ত্রুটি", - "noStatsAvailable": "কোন পরিসংখ্যান উপলব্ধ নেই", - "cpuUsage": "সিপিইউ ব্যবহার", - "current": "বর্তমান", - "memoryUsage": "মেমরি ব্যবহার", - "networkIo": "নেটওয়ার্ক আই/ও", - "input": "ইনপুট", - "output": "আউটপুট", - "blockIo": "ব্লক আই/ও", - "read": "পড়ুন", - "write": "লিখুন", - "pids": "পিআইডি", - "containerInformation": "কন্টেইনারের তথ্য", - "name": "নাম", - "id": "আইডি", - "state": "রাজ্য", - "containerMustBeRunning": "কনসোল অ্যাক্সেস করতে হলে কন্টেইনারটি অবশ্যই চালু থাকতে হবে।", - "verificationCodePrompt": "যাচাইকরণ কোড প্রবেশ করান", - "totpVerificationFailed": "TOTP যাচাইকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।", - "warpgateVerificationFailed": "ওয়ার্পগেট প্রমাণীকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।", - "connectedTo": "{{containerName}} এর সাথে সংযুক্ত", - "disconnected": "বিচ্ছিন্ন", - "consoleError": "কনসোল ত্রুটি", - "errorMessage": "ত্রুটি: {{message}}", - "failedToConnect": "কন্টেইনারের সাথে সংযোগ করতে ব্যর্থ হয়েছে", - "console": "কনসোল", - "selectShell": "শেল নির্বাচন করুন", - "bash": "ব্যাশ", - "sh": "শ", - "ash": "ছাই", - "connect": "সংযোগ করুন", - "disconnect": "সংযোগ বিচ্ছিন্ন করুন", - "notConnected": "সংযুক্ত নয়", - "clickToConnect": "শেল সেশন শুরু করতে কানেক্ট-এ ক্লিক করুন।", - "connectingTo": "{{containerName}} এর সাথে সংযোগ স্থাপন করা হচ্ছে...", - "containerNotFound": "কন্টেইনারটি খুঁজে পাওয়া যায়নি", - "backToList": "তালিকায় ফিরে যান", - "logs": "লগ", - "stats": "পরিসংখ্যান", - "consoleTab": "কনসোল", - "startContainerToAccess": "কনসোল অ্যাক্সেস করতে কন্টেইনারটি চালু করুন।" + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "সাধারণ", - "sectionOidc": "ওআইডিসি", - "sectionUsers": "ব্যবহারকারীরা", - "sectionSessions": "অধিবেশন", - "sectionRoles": "ভূমিকা", - "sectionDatabase": "ডাটাবেস", - "sectionApiKeys": "এপিআই কী", - "allowRegistration": "ব্যবহারকারী নিবন্ধনের অনুমতি দিন", - "allowRegistrationDesc": "নতুন ব্যবহারকারীদের স্ব-নিবন্ধন করতে দিন", - "allowPasswordLogin": "পাসওয়ার্ড লগইনের অনুমতি দিন", - "allowPasswordLoginDesc": "ব্যবহারকারীর নাম/পাসওয়ার্ড লগইন", - "oidcAutoProvision": "OIDC অটো-প্রভিশন", - "oidcAutoProvisionDesc": "নিবন্ধন নিষ্ক্রিয় থাকলেও OIDC ব্যবহারকারীদের জন্য স্বয়ংক্রিয়ভাবে অ্যাকাউন্ট তৈরি করুন।", - "allowPasswordReset": "পাসওয়ার্ড রিসেট করার অনুমতি দিন", - "allowPasswordResetDesc": "ডকার লগের মাধ্যমে কোড রিসেট করুন", - "sessionTimeout": "সেশন টাইমআউট", - "hours": "ঘন্টা", - "sessionTimeoutRange": "সর্বনিম্ন ১ ঘণ্টা · সর্বোচ্চ ৭২০ ঘণ্টা", - "monitoringDefaults": "ডিফল্ট নিরীক্ষণ", - "statusCheck": "অবস্থা যাচাই", - "metrics": "মেট্রিক্স", - "sec": "সেকেন্ড", - "logLevel": "লগ স্তর", - "enableGuacamole": "গুয়াকামোল সক্রিয় করুন", - "enableGuacamoleDesc": "RDP/VNC রিমোট ডেস্কটপ", + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "SSO-এর জন্য OpenID Connect কনফিগার করুন। * চিহ্নিত ফিল্ডগুলো পূরণ করা আবশ্যক।", - "oidcClientId": "ক্লায়েন্ট আইডি", - "oidcClientSecret": "ক্লায়েন্ট গোপনীয়", - "oidcAuthUrl": "অনুমোদন ইউআরএল", - "oidcIssuerUrl": "ইস্যুকারী ইউআরএল", - "oidcTokenUrl": "টোকেন ইউআরএল", - "oidcUserIdentifier": "ব্যবহারকারী শনাক্তকারী পথ", - "oidcDisplayName": "ডিসপ্লে নেম পাথ", - "oidcScopes": "স্কোপ", - "oidcUserinfoUrl": "ব্যবহারকারীর তথ্য ইউআরএল ওভাররাইড করুন", - "oidcAllowedUsers": "অনুমোদিত ব্যবহারকারী", - "oidcAllowedUsersDesc": "প্রতি লাইনে একটি ইমেল। সবগুলো দেখানোর জন্য খালি রাখুন।", - "removeOidc": "অপসারণ করুন", - "usersCount": "{{count}} ব্যবহারকারীরা", - "createUser": "তৈরি করুন", - "newRole": "নতুন ভূমিকা", - "roleName": "নাম", - "roleDisplayName": "প্রদর্শনের নাম", - "roleDescription": "বর্ণনা", - "rolesCount": "{{count}} ভূমিকা", - "createRole": "তৈরি করুন", - "creating": "তৈরি করা হচ্ছে...", - "exportDatabase": "ডেটাবেস রপ্তানি করুন", - "exportDatabaseDesc": "সমস্ত হোস্ট, ক্রেডেনশিয়াল এবং সেটিংসের একটি ব্যাকআপ ডাউনলোড করুন।", - "export": "রপ্তানি", - "exporting": "রপ্তানি করা হচ্ছে...", - "importDatabase": "ডেটাবেস আমদানি করুন", - "importDatabaseDesc": "একটি .sqlite ব্যাকআপ ফাইল থেকে পুনরুদ্ধার করুন", - "importDatabaseSelected": "নির্বাচিত: {{name}}", - "selectFile": "ফাইল নির্বাচন করুন", - "changeFile": "পরিবর্তন", - "import": "আমদানি", - "importing": "আমদানি করা হচ্ছে...", - "apiKeysCount": "{{count}} কী", - "newApiKey": "নতুন এপিআই কী", - "apiKeyCreatedWarning": "কী তৈরি হয়েছে - এখনই এটি কপি করুন, এটি আর দেখানো হবে না।", - "apiKeyName": "নাম", - "apiKeyUser": "ব্যবহারকারী", - "apiKeySelectUser": "একজন ব্যবহারকারী নির্বাচন করুন...", - "apiKeyExpiresAt": "মেয়াদ শেষ হবে", - "createKey": "চাবি তৈরি করুন", - "apiKeyNoExpiry": "কোনো মেয়াদ নেই", - "revokedBadge": "বাতিল করা হয়েছে", - "authTypeDual": "দ্বৈত প্রমাণীকরণ", - "authTypeOidc": "ওআইডিসি", - "authTypeLocal": "স্থানীয়", - "adminStatusAdministrator": "প্রশাসক", - "adminStatusRegularUser": "নিয়মিত ব্যবহারকারী", - "adminBadge": "অ্যাডমিন", - "systemBadge": "সিস্টেম", - "customBadge": "কাস্টম", - "youBadge": "তুমি", - "sessionsActive": "{{count}} সক্রিয়", - "sessionActive": "সক্রিয়: {{time}}", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "সব", - "revokeAllSessionsSuccess": "ব্যবহারকারীর সমস্ত সেশন বাতিল করা হয়েছে", - "revokeAllSessionsFailed": "সেশন বাতিল করতে ব্যর্থ হয়েছে", - "revokeSessionFailed": "সেশন বাতিল করতে ব্যর্থ হয়েছে", - "addRole": "ভূমিকা যোগ করুন", - "noCustomRoles": "কোনো কাস্টম ভূমিকা সংজ্ঞায়িত করা হয়নি", - "removeRoleFailed": "ভূমিকা অপসারণ করতে ব্যর্থ হয়েছে", - "assignRoleFailed": "ভূমিকা নির্ধারণ করতে ব্যর্থ হয়েছে", - "deleteRoleFailed": "ভূমিকা মুছে ফেলতে ব্যর্থ হয়েছে", - "userAdminAccess": "প্রশাসক", - "userAdminAccessDesc": "সমস্ত অ্যাডমিন সেটিংসে সম্পূর্ণ অ্যাক্সেস", - "userRoles": "ভূমিকা", - "revokeAllUserSessions": "সমস্ত সেশন বাতিল করুন", - "revokeAllUserSessionsDesc": "সমস্ত ডিভাইসে পুনরায় লগইন করতে বাধ্য করুন", - "revoke": "বাতিল করুন", - "deleteUserWarning": "এই ব্যবহারকারীকে মুছে ফেলা স্থায়ী।", - "deleteUser": "মুছে ফেলুন {{username}}", - "deleting": "মুছে ফেলা হচ্ছে...", - "deleteUserFailed": "ব্যবহারকারীকে মুছে ফেলতে ব্যর্থ হয়েছে", - "deleteUserSuccess": "ব্যবহারকারী \"{{username}}\" মুছে ফেলা হয়েছে", - "deleteRoleSuccess": "ভূমিকা \"{{name}}\" মুছে ফেলা হয়েছে", - "revokeKeySuccess": "কী \"{{name}}\" বাতিল করা হয়েছে", - "revokeKeyFailed": "কী প্রত্যাহার করতে ব্যর্থ হয়েছে", - "copiedToClipboard": "ক্লিপবোর্ডে কপি করা হয়েছে", - "done": "সম্পন্ন হয়েছে", - "createUserTitle": "ব্যবহারকারী তৈরি করুন", - "createUserDesc": "একটি নতুন স্থানীয় অ্যাকাউন্ট তৈরি করুন।", - "createUserUsername": "ব্যবহারকারীর নাম", - "createUserPassword": "পাসওয়ার্ড", - "createUserPasswordHint": "সর্বনিম্ন ৬টি অক্ষর।", - "createUserEnterUsername": "ব্যবহারকারীর নাম লিখুন", - "createUserEnterPassword": "পাসওয়ার্ড লিখুন", - "createUserSubmit": "ব্যবহারকারী তৈরি করুন", - "editUserTitle": "ব্যবহারকারী পরিচালনা করুন: {{username}}", - "editUserDesc": "ভূমিকা, অ্যাডমিন স্ট্যাটাস, সেশন এবং অ্যাকাউন্ট সেটিংস সম্পাদনা করুন।", - "editUserUsername": "ব্যবহারকারীর নাম", - "editUserAuthType": "প্রমাণীকরণ প্রকার", - "editUserAdminStatus": "প্রশাসক অবস্থা", - "editUserUserId": "ব্যবহারকারীর আইডি", - "linkAccountTitle": "OIDC-কে পাসওয়ার্ড অ্যাকাউন্টের সাথে লিঙ্ক করুন", - "linkAccountDesc": "OIDC অ্যাকাউন্ট {{username}} -কে একটি বিদ্যমান স্থানীয় অ্যাকাউন্টের সাথে একীভূত করুন।", - "linkAccountWarningTitle": "এর ফলে:", - "linkAccountEffect1": "শুধুমাত্র OIDC-এর জন্য নির্ধারিত অ্যাকাউন্টটি মুছে ফেলুন", - "linkAccountEffect2": "টার্গেট অ্যাকাউন্টে OIDC লগইন যোগ করুন", - "linkAccountEffect3": "OIDC এবং পাসওয়ার্ড উভয় লগইনের অনুমতি দিন", - "linkAccountTargetUsername": "লক্ষ্য ব্যবহারকারীর নাম", - "linkAccountTargetPlaceholder": "লিঙ্ক করার জন্য স্থানীয় অ্যাকাউন্টের ইউজারনেম লিখুন", - "linkAccounts": "অ্যাকাউন্ট লিঙ্ক করুন", - "linkAccountSuccess": "OIDC অ্যাকাউন্ট \"{{username}} \" এর সাথে সংযুক্ত", - "linkAccountFailed": "OIDC অ্যাকাউন্ট লিঙ্ক করতে ব্যর্থ হয়েছে", - "linkAccountInProgress": "সংযোগ স্থাপন...", - "saving": "সঞ্চয়...", - "updateRegistrationFailed": "নিবন্ধন সেটিং আপডেট করতে ব্যর্থ হয়েছে", - "updatePasswordLoginFailed": "পাসওয়ার্ড লগইন সেটিং আপডেট করতে ব্যর্থ হয়েছে", - "updateOidcAutoProvisionFailed": "OIDC অটো-প্রভিশন সেটিং আপডেট করতে ব্যর্থ হয়েছে", - "updatePasswordResetFailed": "পাসওয়ার্ড রিসেট সেটিং আপডেট করতে ব্যর্থ হয়েছে", - "sessionTimeoutRange2": "সেশন টাইমআউট অবশ্যই ১ থেকে ৭২০ ঘণ্টার মধ্যে হতে হবে।", - "sessionTimeoutSaved": "সেশন টাইমআউট সংরক্ষিত হয়েছে", - "sessionTimeoutSaveFailed": "সেশন সংরক্ষণ করতে ব্যর্থ হলে সময়সীমা শেষ হয়ে যায়।", - "monitoringIntervalInvalid": "অবৈধ ব্যবধান মান", - "monitoringSaved": "পর্যবেক্ষণ সেটিংস সংরক্ষিত হয়েছে", - "monitoringSaveFailed": "মনিটরিং সেটিংস সংরক্ষণ করতে ব্যর্থ হয়েছে", - "guacamoleSaved": "গুয়াকামোল সেটিংস সংরক্ষিত হয়েছে", - "guacamoleSaveFailed": "গুয়াকামোল সেটিংস সংরক্ষণ করা যায়নি", - "guacamoleUpdateFailed": "গুয়াকামোল সেটিং আপডেট করতে ব্যর্থ হয়েছে", - "logLevelUpdateFailed": "লগ স্তর আপডেট করতে ব্যর্থ হয়েছে", - "oidcSaved": "OIDC কনফিগারেশন সংরক্ষিত হয়েছে", - "oidcSaveFailed": "OIDC কনফিগারেশন সংরক্ষণ করতে ব্যর্থ হয়েছে", - "oidcRemoved": "OIDC কনফিগারেশন সরানো হয়েছে", - "oidcRemoveFailed": "OIDC কনফিগারেশন অপসারণ করতে ব্যর্থ হয়েছে", - "createUserRequired": "ব্যবহারকারীর নাম এবং পাসওয়ার্ড প্রয়োজন", - "createUserPasswordTooShort": "পাসওয়ার্ডটি কমপক্ষে ৬ অক্ষরের হতে হবে।", - "createUserSuccess": "ব্যবহারকারী \"{{username}}\" তৈরি করেছেন", - "createUserFailed": "ব্যবহারকারী তৈরি করতে ব্যর্থ হয়েছে", - "updateAdminStatusFailed": "অ্যাডমিন স্ট্যাটাস আপডেট করতে ব্যর্থ হয়েছে", - "allSessionsRevoked": "সকল অধিবেশন বাতিল করা হয়েছে", - "revokeSessionsFailed": "সেশন বাতিল করতে ব্যর্থ হয়েছে", - "createRoleRequired": "নাম এবং প্রদর্শিত নাম আবশ্যক", - "createRoleSuccess": "ভূমিকা \"{{name}}\" তৈরি করা হয়েছে", - "createRoleFailed": "ভূমিকা তৈরি করতে ব্যর্থ হয়েছে", - "apiKeyNameRequired": "কী-এর নাম আবশ্যক।", - "apiKeyUserRequired": "ব্যবহারকারী আইডি প্রয়োজন", - "apiKeyCreatedSuccess": "এপিআই কী \"{{name}}\" তৈরি করা হয়েছে", - "apiKeyCreateFailed": "এপিআই কী তৈরি করতে ব্যর্থ হয়েছে", - "exportSuccess": "ডাটাবেস সফলভাবে রপ্তানি করা হয়েছে", - "exportFailed": "ডাটাবেস রপ্তানি ব্যর্থ হয়েছে", - "importSelectFile": "অনুগ্রহ করে প্রথমে একটি ফাইল নির্বাচন করুন।", - "importCompleted": "আমদানি সম্পন্ন হয়েছে: {{total}} টি আইটেম আমদানি করা হয়েছে, {{skipped}} টি বাদ দেওয়া হয়েছে", - "importFailed": "আমদানি ব্যর্থ হয়েছে: {{error}}", - "importError": "ডাটাবেস আমদানি ব্যর্থ হয়েছে" + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "হোস্ট", - "hostPlaceholder": "192.168.1.1 অথবা example.com", - "portLabel": "বন্দর", - "portPlaceholder": "২২", - "usernameLabel": "ব্যবহারকারীর নাম", - "usernamePlaceholder": "ব্যবহারকারীর নাম", - "authLabel": "প্রমাণীকরণ", - "passwordLabel": "পাসওয়ার্ড", - "passwordPlaceholder": "পাসওয়ার্ড", - "privateKeyLabel": "ব্যক্তিগত চাবি", - "privateKeyPlaceholder": "প্রাইভেট কী পেস্ট করুন...", - "credentialLabel": "শংসাপত্র", - "credentialPlaceholder": "একটি সংরক্ষিত পরিচয়পত্র নির্বাচন করুন", - "connectToTerminal": "টার্মিনালের সাথে সংযোগ করুন", - "connectToFiles": "ফাইলগুলির সাথে সংযোগ করুন" + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", + "portPlaceholder": "22", + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "কোন টার্মিনাল নির্বাচন করা হয়নি", - "noTerminalSelectedHint": "এর কমান্ড হিস্ট্রি দেখতে একটি SSH টার্মিনাল ট্যাব খুলুন।", - "searchPlaceholder": "অনুসন্ধানের ইতিহাস...", - "clearAll": "সব পরিষ্কার করুন", - "noHistoryEntries": "ইতিহাসের কোন এন্ট্রি নেই", - "trackingDisabled": "ইতিহাস ট্র্যাকিং নিষ্ক্রিয় করা হয়েছে", - "trackingDisabledHint": "কমান্ড রেকর্ড করতে আপনার প্রোফাইল সেটিংসে এটি চালু করুন।" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "মূল রেকর্ডিং", - "recordToTerminals": "টার্মিনালে রেকর্ড", - "selectAll": "সব", - "selectNone": "কোনোটিই না", - "noTerminalTabsOpen": "কোন টার্মিনাল ট্যাব খোলা নেই", - "selectTerminalsAbove": "উপরের টার্মিনালগুলি নির্বাচন করুন", - "broadcastInputPlaceholder": "কীস্ট্রোক সম্প্রচার করতে এখানে টাইপ করুন...", - "stopRecording": "রেকর্ডিং বন্ধ করুন", - "startRecording": "রেকর্ডিং শুরু করুন", - "settingsTitle": "সেটিংস", - "enableRightClickCopyPaste": "ডান-ক্লিক কপি/পেস্ট সক্ষম করুন" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "লেআউট", - "selectLayoutAbove": "উপরে একটি লেআউট নির্বাচন করুন", - "selectLayoutHint": "কতগুলি প্যান প্রদর্শন করতে হবে তা বেছে নিন", - "panesTitle": "প্যানে", - "openTabsTitle": "ট্যাব খুলুন", - "dragTabsHint": "ট্যাবগুলিকে উপরের প্যানে টেনে আনুন, অথবা কুইক অ্যাসাইন ব্যবহার করুন।", - "dropHere": "এখানে নেমে যান", - "emptyPane": "খালি", - "dashboard": "ড্যাশবোর্ড", - "clearSplitScreen": "ক্লিয়ার স্প্লিট স্ক্রিন", - "quickAssign": "দ্রুত বরাদ্দ করুন", - "alreadyAssigned": "প্যান {{index}}", - "splitTab": "স্প্লিট ট্যাব", - "addToSplit": "স্প্লিটে যোগ করুন", - "removeFromSplit": "স্প্লিট থেকে সরান", - "assignToPane": "প্যানে বরাদ্দ করুন" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "স্নিপেট তৈরি করুন", - "createSnippetDescription": "দ্রুত কার্যকর করার জন্য একটি নতুন কমান্ড স্নিপেট তৈরি করুন", - "nameLabel": "নাম", - "namePlaceholder": "যেমন, Nginx রিস্টার্ট করুন", - "descriptionLabel": "বর্ণনা", - "descriptionPlaceholder": "ঐচ্ছিক বিবরণ", - "optional": "ঐচ্ছিক", - "folderLabel": "ফোল্ডার", - "noFolder": "কোন ফোল্ডার নেই (শ্রেণীবিহীন)", - "commandLabel": "আদেশ", - "commandPlaceholder": "যেমন, sudo systemctl restart nginx", - "cancel": "বাতিল করুন", - "createSnippetButton": "স্নিপেট তৈরি করুন", - "createFolderTitle": "ফোল্ডার তৈরি করুন", - "createFolderDescription": "আপনার স্নিপেটগুলো ফোল্ডারে সাজিয়ে নিন।", - "folderNameLabel": "ফোল্ডারের নাম", - "folderNamePlaceholder": "যেমন, সিস্টেম কমান্ড, ডকার স্ক্রিপ্ট", - "folderColorLabel": "ফোল্ডারের রঙ", - "folderIconLabel": "ফোল্ডার আইকন", - "previewLabel": "প্রিভিউ", - "folderNameFallback": "ফোল্ডারের নাম", - "createFolderButton": "ফোল্ডার তৈরি করুন", - "targetTerminals": "টার্গেট টার্মিনাল", - "selectAll": "সব", - "selectNone": "কোনোটিই না", - "noTerminalTabsOpen": "কোন টার্মিনাল ট্যাব খোলা নেই", - "searchPlaceholder": "অনুসন্ধানের অংশবিশেষ...", - "newSnippet": "নতুন স্নিপেট", - "newFolder": "নতুন ফোল্ডার", - "run": "দৌড়", - "noSnippetsInFolder": "এই ফোল্ডারে কোনো স্নিপেট নেই", - "uncategorized": "অশ্রেণীবদ্ধ", - "editSnippetTitle": "সম্পাদনা স্নিপেট", - "editSnippetDescription": "এই কমান্ড স্নিপেটটি আপডেট করুন", - "saveSnippetButton": "পরিবর্তনগুলি সংরক্ষণ করুন", - "createSuccess": "স্নিপেট সফলভাবে তৈরি করা হয়েছে", - "createFailed": "স্নিপেট তৈরি করতে ব্যর্থ হয়েছে", - "updateSuccess": "স্নিপেট সফলভাবে আপডেট করা হয়েছে", - "updateFailed": "স্নিপেট আপডেট করতে ব্যর্থ হয়েছে", - "deleteFailed": "স্নিপেট মুছে ফেলা ব্যর্থ হয়েছে", - "folderCreateSuccess": "ফোল্ডারটি সফলভাবে তৈরি করা হয়েছে।", - "folderCreateFailed": "ফোল্ডার তৈরি করতে ব্যর্থ হয়েছে", - "editFolderTitle": "ফোল্ডার সম্পাদনা করুন", - "editFolderDescription": "এই ফোল্ডারটির নাম পরিবর্তন করুন বা এর চেহারা বদলে দিন।", - "saveFolderButton": "পরিবর্তনগুলি সংরক্ষণ করুন", - "editFolder": "ফোল্ডার সম্পাদনা করুন", - "deleteFolder": "ফোল্ডারটি মুছে ফেলুন", - "folderDeleteSuccess": "ফোল্ডার \"{{name}}\" মুছে ফেলা হয়েছে", - "folderDeleteFailed": "ফোল্ডারটি মুছে ফেলা ব্যর্থ হয়েছে", - "folderEditSuccess": "ফোল্ডারটি সফলভাবে আপডেট করা হয়েছে", - "folderEditFailed": "ফোল্ডার আপডেট করতে ব্যর্থ হয়েছে", - "confirmRunMessage": "\"{{name}} \" চালান?", - "confirmRunButton": "দৌড়", - "runSuccess": "{{count}} টার্মিনালে \"{{name}}\" চালানো হয়েছে", - "copySuccess": "\"{{name}}\" ক্লিপবোর্ডে কপি করা হয়েছে", - "shareTitle": "স্নিপেট শেয়ার করুন", - "shareUser": "ব্যবহারকারী", - "shareRole": "ভূমিকা", - "selectUser": "একজন ব্যবহারকারী নির্বাচন করুন...", - "selectRole": "একটি ভূমিকা নির্বাচন করুন...", - "shareSuccess": "স্নিপেট সফলভাবে শেয়ার করা হয়েছে", - "shareFailed": "স্নিপেট শেয়ার করতে ব্যর্থ হয়েছে", - "revokeSuccess": "প্রবেশাধিকার বাতিল করা হয়েছে", - "revokeFailed": "অ্যাক্সেস প্রত্যাহার করতে ব্যর্থ হয়েছে", - "currentAccess": "বর্তমান অ্যাক্সেস", - "shareLoadError": "শেয়ার ডেটা লোড করতে ব্যর্থ হয়েছে", - "loading": "লোড হচ্ছে...", - "close": "বন্ধ করুন", - "reorderFailed": "স্নিপেটের ক্রম সংরক্ষণ করতে ব্যর্থ হয়েছে" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "অ্যাকাউন্ট", - "sectionAppearance": "চেহারা", - "sectionSecurity": "নিরাপত্তা", - "sectionApiKeys": "এপিআই কী", - "sectionC2sTunnels": "সি২এস টানেল", - "usernameLabel": "ব্যবহারকারীর নাম", - "roleLabel": "ভূমিকা", - "roleAdministrator": "প্রশাসক", - "authMethodLabel": "প্রমাণীকরণ পদ্ধতি", - "authMethodLocal": "স্থানীয়", - "twoFaLabel": "২এফএ", - "twoFaOn": "চালু", - "twoFaOff": "বন্ধ", - "versionLabel": "সংস্করণ", - "deleteAccount": "অ্যাকাউন্ট মুছে ফেলুন", - "deleteAccountDescription": "আপনার অ্যাকাউন্ট স্থায়ীভাবে মুছে ফেলুন", - "deleteButton": "মুছে ফেলুন", - "deleteAccountPermanent": "এই পদক্ষেপটি স্থায়ী এবং এটি পূর্বাবস্থায় ফেরানো যাবে না।", - "deleteAccountWarning": "সমস্ত সেশন, হোস্ট, ক্রেডেনশিয়াল এবং সেটিংস স্থায়ীভাবে মুছে ফেলা হবে।", - "confirmPasswordDeletePlaceholder": "নিশ্চিত করতে আপনার পাসওয়ার্ড লিখুন", - "languageLabel": "ভাষা", - "themeLabel": "থিম", - "fontSizeLabel": "ফন্ট সাইজ", - "accentColorLabel": "অ্যাকসেন্ট রঙ", - "settingsTerminal": "টার্মিনাল", - "commandAutocomplete": "কমান্ড অটোকমপ্লিট", - "commandAutocompleteDesc": "টাইপ করার সময় স্বয়ংক্রিয় সম্পূর্ণতা দেখান", - "historyTracking": "ইতিহাস ট্র্যাকিং", - "historyTrackingDesc": "টার্মিনাল কমান্ড ট্র্যাক করুন", - "syntaxHighlighting": "সিনট্যাক্স হাইলাইটিং", - "syntaxHighlightingDesc": "টার্মিনাল আউটপুট হাইলাইট করুন", - "commandPalette": "কমান্ড প্যালেট", - "commandPaletteDesc": "কিবোর্ড শর্টকাট সক্রিয় করুন", - "reopenTabsOnLogin": "লগইন করার সময় ট্যাবগুলি পুনরায় খুলুন", - "reopenTabsOnLoginDesc": "লগ ইন করলে বা পৃষ্ঠা রিফ্রেশ করলে, এমনকি অন্য ডিভাইস থেকেও আপনার খোলা ট্যাবগুলি পুনরুদ্ধার করুন।", - "confirmTabClose": "ট্যাব বন্ধ নিশ্চিত করুন", - "confirmTabCloseDesc": "টার্মিনাল ট্যাব বন্ধ করার আগে জিজ্ঞাসা করুন।", - "settingsSidebar": "সাইডবার", - "showHostTags": "হোস্ট ট্যাগগুলি দেখান", - "showHostTagsDesc": "হোস্ট তালিকায় ট্যাগগুলি প্রদর্শন করুন", - "hostTrayOnClick": "হোস্ট অ্যাকশনগুলি প্রসারিত করতে ক্লিক করুন", - "hostTrayOnClickDesc": "সর্বদা সংযোগ বাটনগুলো দেখান; ম্যানেজমেন্ট অপশনগুলো প্রসারিত করতে হোভার করার পরিবর্তে ক্লিক করুন।", - "pinAppRail": "পিন অ্যাপ রেল", - "pinAppRailDesc": "হোভার করার সময় প্রসারিত না হয়ে, বাম সাইডবারের অ্যাপ রেলটিকে সর্বদা প্রসারিত রাখুন।", - "settingsSnippets": "খণ্ডাংশ", - "foldersCollapsed": "ফোল্ডারগুলো ভেঙে গেছে", - "foldersCollapsedDesc": "ডিফল্টরূপে ফোল্ডারগুলি সংকুচিত করুন", - "confirmExecution": "কার্যকর করা নিশ্চিত করুন", - "confirmExecutionDesc": "স্নিপেট চালানোর আগে নিশ্চিত করুন।", - "settingsUpdates": "আপডেট", - "disableUpdateChecks": "আপডেট চেক নিষ্ক্রিয় করুন", - "disableUpdateChecksDesc": "আপডেট চেক করা বন্ধ করুন", - "totpAuthenticator": "TOTP প্রমাণীকরণকারী", - "totpEnabled": "2FA সক্রিয় করা হয়েছে", - "totpDisabled": "অতিরিক্ত লগইন নিরাপত্তা যোগ করুন", - "disable": "অক্ষম করুন", - "enable": "সক্ষম করুন", - "setupTotp": "TOTP সেটআপ করুন", - "qrCode": "কিউআর কোড", - "totpInstructions": "আপনার অথেন্টিকেটর অ্যাপে কিউআর কোড স্ক্যান করুন অথবা সিক্রেটটি প্রবেশ করান, তারপর ৬-সংখ্যার কোডটি লিখুন।", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "যাচাই করুন", - "changePassword": "পাসওয়ার্ড পরিবর্তন করুন", - "currentPasswordLabel": "বর্তমান পাসওয়ার্ড", - "currentPasswordPlaceholder": "বর্তমান পাসওয়ার্ড", - "newPasswordLabel": "নতুন পাসওয়ার্ড", - "newPasswordPlaceholder": "নতুন পাসওয়ার্ড", - "confirmPasswordLabel": "নতুন পাসওয়ার্ড নিশ্চিত করুন", - "confirmPasswordPlaceholder": "নতুন পাসওয়ার্ড নিশ্চিত করুন", - "updatePassword": "পাসওয়ার্ড আপডেট করুন", - "createApiKeyTitle": "এপিআই কী তৈরি করুন", - "createApiKeyDescription": "প্রোগ্রাম্যাটিক অ্যাক্সেসের জন্য একটি নতুন এপিআই কী তৈরি করুন।", - "apiKeyNameLabel": "নাম", - "apiKeyNamePlaceholder": "যেমন সিআই পাইপলাইন", - "expiryDateLabel": "মেয়াদ শেষ হওয়ার তারিখ", - "optional": "ঐচ্ছিক", - "cancel": "বাতিল করুন", - "createKey": "চাবি তৈরি করুন", - "apiKeyCount": "{{count}} কী", - "newKey": "নতুন চাবি", - "noApiKeys": "এখনো কোনো এপিআই কী নেই।", - "apiKeyActive": "সক্রিয়", - "apiKeyUsageHint": "আপনার চাবিটি অন্তর্ভুক্ত করুন", - "apiKeyUsageHintHeader": "হেডার।", - "apiKeyPermissionsHint": "কী-গুলো সৃষ্টিকারী ব্যবহারকারীর অনুমতি উত্তরাধিকারসূত্রে লাভ করে।", - "roleUser": "ব্যবহারকারী", - "authMethodDual": "দ্বৈত প্রমাণীকরণ", - "authMethodOidc": "ওআইডিসি", - "totpSetupFailed": "TOTP সেটআপ শুরু করতে ব্যর্থ হয়েছে", - "totpEnter6Digits": "একটি ৬-সংখ্যার কোড প্রবেশ করান", - "totpEnabledSuccess": "দ্বি-স্তর প্রমাণীকরণ সক্রিয় করা হয়েছে", - "totpInvalidCode": "কোডটি ভুল, অনুগ্রহ করে আবার চেষ্টা করুন।", - "totpDisableInputRequired": "আপনার TOTP কোড বা পাসওয়ার্ড লিখুন", - "totpDisabledSuccess": "দ্বি-স্তর প্রমাণীকরণ নিষ্ক্রিয় করা হয়েছে", - "totpDisableFailed": "2FA নিষ্ক্রিয় করতে ব্যর্থ হয়েছে", - "totpDisableTitle": "2FA নিষ্ক্রিয় করুন", - "totpDisablePlaceholder": "TOTP কোড বা পাসওয়ার্ড প্রবেশ করান", - "totpDisableConfirm": "2FA নিষ্ক্রিয় করুন", - "totpContinueVerify": "যাচাই চালিয়ে যান", - "totpVerifyTitle": "কোড যাচাই করুন", - "totpBackupTitle": "ব্যাকআপ কোড", - "totpDownloadBackup": "ব্যাকআপ কোড ডাউনলোড করুন", - "done": "সম্পন্ন হয়েছে", - "secretCopied": "গোপনীয় তথ্য ক্লিপবোর্ডে কপি করা হয়েছে", - "apiKeyNameRequired": "কী-এর নাম আবশ্যক।", - "apiKeyCreated": "এপিআই কী \"{{name}}\" তৈরি করা হয়েছে", - "apiKeyCreateFailed": "এপিআই কী তৈরি করতে ব্যর্থ হয়েছে", - "apiKeyUser": "ব্যবহারকারী", - "apiKeyExpires": "মেয়াদ শেষ", - "apiKeyRevoked": "এপিআই কী \"{{name}}\" বাতিল করা হয়েছে", - "apiKeyRevokeFailed": "এপিআই কী প্রত্যাহার করতে ব্যর্থ হয়েছে", - "passwordFieldsRequired": "বর্তমান এবং নতুন পাসওয়ার্ড প্রয়োজন।", - "passwordMismatch": "পাসওয়ার্ডগুলো মিলছে না।", - "passwordTooShort": "পাসওয়ার্ডটি কমপক্ষে ৬ অক্ষরের হতে হবে।", - "passwordUpdated": "পাসওয়ার্ড সফলভাবে আপডেট করা হয়েছে", - "passwordUpdateFailed": "পাসওয়ার্ড আপডেট করতে ব্যর্থ হয়েছে", - "deletePasswordRequired": "আপনার অ্যাকাউন্ট মুছে ফেলার জন্য পাসওয়ার্ড প্রয়োজন।", - "deleteFailed": "অ্যাকাউন্ট মুছে ফেলতে ব্যর্থ হয়েছে", - "deleting": "মুছে ফেলা হচ্ছে...", - "colorPickerTooltip": "রঙ বাছাইকারী খুলুন", - "themeSystem": "সিস্টেম", - "themeLight": "আলো", - "themeDark": "অন্ধকার", - "themeDracula": "ড্রাকুলা", - "themeCatppuccin": "ক্যাটপুচিন", - "themeNord": "নর্ড", - "themeSolarized": "সৌরশক্তি চালিত", - "themeTokyoNight": "টোকিও রাত", - "themeOneDark": "এক অন্ধকার", - "themeGruvbox": "গ্রুভবক্স" + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ca_ES.json b/src/ui/locales/translated/ca_ES.json index 82d6878c..9bc5af32 100644 --- a/src/ui/locales/translated/ca_ES.json +++ b/src/ui/locales/translated/ca_ES.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Carpetes", - "folder": "Carpeta", - "password": "Contrasenya", - "key": "Clau", - "sshPrivateKey": "Clau privada SSH", - "upload": "Pujada", - "keyPassword": "Contrasenya clau", - "sshKey": "Clau SSH", - "uploadPrivateKeyFile": "Puja el fitxer de clau privada", - "searchCredentials": "Cerca credencials...", - "addCredential": "Afegeix credencials", - "caCertificate": "Certificat CA (-cert.pub)", - "caCertificateDescription": "Opcional: Puja o enganxa el fitxer de certificat signat per l'autoritat de certificació (per exemple, id_ed25519-cert.pub). Obligatori quan el servidor SSH utilitza l'autorització basada en certificats.", - "uploadCertFile": "Puja el fitxer -cert.pub", - "clearCert": "Clar", - "certLoaded": "Certificat carregat", - "certPublicKeyLabel": "Certificat de CA", - "certTypeLabel": "Tipus de certificat", - "pasteOrUploadCert": "Enganxa o puja un certificat -cert.pub...", - "hasCaCert": "Té un certificat de CA", - "noCaCert": "Sense certificat de CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "No s'han pogut carregar les alertes", - "failedToDismissAlert": "No s'ha pogut ignorar l'alerta" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configuració del servidor", - "description": "Configureu l'URL del servidor Termix per connectar-vos als vostres serveis de backend", - "serverUrl": "URL del servidor", - "enterServerUrl": "Si us plau, introduïu una URL de servidor", - "saveFailed": "No s'ha pogut desar la configuració", - "saveError": "S'ha produït un error en desar la configuració", - "saving": "Desant...", - "saveConfig": "Desa la configuració", - "helpText": "Introduïu l'URL on s'executa el vostre servidor Termix (per exemple, http://localhost:30001 o https://el-vostre-servidor.com)", - "changeServer": "Canviar servidor", - "mustIncludeProtocol": "L'URL del servidor ha de començar per http:// o https://", - "allowInvalidCertificate": "Permetre certificat no vàlid", - "allowInvalidCertificateDesc": "Utilitzeu-ho només per a servidors autoallotjats de confiança amb certificats autosignats o d'adreça IP.", - "useEmbedded": "Utilitza el servidor local", - "embeddedDesc": "Executeu Termix amb el servidor local integrat (no cal cap servidor remot)", - "embeddedConnecting": "Connectant al servidor local...", - "embeddedNotReady": "El servidor local encara no està a punt. Espereu un moment i torneu-ho a intentar.", - "localServer": "Servidor local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Error de comprovació de versió", - "checkFailed": "No s'ha pogut comprovar si hi ha actualitzacions", - "upToDate": "L'aplicació està actualitzada", - "currentVersion": "Esteu executant la versió {{version}}", - "updateAvailable": "Actualització disponible", - "newVersionAvailable": "Hi ha una nova versió disponible! Esteu executant {{current}}, però {{latest}} està disponible.", - "betaVersion": "Versió beta", - "betaVersionDesc": "Esteu executant {{current}}, que és més recent que l'última versió estable {{latest}}.", - "releasedOn": "Publicat el {{date}}", - "downloadUpdate": "Descarrega l'actualització", - "checking": "S'estan buscant actualitzacions...", - "checkUpdates": "Comprova si hi ha actualitzacions", - "checkingUpdates": "S'estan buscant actualitzacions...", - "updateRequired": "Actualització necessària" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Tanca", - "minimize": "Minimitzar", - "online": "En línia", - "offline": "Fora de línia", - "continue": "Continua", - "maintenance": "Manteniment", - "degraded": "Degradat", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", "error": "Error", - "warning": "Avís", - "unsavedChanges": "Canvis no desats", - "dismiss": "Ignora", - "loading": "S'està carregant...", - "optional": "Opcional", - "connect": "Connecta", - "copied": "Copiat", - "connecting": "Connectant...", - "updateAvailable": "Actualització disponible", - "appName": "Tèrmix", - "openInNewTab": "Obre en una pestanya nova", - "noReleases": "Sense llançaments", - "updatesAndReleases": "Actualitzacions i llançaments", - "newVersionAvailable": "Hi ha disponible una nova versió ({{version}}).", - "failedToFetchUpdateInfo": "No s'ha pogut obtenir la informació d'actualització", - "preRelease": "Preestrena", - "noReleasesFound": "No s'han trobat llançaments.", - "cancel": "Cancel·la", - "username": "Nom d'usuari", - "login": "Inicia la sessió", - "register": "Registra't", - "password": "Contrasenya", - "confirmPassword": "Confirma la contrasenya", - "back": "Enrere", - "save": "Desa", - "saving": "Desant...", - "delete": "Suprimeix", - "rename": "Canvia el nom", - "edit": "Edita", - "add": "Afegeix", - "confirm": "Confirma", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", "no": "No", - "or": "O", - "next": "Següent", - "previous": "Anterior", - "refresh": "Actualitza", - "language": "Llengua", - "checking": "S'està comprovant...", - "checkingDatabase": "S'està comprovant la connexió a la base de dades...", - "checkingAuthentication": "S'està comprovant l'autenticació...", - "backendReconnected": "Connexió al servidor restaurada", - "connectionDegraded": "Connexió al servidor perduda, recuperació…", - "reload": "Torna a carregar", - "remove": "Elimina", - "create": "Crea", - "update": "Actualització", - "copy": "Còpia", - "copyFailed": "No s'ha pogut copiar al porta-retalls", - "maximize": "Maximitzar", - "restore": "Restaurar", - "of": "de" + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Inici", + "home": "Home", "terminal": "Terminal", "docker": "Docker", - "tunnels": "Túnels", - "fileManager": "Gestor de fitxers", - "serverStats": "Estadístiques del servidor", - "admin": "Administrador", - "userProfile": "Perfil d'usuari", - "splitScreen": "Pantalla dividida", - "confirmClose": "Voleu tancar aquesta sessió activa?", - "close": "Tanca", - "cancel": "Cancel·la", - "sshManager": "Gestor d'SSH", - "cannotSplitTab": "No es pot dividir aquesta pestanya", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copia la contrasenya", - "copySudoPassword": "Copia la contrasenya de Sudo", - "passwordCopied": "La contrasenya s'ha copiat al porta-retalls", - "noPasswordAvailable": "No hi ha cap contrasenya disponible", - "failedToCopyPassword": "No s'ha pogut copiar la contrasenya", - "refreshTab": "Actualitza la connexió", - "openFileManager": "Obre el gestor de fitxers", - "dashboard": "Tauler de control", - "networkGraph": "Gràfic de xarxa", - "quickConnect": "Connexió ràpida", - "sshTools": "Eines SSH", - "history": "Història", - "hosts": "Amfitrions", - "snippets": "Fragments", - "hostManager": "Gestor d'amfitrions", - "credentials": "Credencials", - "connections": "Connexions", - "roleAdministrator": "Administrador/a", - "roleUser": "Usuari" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Amfitrions", - "noHosts": "Sense amfitrions SSH", - "retry": "Torna-ho a intentar", - "refresh": "Actualitza", - "optional": "Opcional", - "downloadSample": "Descarrega la mostra", - "failedToDeleteHost": "No s'ha pogut suprimir {{name}}", - "importSkipExisting": "Importa (omet els existents)", - "connectionDetails": "Detalls de la connexió", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Escriptori remot", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Nom d'usuari", - "folder": "Carpeta", - "tags": "Etiquetes", - "pin": "Fixar", - "addHost": "Afegeix amfitrió", - "editHost": "Edita l'amfitrió", - "cloneHost": "Host clonat", - "enableTerminal": "Habilita el terminal", - "enableTunnel": "Habilita el túnel", - "enableFileManager": "Habilita el gestor de fitxers", - "enableDocker": "Activa Docker", - "defaultPath": "Camí predeterminat", - "connection": "Connexió", - "upload": "Pujada", - "authentication": "Autenticació", - "password": "Contrasenya", - "key": "Clau", - "credential": "Credencial", - "none": "Cap", - "sshPrivateKey": "Clau privada SSH", - "keyType": "Tipus de clau", - "uploadFile": "Puja el fitxer", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Túnels", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Fitxers", - "tabStats": "Estadístiques", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Compartir", - "tabAuthentication": "Autenticació", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Túnel", - "fileManager": "Gestor de fitxers", - "serverStats": "Estadístiques del servidor", - "status": "Estat", - "folderRenamed": "La carpeta \"{{oldName}}\" s'ha canviat de nom a \"{{newName}}\" correctament", - "failedToRenameFolder": "No s'ha pogut canviar el nom de la carpeta", - "movedToFolder": "Mogut a \"{{folder}}\"", - "editHostTooltip": "Edita l'amfitrió", - "statusChecks": "Comprovacions d'estat", - "metricsCollection": "Col·lecció de mètriques", - "metricsInterval": "Interval de recopilació de mètriques", - "metricsIntervalDesc": "Amb quina freqüència s'han de recopilar estadístiques del servidor (5 segons - 1 hora)", - "behavior": "Comportament", - "themePreview": "Vista prèvia del tema", - "theme": "Tema", - "fontFamily": "Família tipogràfica", - "fontSize": "Mida de la lletra", - "letterSpacing": "Espaiat entre lletres", - "lineHeight": "Alçada de la línia", - "cursorStyle": "Estil del cursor", - "cursorBlink": "Parpelleig del cursor", - "scrollbackBuffer": "Buffer de desplaçament enrere", - "bellStyle": "Estil de campana", - "rightClickSelectsWord": "Clic dret Selecciona Word", - "fastScrollModifier": "Modificador de desplaçament ràpid", - "fastScrollSensitivity": "Sensibilitat de desplaçament ràpid", - "sshAgentForwarding": "Reenviament d'agents SSH", - "backspaceMode": "Mode de retrocés", - "startupSnippet": "Fragment d'inici", - "selectSnippet": "Selecciona un fragment", - "forceKeyboardInteractive": "Forçar la interacció del teclat", - "overrideCredentialUsername": "Substitueix les credencials i el nom d'usuari", - "overrideCredentialUsernameDesc": "Feu servir el nom d'usuari especificat anteriorment en comptes del nom d'usuari de la credencial", - "jumpHostChain": "Cadena d'amfitrió de salt", - "portKnocking": "Cop de port", - "addKnock": "Afegeix un port", - "addProxyNode": "Afegeix un node", - "proxyNode": "Node intermediari", - "proxyType": "Tipus de proxy", - "quickActions": "Accions ràpides", - "sudoPasswordAutoFill": "Emplenament automàtic de contrasenya de Sudo", - "sudoPassword": "Contrasenya de Sudo", - "keepaliveInterval": "Interval de manteniment (ms)", - "moshCommand": "Comandament MOSH", - "environmentVariables": "Variables d'entorn", - "addVariable": "Afegeix una variable", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Copia l'URL del terminal", - "copyFileManagerUrl": "Copia l'URL del gestor de fitxers", - "copyRemoteDesktopUrl": "Copia l'URL de l'escriptori remot", - "failedToConnect": "No s'ha pogut connectar a la consola", - "connect": "Connecta", - "disconnect": "Desconnecta", - "start": "Inici", - "enableStatusCheck": "Activa la comprovació d'estat", - "enableMetrics": "Activa les mètriques", - "bulkUpdateFailed": "L'actualització massiva ha fallat", - "selectAll": "Selecciona-ho tot", - "deselectAll": "Desselecciona-ho tot", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Shell segur", - "virtualNetwork": "Xarxa virtual", - "unencryptedShell": "Shell sense xifrar", - "addressIp": "Adreça / IP", - "friendlyName": "Nom amigable", - "folderAndAdvanced": "Carpeta i avançat", - "privateNotes": "Notes privades", - "privateNotesPlaceholder": "Detalls sobre aquest servidor...", - "pinToTop": "Fixa a la part superior", - "pinToTopDesc": "Mostra sempre aquest amfitrió a la part superior de la llista", - "portKnockingSequence": "Seqüència de cops de port", - "addKnockBtn": "Afegeix Toc", - "noPortKnocking": "No s'ha configurat cap detonació de port.", - "knockPort": "Port de Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Retard després (ms)", - "useSocks5Proxy": "Utilitza el servidor intermediari SOCKS5", - "useSocks5ProxyDesc": "Enrutar la connexió a través d'un servidor proxy", - "proxyHost": "Amfitrió intermediari", - "proxyPort": "Port de proxy", - "proxyUsername": "Nom d'usuari del proxy", - "proxyPassword": "Contrasenya del servidor intermediari", - "proxySingleMode": "Proxy únic", - "proxyChainMode": "Cadena de proxy", - "you": "Tu", - "jumpHostChainLabel": "Cadena d'amfitrió de salt", - "addJumpBtn": "Afegeix un salt", - "noJumpHosts": "No hi ha cap amfitrió de salt configurat.", - "selectAServer": "Seleccioneu un servidor...", - "sshPort": "Port SSH", - "authMethod": "Mètode d'autenticació", - "storedCredential": "Credencial emmagatzemada", - "selectACredential": "Selecciona una credencial...", - "keyTypeLabel": "Tipus de clau", - "keyTypeAuto": "Detecció automàtica", - "keyPasteTab": "Enganxa", - "keyUploadTab": "Pujada", - "keyFileLoaded": "S'ha carregat el fitxer clau", - "keyUploadClick": "Feu clic per carregar .pem / .key / .ppk", - "clearKey": "Tecla Esborra", - "keySaved": "Clau SSH desada", - "keyReplaceNotice": "enganxa una nova clau a continuació per substituir-la", - "keyPassphraseSaved": "Contrasenya desada, escriu per canviar-la", - "replaceKey": "Substitueix la clau", - "forceKeyboardInteractiveLabel": "Força el teclat interactiu", - "forceKeyboardInteractiveShortDesc": "Força l'entrada manual de la contrasenya fins i tot si hi ha claus presents", - "terminalAppearance": "Aspecte del terminal", - "colorTheme": "Tema de color", - "fontFamilyLabel": "Família tipogràfica", - "fontSizeLabel": "Mida de la lletra", - "cursorStyleLabel": "Estil del cursor", - "letterSpacingPx": "Espaiat entre lletres (px)", - "lineHeightLabel": "Alçada de la línia", - "bellStyleLabel": "Estil de campana", - "backspaceModeLabel": "Mode de retrocés", - "cursorBlinking": "Cursor parpellejant", - "cursorBlinkingDesc": "Habilita l'animació intermitent per al cursor del terminal", - "rightClickSelectsWordLabel": "Feu clic amb el botó dret Selecciona Word", - "rightClickSelectsWordShortDesc": "Seleccioneu la paraula sota el cursor en fer clic amb el botó dret", - "behaviorAndAdvanced": "Comportament i avançat", - "scrollbackBufferLabel": "Buffer de desplaçament enrere", - "scrollbackMaxLines": "Nombre màxim de línies que es conserven a l'historial", - "sshAgentForwardingLabel": "Reenviament d'agents SSH", - "sshAgentForwardingShortDesc": "Passa les teves claus SSH locals a aquest amfitrió", - "enableAutoMosh": "Activa l'Auto-Mosh", - "enableAutoMoshDesc": "Prefereix Mosh a SSH si està disponible", - "enableAutoTmux": "Activa la transmissió automàtica", - "enableAutoTmuxDesc": "Inicia o connecta automàticament a la sessió de tmux", - "sudoPasswordAutoFillLabel": "Autoemplenament de contrasenya de Sudo", - "sudoPasswordAutoFillShortDesc": "Proporciona automàticament la contrasenya sudo quan se li demani", - "sudoPasswordLabel": "Contrasenya de Sudo", - "environmentVariablesLabel": "Variables d'entorn", - "addVariableBtn": "Afegeix una variable", - "noEnvVars": "No hi ha cap variable d'entorn configurada.", - "fastScrollModifierLabel": "Modificador de desplaçament ràpid", - "fastScrollSensitivityLabel": "Sensibilitat de desplaçament ràpid", - "moshCommandLabel": "Comandament Mosh", - "startupSnippetLabel": "Fragment d'inici", - "keepaliveIntervalLabel": "Interval de manteniment (segons)", - "maxKeepaliveMisses": "Màxims errors de manteniment en directe", - "tunnelSettings": "Configuració del túnel", - "enableTunneling": "Activa la tunelització", - "enableTunnelingDesc": "Habilita la funcionalitat del túnel SSH per a aquest amfitrió", - "serverTunnelsSection": "Túnels de servidor", - "addTunnelBtn": "Afegeix un túnel", - "noTunnelsConfigured": "No hi ha túnels configurats.", - "tunnelLabel": "Túnel {{number}}", - "tunnelType": "Tipus de túnel", - "tunnelModeLocalDesc": "Reenvia un port local a un port del servidor remot (o a un host accessible des d'aquest).", - "tunnelModeRemoteDesc": "Reenvia un port del servidor remot a un port local de la teva màquina.", - "tunnelModeDynamicDesc": "Crea un proxy SOCKS5 en un port local per al reenviament dinàmic de ports.", - "sameHost": "Aquest host (túnel directe)", - "endpointHost": "Amfitrió de punt final", - "endpointPort": "Port de punt final", - "bindHost": "Vincula l'amfitrió", - "sourcePort": "Port d'origen", - "maxRetries": "Màxim d'intents", - "retryIntervalS": "Interval de reintent (s)", - "autoStartLabel": "Inici automàtic", - "autoStartDesc": "Connecta automàticament aquest túnel quan es carregui l'amfitrió", - "tunnelConnecting": "Túnel que connecta...", - "tunnelDisconnected": "Túnel desconnectat", - "failedToConnectTunnel": "No s'ha pogut connectar", - "failedToDisconnectTunnel": "No s'ha pogut desconnectar", - "dockerIntegration": "Integració de Docker", - "enableDockerMonitor": "Activa Docker", - "enableDockerMonitorDesc": "Supervisar i gestionar els contenidors d'aquest amfitrió mitjançant Docker", - "enableFileManagerMonitor": "Habilita el gestor de fitxers", - "enableFileManagerMonitorDesc": "Navega i gestiona fitxers en aquest amfitrió mitjançant SFTP", - "defaultPathLabel": "Camí predeterminat", - "fileManagerPathHint": "El directori que s'obrirà quan s'iniciï el gestor de fitxers per a aquest amfitrió.", - "statusChecksLabel": "Comprovacions d'estat", - "enableStatusChecks": "Activa les comprovacions d'estat", - "enableStatusChecksDesc": "Fes ping periòdicament a aquest host per verificar la disponibilitat", - "useGlobalInterval": "Utilitza l'interval global", - "useGlobalIntervalDesc": "Substitueix amb l'interval de comprovació d'estat de tot el servidor", - "checkIntervalS": "Interval (s) de comprovació", - "checkIntervalDesc": "Segons entre cada ping de connectivitat", - "metricsCollectionLabel": "Col·lecció de mètriques", - "enableMetricsLabel": "Activa les mètriques", - "enableMetricsDesc": "Recopila l'ús de CPU, RAM, disc i xarxa d'aquest amfitrió", - "useGlobalMetrics": "Utilitza l'interval global", - "useGlobalMetricsDesc": "Substitueix amb l'interval de mètriques de tot el servidor", - "metricsIntervalS": "Interval de mètriques (s)", - "metricsIntervalDesc2": "Segons entre instantànies de mètriques", - "visibleWidgets": "Widgets visibles", - "cpuUsageLabel": "Ús de la CPU", - "cpuUsageDesc": "Percentatge de CPU, mitjanes de càrrega, gràfic miniàlgic", - "memoryLabel": "Ús de memòria", - "memoryDesc": "Ús de RAM, swap, memòria cau", - "storageLabel": "Ús del disc", - "storageDesc": "Ús del disc per punt de muntatge", - "networkLabel": "Interfícies de xarxa", - "networkDesc": "Llista d'interfícies i amplada de banda", - "uptimeLabel": "Temps de funcionament", - "uptimeDesc": "Temps de funcionament i temps d'arrencada del sistema", - "systemInfoLabel": "Informació del sistema", - "systemInfoDesc": "Sistema operatiu, nucli, nom d'amfitrió, arquitectura", - "recentLoginsLabel": "Inicis de sessió recents", - "recentLoginsDesc": "Esdeveniments d'inici de sessió correcte i fallit", - "topProcessesLabel": "Processos principals", - "topProcessesDesc": "PID, CPU%, MEM%, comanda", - "listeningPortsLabel": "Ports d'escolta", - "listeningPortsDesc": "Ports oberts amb procés i estat", - "firewallLabel": "Tallafocs", - "firewallDesc": "Estat del tallafocs, AppArmor i SELinux", - "quickActionsLabel": "Accions ràpides", - "quickActionsToolbar": "Les accions ràpides apareixen com a botons a la barra d'eines d'estadístiques del servidor per executar ordres amb un sol clic.", - "noQuickActions": "Encara no hi ha accions ràpides.", - "buttonLabel": "Etiqueta del botó", - "selectSnippetPlaceholder": "Selecciona un fragment...", - "addActionBtn": "Afegeix una acció", - "hostSharedSuccessfully": "L'amfitrió s'ha compartit correctament", - "failedToShareHost": "No s'ha pogut compartir l'amfitrió", - "accessRevoked": "Accés revocat", - "failedToRevokeAccess": "No s'ha pogut revocar l'accés", - "cancelBtn": "Cancel·la", - "savingBtn": "Desant...", - "addHostBtn": "Afegeix amfitrió", - "hostUpdated": "L'amfitrió s'ha actualitzat", - "hostCreated": "Amfitrió creat", - "failedToSave": "No s'ha pogut desar l'amfitrió", - "credentialUpdated": "Credencial actualitzada", - "credentialCreated": "Credencial creada", - "failedToSaveCredential": "No s'ha pogut desar la credencial", - "backToHosts": "Torna a Amfitrions", - "backToCredentials": "Torna a les credencials", - "pinned": "Fixat", - "noHostsFound": "No s'han trobat amfitrions", - "tryDifferentTerm": "Prova un terme diferent", - "addFirstHost": "Afegeix el teu primer amfitrió per començar", - "noCredentialsFound": "No s'han trobat credencials", - "addCredentialBtn": "Afegeix credencials", - "updateCredentialBtn": "Actualitza les credencials", - "features": "Característiques", - "noFolder": "(Sense carpeta)", - "deleteSelected": "Suprimeix", - "exitSelection": "Sortir de la selecció", - "importSkip": "Importa (omet els existents)", - "importOverwrite": "Importa (sobreescriu)", - "collapseBtn": "Replegar", - "importExportBtn": "Importació / Exportació", - "hostStatusesRefreshed": "Estats de l'amfitrió actualitzats", - "failedToRefreshHosts": "No s'han pogut actualitzar els amfitrions", - "movedHostTo": "S'ha mogut {{host}} a \"{{folder}}\"", - "failedToMoveHost": "No s'ha pogut moure l'amfitrió", - "folderRenamedTo": "La carpeta s'ha rebatejat com a \"{{name}}\"", - "deletedFolder": "Carpeta suprimida \"{{name}}\"", - "failedToDeleteFolder": "No s'ha pogut suprimir la carpeta", - "deleteAllInFolder": "Voleu suprimir tots els hosts de \"{{name}}\"? Això no es pot desfer.", - "deletedHost": "Suprimit {{name}}", - "copiedToClipboard": "Copiat al porta-retalls", - "terminalUrlCopied": "S'ha copiat l'URL del terminal", - "fileManagerUrlCopied": "S'ha copiat l'URL del gestor de fitxers", - "tunnelUrlCopied": "S'ha copiat l'URL del túnel", - "dockerUrlCopied": "S'ha copiat l'URL de Docker", - "serverStatsUrlCopied": "URL d'estadístiques del servidor copiada", - "rdpUrlCopied": "URL RDP copiada", - "vncUrlCopied": "URL de VNC copiada", - "telnetUrlCopied": "S'ha copiat l'URL de Telnet", - "remoteDesktopUrlCopied": "S'ha copiat l'URL de l'escriptori remot", - "expandActions": "Expandeix accions", - "collapseActions": "Accions de plegament", - "wakeOnLanAction": "Despertar-se en LAN", - "wakeOnLanSuccess": "Paquet màgic enviat a {{name}}", - "wakeOnLanError": "No s'ha pogut enviar el paquet màgic", - "cloneHostAction": "Host clonat", - "copyAddress": "Copiar adreça", - "copyLink": "Copia l'enllaç", - "copyTerminalUrlAction": "Copia l'URL del terminal", - "copyFileManagerUrlAction": "Copia l'URL del gestor de fitxers", - "copyTunnelUrlAction": "Copia l'URL del túnel", - "copyDockerUrlAction": "Copia l'URL de Docker", - "copyServerStatsUrlAction": "Copia l'URL de les estadístiques del servidor", - "copyRdpUrlAction": "Copia l'URL de l'RDP", - "copyVncUrlAction": "Copia l'URL de VNC", - "copyTelnetUrlAction": "Copia l'URL de Telnet", - "copyRemoteDesktopUrlAction": "Copia l'URL de l'escriptori remot", - "deleteCredentialConfirm": "Voleu suprimir la credencial \"{{name}}\"?", - "deletedCredential": "Suprimit {{name}}", - "deploySSHKeyTitle": "Implementa la clau SSH", - "deployingBtn": "S'està desplegant...", - "deployBtn": "Implementa", - "failedToDeployKey": "No s'ha pogut implementar la clau", - "deleteHostsConfirm": "Voleu suprimir l'amfitrió {{count}}{{plural}}? Això no es pot desfer.", - "movedToRoot": "Mogut a l'arrel", - "failedToMoveHosts": "No s'han pogut moure els amfitrions", - "enableTerminalFeature": "Habilita el terminal", - "disableTerminalFeature": "Desactiva el terminal", - "enableFilesFeature": "Habilita els fitxers", - "disableFilesFeature": "Desactiva els fitxers", - "enableTunnelsFeature": "Habilita els túnels", - "disableTunnelsFeature": "Desactiva els túnels", - "enableDockerFeature": "Activa Docker", - "disableDockerFeature": "Desactiva Docker", - "addTagsPlaceholder": "Afegeix etiquetes...", - "authDetails": "Detalls d'autenticació", - "credType": "Tipus", - "generateKeyPairDesc": "Genereu un nou parell de claus, tant les claus privades com les públiques s'ompliran automàticament.", - "generatingKey": "Generant...", - "generateLabel": "Genera {{label}}", - "uploadFileBtn": "Puja el fitxer", - "keyPassphraseOptional": "Contrasenya de clau (opcional)", - "sshPublicKeyOptional": "Clau pública SSH (opcional)", - "publicKeyGenerated": "Clau pública generada", - "failedToGeneratePublicKey": "No s'ha pogut obtenir la clau pública", - "publicKeyCopied": "Clau pública copiada", - "keyPairGenerated": "Parell de claus generat {{label}}", - "failedToGenerateKeyPair": "No s'ha pogut generar el parell de claus", - "searchHostsPlaceholder": "Cerca amfitrions, adreces, etiquetes…", - "searchCredentialsPlaceholder": "Cerca credencials…", - "refreshBtn": "Actualitza", - "addTag": "Afegeix etiquetes...", - "deleteConfirmBtn": "Suprimeix", - "tunnelRequirementsText": "El servidor SSH ha de tenir definits GatewayPorts sí, AllowTcpForwarding sí i PermitRootLogin sí a /etc/ssh/sshd_config.", - "deleteHostConfirm": "Voleu suprimir \"{{name}}\"?", - "enableAtLeastOneProtocol": "Activeu com a mínim un dels protocols anteriors per configurar l'autenticació i la connexió.", - "keyPassphrase": "Contrasenya de clau", - "connectBtn": "Connecta", - "disconnectBtn": "Desconnecta", - "basicInformation": "Informació bàsica", - "authDetailsSection": "Detalls d'autenticació", - "credTypeLabel": "Tipus", - "hostsTab": "Amfitrions", - "credentialsTab": "Credencials", - "selectMultiple": "Selecciona diversos", - "selectHosts": "Selecciona els amfitrions", - "connectionLabel": "Connexió", - "authenticationLabel": "Autenticació", - "generateKeyPairTitle": "Genera un parell de claus", - "generateKeyPairDescription": "Genereu un nou parell de claus, tant les claus privades com les públiques s'ompliran automàticament.", - "generateFromPrivateKey": "Generar a partir de la clau privada", - "refreshBtn2": "Actualitza", - "exitSelectionTitle": "Sortir de la selecció", - "exportAll": "Exporta-ho tot", - "addHostBtn2": "Afegeix amfitrió", - "addCredentialBtn2": "Afegeix credencials", - "checkingHostStatuses": "Comprovant els estats de l'amfitrió...", - "pinnedSection": "Fixat", - "hostsExported": "Els amfitrions s'han exportat correctament", - "exportFailed": "No s'han pogut exportar els amfitrions", - "sampleDownloaded": "Fitxer de mostra descarregat", - "failedToDeleteCredential2": "No s'ha pogut suprimir la credencial", - "noFolderOption": "(Sense carpeta)", - "nSelected": "{{count}} seleccionat", - "featuresMenu": "Característiques", - "moveMenu": "Moure", - "cancelSelection": "Cancel·la", - "deployDialogDesc": "Implementa {{name}} a les claus_autoritzades d'un host.", - "targetHostLabel": "Amfitrió de destinació", - "selectHostOption": "Seleccioneu un amfitrió...", - "keyDeployedSuccess": "La clau s'ha desplegat correctament", - "failedToDeployKey2": "No s'ha pogut implementar la clau", - "deletedCount": "S'han suprimit els amfitrions {{count}}", - "failedToDeleteCount": "No s'han pogut suprimir els amfitrions {{count}}", - "duplicatedHost": "Duplicat \"{{name}}\"", - "failedToDuplicateHost": "No s'ha pogut duplicar l'amfitrió", - "updatedCount": "Amfitrions {{count}} actualitzats", - "friendlyNameLabel": "Nom amigable", - "descriptionLabel": "Descripció", - "loadingHost": "S'està carregant l'amfitrió...", - "loadingHosts": "S'estan carregant els amfitrions...", - "loadingCredentials": "S'estan carregant les credencials...", - "noHostsYet": "Encara no hi ha amfitrions", - "noHostsMatchSearch": "No hi ha amfitrions que coincideixin amb la teva cerca", - "hostNotFound": "No s'ha trobat l'amfitrió", - "searchHosts": "Cerca amfitrions...", - "sortHosts": "Ordena els hosts", - "sortDefault": "Ordre per defecte", - "sortNameAsc": "Nom (A → Z)", - "sortNameDesc": "Nom (Z → A)", - "sortIpAsc": "Adreça IP (Asc)", - "sortIpDesc": "Adreça IP (Desc.)", - "sortOnlineFirst": "En línia primer", - "sortOfflineFirst": "Fora de línia primer", - "sortPinnedFirst": "Fixat primer", - "filterHosts": "Filtra els amfitrions", - "filterClearAll": "Esborra els filtres", - "filterStatusGroup": "Estat", - "filterOnline": "En línia", - "filterOffline": "Fora de línia", - "filterPinned": "Fixat", - "filterAuthGroup": "Tipus d'autorització", - "filterAuthPassword": "Contrasenya", - "filterAuthKey": "Clau SSH", - "filterAuthCredential": "Credencial", - "filterAuthNone": "Cap", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Característiques", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Gestor de fitxers", - "filterFeatureTunnel": "Túnel", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etiquetes", - "shareHost": "Comparteix l'amfitrió", - "shareHostTitle": "Comparteix: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Aquest amfitrió ha d'utilitzar una credencial per habilitar la compartició. Editeu l'amfitrió i assigneu-li primer una credencial." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Connexió", - "authentication": "Autenticació", - "connectionSettings": "Configuració de connexió", - "displaySettings": "Configuració de la pantalla", - "audioSettings": "Configuració d'àudio", - "rdpPerformance": "Rendiment de l'RDP", - "deviceRedirection": "Redirecció de dispositius", - "session": "Sessió", - "gateway": "Porta d'entrada", - "remoteApp": "Aplicació remota", - "clipboard": "Porta-retalls", - "sessionRecording": "Gravació de sessions", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Configuració de VNC", - "terminalSettings": "Configuració del terminal", - "rdpPort": "Port RDP", - "username": "Nom d'usuari", - "password": "Contrasenya", - "domain": "Domini", - "securityMode": "Mode de seguretat", - "colorDepth": "Profunditat de color", - "width": "Amplada", - "height": "Alçada", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Mètode de redimensionament", - "clientName": "Nom del client", - "initialProgram": "Programa inicial", - "serverLayout": "Disseny del servidor", - "timezone": "Zona horària", - "gatewayHostname": "Nom de l'amfitrió de la passarel·la", - "gatewayPort": "Port de passarel·la", - "gatewayUsername": "Nom d'usuari de la passarel·la", - "gatewayPassword": "Contrasenya de la passarel·la", - "gatewayDomain": "Domini de passarel·la", - "remoteAppProgram": "Programa RemoteApp", - "workingDirectory": "Directori de treball", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", "arguments": "Arguments", - "normalizeLineEndings": "Normalitzar els finals de línia", - "recordingPath": "Ruta d'enregistrament", - "recordingName": "Nom de l'enregistrament", - "macAddress": "Adreça MAC", - "broadcastAddress": "Adreça de difusió", - "udpPort": "Port UDP", - "waitTimeS": "Temps d'espera (s)", - "driveName": "Nom de la unitat", - "drivePath": "Camí de conducció", - "ignoreCertificate": "Ignora el certificat", - "ignoreCertificateDesc": "Permet connexions a amfitrions amb certificats autosignats", - "forceLossless": "Força sense pèrdues", - "forceLosslessDesc": "Força la codificació d'imatges sense pèrdues (més qualitat, més amplada de banda)", - "disableAudio": "Desactiva l'àudio", - "disableAudioDesc": "Silencia tot l'àudio de la sessió remota", - "enableAudioInput": "Activa l'entrada d'àudio (micròfon)", - "enableAudioInputDesc": "Reenvia el micròfon local a la sessió remota", - "wallpaper": "Fons de pantalla", - "wallpaperDesc": "Mostra el fons de pantalla de l'escriptori (desactivar-lo millora el rendiment)", - "theming": "Tematització", - "themingDesc": "Habilita els temes i estils visuals", - "fontSmoothing": "Suavitzat de fonts", - "fontSmoothingDesc": "Activa la renderització de fonts ClearType", - "fullWindowDrag": "Arrossegar per tota la finestra", - "fullWindowDragDesc": "Mostra el contingut de la finestra mentre arrossegues", - "desktopComposition": "Composició d'escriptori", - "desktopCompositionDesc": "Activa els efectes Aero Glass", - "menuAnimations": "Animacions de menú", - "menuAnimationsDesc": "Activa les animacions de diapositives i esvaïment del menú", - "disableBitmapCaching": "Desactiva la memòria cau de mapes de bits", - "disableBitmapCachingDesc": "Desactiva la memòria cau de mapes de bits (pot ajudar amb errors)", - "disableOffscreenCaching": "Desactiva la memòria cau fora de pantalla", - "disableOffscreenCachingDesc": "Desactiva la memòria cau fora de pantalla", - "disableGlyphCaching": "Desactiva la memòria cau de glifs", - "disableGlyphCachingDesc": "Desactiva la memòria cau de glifos", - "enableGfx": "Activa els efectes especials", - "enableGfxDesc": "Utilitza el canal de gràfics RemoteFX", - "enablePrinting": "Habilita la impressió", - "enablePrintingDesc": "Redirigir les impressores locals a la sessió remota", - "enableDriveRedirection": "Activa la redirecció de la unitat", - "enableDriveRedirectionDesc": "Assignar una carpeta local com a unitat a la sessió remota", - "createDrivePath": "Crea una ruta de conducció", - "createDrivePathDesc": "Crea automàticament la carpeta si no existeix", - "disableDownload": "Desactiva la descàrrega", - "disableDownloadDesc": "Impedir la descàrrega de fitxers des de la sessió remota", - "disableUpload": "Desactiva la càrrega", - "disableUploadDesc": "Impedir la càrrega de fitxers a la sessió remota", - "enableTouch": "Activa el tacte", - "enableTouchDesc": "Activa el reenviament d'entrada tàctil", - "consoleSession": "Sessió de consola", - "consoleSessionDesc": "Connecta't a la consola (sessió 0) en comptes d'una nova sessió", - "sendWolPacket": "Enviar paquet WOL", - "sendWolPacketDesc": "Envia un paquet màgic per despertar aquest host abans de connectar-se", - "disableCopy": "Desactiva la còpia", - "disableCopyDesc": "Impedir la còpia de text des de la sessió remota", - "disablePaste": "Desactiva l'enganxament", - "disablePasteDesc": "Impedir enganxar text a la sessió remota", - "createPathIfMissing": "Crea un camí si falta", - "createPathIfMissingDesc": "Crea automàticament el directori d'enregistrament", - "excludeOutput": "Exclou la sortida", - "excludeOutputDesc": "No enregistrar la sortida de pantalla (només metadades)", - "excludeMouse": "Exclou el ratolí", - "excludeMouseDesc": "No enregistreu els moviments del ratolí", - "includeKeystrokes": "Inclou les pulsacions de tecles", - "includeKeystrokesDesc": "Enregistra les pulsacions de tecles en brut a més de la sortida de la pantalla", - "vncPort": "Port VNC", - "vncPassword": "Contrasenya de VNC", - "vncUsernameOptional": "Nom d'usuari (opcional)", - "vncLeaveBlank": "Deixeu-ho en blanc si no és necessari", - "cursorMode": "Mode del cursor", - "swapRedBlue": "Intercanvia vermell/blau", - "swapRedBlueDesc": "Intercanvia els canals de color vermell i blau (corregeix alguns problemes de color)", - "readOnly": "Només lectura", - "readOnlyDesc": "Visualitza la pantalla remota sense enviar cap entrada", - "telnetPort": "Port Telnet", - "terminalType": "Tipus de terminal", - "fontName": "Nom de la font", - "fontSize": "Mida de la lletra", - "colorScheme": "Esquema de colors", - "backspaceKey": "Tecla Retrocés", - "saveHostFirst": "Primer desa l'amfitrió.", - "sharingOptionsAfterSave": "Les opcions de compartició estan disponibles després que s'hagi desat l'amfitrió.", - "sharingLoadError": "No s'han pogut carregar les dades de compartició. Comprova la connexió i torna-ho a provar.", - "shareHostSection": "Comparteix l'amfitrió", - "shareWithUser": "Comparteix amb l'usuari", - "shareWithRole": "Comparteix amb el rol", - "selectUser": "Selecciona l'usuari", - "selectRole": "Selecciona un rol", - "selectUserOption": "Selecciona un usuari...", - "selectRoleOption": "Selecciona un rol...", - "permissionLevel": "Nivell de permís", - "expiresInHours": "Caduca en (hores)", - "noExpiryPlaceholder": "Deixeu-ho en blanc si no hi ha caducitat", - "shareBtn": "Comparteix", - "currentAccess": "Accés actual", - "typeHeader": "Tipus", - "targetHeader": "Objectiu", - "permissionHeader": "Permís", - "grantedByHeader": "Atorgat per", - "expiresHeader": "Caduca", - "noAccessEntries": "Encara no hi ha entrades d'accés.", - "expiredLabel": "Caducat", - "neverLabel": "Mai", - "revokeBtn": "Revocar", - "cancelBtn": "Cancel·la", - "savingBtn": "Desant...", - "updateHostBtn": "Actualitza l'amfitrió", - "addHostBtn": "Afegeix amfitrió" + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Cerca hosts, ordres o configuracions...", - "quickActions": "Accions ràpides", - "hostManager": "Gestor d'amfitrions", - "hostManagerDesc": "Gestionar, afegir o editar amfitrions", - "addNewHost": "Afegeix un nou amfitrió", - "addNewHostDesc": "Registra un nou amfitrió", - "adminSettings": "Configuració de l'administrador", - "adminSettingsDesc": "Configura les preferències del sistema i els usuaris", - "userProfile": "Perfil d'usuari", - "userProfileDesc": "Gestiona el teu compte i les teves preferències", - "addCredential": "Afegeix credencials", - "addCredentialDesc": "Emmagatzemar claus o contrasenyes SSH", - "recentActivity": "Activitat recent", - "serversAndHosts": "Servidors i amfitrions", - "noHostsFound": "No s'han trobat hosts que coincideixin amb \"{{search}}\"", - "links": "Enllaços", - "navigate": "Navega", - "select": "Selecciona", - "toggleWith": "Commuta amb" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Panell {{index}} - buit", - "noTabAssigned": "Cap pestanya assignada", - "focusedPane": "Panell actiu" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Sense connexions", - "noConnectionsDesc": "Obriu un terminal, un gestor de fitxers o un escriptori remot per veure les connexions aquí", - "connectedFor": "Connectat per {{duration}}", - "connected": "Connectat", - "disconnected": "Desconnectat", - "closeTab": "Tanca la pestanya", - "closeConnection": "Connexió propera", - "forgetTab": "Oblidar", - "removeBackground": "Elimina", - "reconnect": "Reconnecta", - "reopenTab": "Reobrir", - "sectionOpen": "Obre", - "sectionBackground": "Fons", - "backgroundDesc": "Les sessions persisteixen durant 30 minuts després de la desconnexió i es poden tornar a connectar.", - "persisted": "Persistia en segon pla", - "expiresIn": "Caduca en {{duration}}", - "search": "Cerca connexions...", - "noSearchResults": "No hi ha connexions que coincideixin amb la teva cerca" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Connectant a la sessió {{type}}...", - "connectionError": "Error de connexió", - "connectionFailed": "La connexió ha fallat", - "failedToConnect": "No s'ha pogut obtenir el testimoni de connexió", - "hostNotFound": "No s'ha trobat l'amfitrió", - "noHostSelected": "No s'ha seleccionat cap amfitrió", - "reconnect": "Reconnecta", - "retry": "Torna-ho a intentar", - "guacdUnavailable": "El servei d'escriptori remot (guacd) no està disponible. Assegureu-vos que guacd estigui en execució, sigui accessible i estigui configurat correctament a la configuració d'administrador.", - "ctrlAltDel": "Ctrl+Alt+Supr", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl+Alt+Supr", - "winL": "Win+L (Pantalla de bloqueig)", - "winKey": "Clau de Windows", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Majúscules", - "win": "Guanyar", - "stickyActive": "{{key}} (bloquejat - feu clic per alliberar)", - "stickyInactive": "{{key}} (feu clic per bloquejar)", - "esc": "Escapada", - "tab": "Pestanya", - "home": "Inici", - "end": "Final", - "pageUp": "Pàgina amunt", - "pageDown": "Pàgina avall", - "arrowUp": "Fletxa amunt", - "arrowDown": "Fletxa avall", - "arrowLeft": "Fletxa esquerra", - "arrowRight": "Fletxa dreta", - "fnToggle": "Tecles de funció", - "reconnect": "Sessió de reconnexió", - "collapse": "Replega la barra d'eines", - "expand": "Expandeix la barra d'eines", - "dragHandle": "Arrossega per reposicionar" + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Connecta't a l'amfitrió", - "clear": "Clar", - "paste": "Enganxa", - "reconnect": "Reconnecta", - "connectionLost": "Connexió perduda", - "connected": "Connectat", - "clipboardWriteFailed": "No s'ha pogut copiar al porta-retalls. Assegureu-vos que la pàgina es serveix per HTTPS o localhost.", - "clipboardReadFailed": "No s'ha pogut llegir del porta-retalls. Assegureu-vos que els permisos del porta-retalls estiguin concedits.", - "clipboardHttpWarning": "Per enganxar cal HTTPS. Feu servir Ctrl+Maj+V o publiqueu Termix sobre HTTPS.", - "unknownError": "S'ha produït un error desconegut", - "websocketError": "Error de connexió de WebSocket", - "connecting": "Connectant...", - "noHostSelected": "No s'ha seleccionat cap amfitrió", - "reconnecting": "Reconnectant... ({{attempt}}/{{max}})", - "reconnected": "S'ha reconnectat correctament", - "tmuxSessionCreated": "Sessió tmux creada: {{name}}", - "tmuxSessionAttached": "sessió tmux adjunta: {{name}}", - "tmuxUnavailable": "tmux no està instal·lat a l'amfitrió remot, tornant a l'intèrpret d'ordres estàndard", - "tmuxSessionPickerTitle": "Sessions de tmux", - "tmuxSessionPickerDesc": "S'han trobat sessions tmux existents en aquest amfitrió. Seleccioneu-ne una per tornar-la a adjuntar o crear una sessió nova.", - "tmuxWindows": "Finestres", - "tmuxWindowCount": "finestra {{count}}", - "tmuxAttached": "Clients adjunts", - "tmuxAttachedCount": "{{count}} adjunt", - "tmuxLastActivity": "Darrera activitat", - "tmuxTimeJustNow": "ara mateix", - "tmuxTimeMinutes": "fa {{count}}minuts", - "tmuxTimeHours": "fa {{count}}h", - "tmuxTimeDays": "fa {{count}}dies", - "tmuxCreateNew": "Inicia una nova sessió", - "tmuxCopyHint": "Ajusta la selecció i prem Intro per copiar al porta-retalls", - "tmuxDetach": "Desconnecta de la sessió de tmux", - "tmuxDetached": "Desconnectat de la sessió tmux", - "maxReconnectAttemptsReached": "S'ha arribat al màxim d'intents de reconnexió", - "closeTab": "Tanca", - "connectionTimeout": "Temps d'espera de connexió", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Executant {{command}} - {{host}}", - "totpRequired": "Cal autenticació de dos factors", - "totpCodeLabel": "Codi de verificació", - "totpVerify": "Verifica", - "warpgateAuthRequired": "Cal autenticació de Warpgate", - "warpgateSecurityKey": "Clau de seguretat", - "warpgateAuthUrl": "URL d'autenticació", - "warpgateOpenBrowser": "Obre al navegador", - "warpgateContinue": "He completat l'autenticació", - "opksshAuthRequired": "Cal autenticació OPKSSH", - "opksshAuthDescription": "Completeu l'autenticació al navegador per continuar. Aquesta sessió serà vàlida durant 24 hores.", - "opksshOpenBrowser": "Obre el navegador per autenticar-te", - "opksshWaitingForAuth": "Esperant l'autenticació al navegador...", - "opksshAuthenticating": "Processant l'autenticació...", - "opksshTimeout": "S'ha esgotat el temps d'autenticació. Torna-ho a provar.", - "opksshAuthFailed": "L'autenticació ha fallat. Si us plau, comproveu les vostres credencials i torneu-ho a intentar.", - "opksshSignInWith": "Inicia la sessió amb {{provider}}", - "sudoPasswordPopupTitle": "Voleu inserir la contrasenya?", - "websocketAbnormalClose": "La connexió s'ha tancat inesperadament. Això pot ser degut a un problema de configuració del proxy invers o de l'SSL. Si us plau, reviseu els registres del servidor.", - "connectionLogTitle": "Registre de connexió", - "connectionLogCopy": "Copia els registres al porta-retalls", - "connectionLogEmpty": "Encara no hi ha registres de connexió", - "connectionLogWaiting": "Esperant els registres de connexió...", - "connectionLogCopied": "Registres de connexió copiats al porta-retalls", - "connectionLogCopyFailed": "No s'han pogut copiar els registres al porta-retalls", - "connectionRejected": "Connexió rebutjada pel servidor. Si us plau, comproveu l'autenticació i la configuració de xarxa.", - "hostKeyRejected": "Verificació de la clau de l'amfitrió SSH rebutjada. Connexió cancel·lada.", - "sessionTakenOver": "La sessió s'ha obert en una altra pestanya. S'està tornant a connectar..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "No s'ha seleccionat cap amfitrió", - "initializingEditor": "Inicialitzant l'editor...", - "file": "Fitxer", - "folder": "Carpeta", - "uploadFile": "Puja el fitxer", - "downloadFile": "Descarrega", - "extractArchive": "Extreure l'arxiu", - "extractingArchive": "Extraient {{name}}...", - "archiveExtractedSuccessfully": "{{name}} s'ha extret correctament", - "extractFailed": "L'extracció ha fallat", - "compressFile": "Comprimir fitxer", - "compressFiles": "Comprimir fitxers", - "compressFilesDesc": "Comprimir elements {{count}} en un arxiu", - "archiveName": "Nom de l'arxiu", - "enterArchiveName": "Introduïu el nom de l'arxiu...", - "compressionFormat": "Format de compressió", - "selectedFiles": "Fitxers seleccionats", - "andMoreFiles": "i {{count}} més...", - "compress": "Comprimir", - "compressingFiles": "Comprimint elements {{count}} en {{name}}...", - "filesCompressedSuccessfully": "{{name}} creat correctament", - "compressFailed": "La compressió ha fallat", - "edit": "Edita", - "preview": "Vista prèvia", - "previous": "Anterior", - "next": "Següent", - "pageXOfY": "Pàgina {{current}} de {{total}}", - "zoomOut": "Allunyar", - "zoomIn": "Ampliar zoom", - "newFile": "Fitxer nou", - "newFolder": "Nova carpeta", - "rename": "Canvia el nom", - "uploading": "S'està carregant...", - "uploadingFile": "S'està carregant {{name}}...", - "fileName": "Nom del fitxer", - "folderName": "Nom de la carpeta", - "fileUploadedSuccessfully": "El fitxer \"{{name}}\" s'ha carregat correctament", - "failedToUploadFile": "No s'ha pogut carregar el fitxer", - "fileDownloadedSuccessfully": "Fitxer \"{{name}}\" descarregat correctament", - "failedToDownloadFile": "No s'ha pogut descarregar el fitxer", - "fileCreatedSuccessfully": "Fitxer \"{{name}}\" creat correctament", - "folderCreatedSuccessfully": "La carpeta \"{{name}}\" s'ha creat correctament", - "failedToCreateItem": "No s'ha pogut crear l'element", - "operationFailed": "L'operació {{operation}} ha fallat per a {{name}}: {{error}}", - "failedToResolveSymlink": "No s'ha pogut resoldre l'enllaç simbòlic", - "itemsDeletedSuccessfully": "{{count}} elements suprimits correctament", - "failedToDeleteItems": "No s'han pogut suprimir els elements", - "sudoPasswordRequired": "Contrasenya d'administrador requerida", - "enterSudoPassword": "Introduïu la contrasenya sudo per continuar amb aquesta operació", - "sudoPassword": "Contrasenya de Sudo", - "sudoOperationFailed": "L'operació Sudo ha fallat.", - "sudoAuthFailed": "L'autenticació del Sudo ha fallat.", - "dragFilesToUpload": "Deixa anar els fitxers aquí per pujar-los", - "emptyFolder": "Aquesta carpeta està buida", - "searchFiles": "Cerca fitxers...", - "upload": "Pujada", - "selectHostToStart": "Seleccioneu un amfitrió per iniciar la gestió de fitxers", - "sshRequiredForFileManager": "El gestor de fitxers requereix SSH. Aquest amfitrió no té SSH habilitat.", - "failedToConnect": "No s'ha pogut connectar a SSH", - "failedToLoadDirectory": "No s'ha pogut carregar el directori", - "noSSHConnection": "No hi ha connexió SSH disponible", - "copy": "Còpia", - "cut": "Tallar", - "paste": "Enganxa", - "copyPath": "Copia la ruta", - "copyPaths": "Copiar camins", - "delete": "Suprimeix", - "properties": "Propietats", - "refresh": "Actualitza", - "downloadFiles": "Baixeu els fitxers {{count}} al navegador", - "copyFiles": "Copia elements {{count}}", - "cutFiles": "Retalla elements {{count}}", - "deleteFiles": "Suprimeix elements {{count}}", - "filesCopiedToClipboard": "{{count}} elements copiats al porta-retalls", - "filesCutToClipboard": "{{count}} elements retallats al porta-retalls", - "pathCopiedToClipboard": "La ruta s'ha copiat al porta-retalls", - "pathsCopiedToClipboard": "{{count}} camins copiats al porta-retalls", - "failedToCopyPath": "No s'ha pogut copiar la ruta al porta-retalls", - "movedItems": "Elements moguts {{count}}", - "failedToDeleteItem": "No s'ha pogut suprimir l'element", - "itemRenamedSuccessfully": "{{type}} s'ha canviat el nom correctament", - "failedToRenameItem": "No s'ha pogut canviar el nom de l'element", - "download": "Descarrega", - "permissions": "Permisos", - "size": "Mida", - "modified": "Modificat", - "path": "Camí", - "confirmDelete": "Esteu segur que voleu suprimir {{name}}?", - "permissionDenied": "Permís denegat", - "serverError": "Error del servidor", - "fileSavedSuccessfully": "Fitxer desat correctament", - "failedToSaveFile": "No s'ha pogut desar el fitxer", - "confirmDeleteSingleItem": "Esteu segur que voleu suprimir permanentment \"{{name}}\"?", - "confirmDeleteMultipleItems": "Esteu segur que voleu suprimir permanentment els elements {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Esteu segur que voleu suprimir permanentment els elements {{count}} ? Això inclou les carpetes i el seu contingut.", - "confirmDeleteFolder": "Esteu segur que voleu suprimir permanentment la carpeta \"{{name}}\" i tot el seu contingut?", - "permanentDeleteWarning": "Aquesta acció no es pot desfer. Els elements s'eliminaran permanentment del servidor.", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", "recent": "Recent", - "pinned": "Fixat", - "folderShortcuts": "Dreceres de carpeta", - "failedToReconnectSSH": "No s'ha pogut reconnectar la sessió SSH", - "openTerminalHere": "Obre el terminal aquí", - "run": "Corre", - "openTerminalInFolder": "Obre el terminal en aquesta carpeta", - "openTerminalInFileLocation": "Obre el terminal a la ubicació del fitxer", - "runningFile": "Córrer - {{file}}", - "onlyRunExecutableFiles": "Només pot executar fitxers executables", - "directories": "Directoris", - "removedFromRecentFiles": "S'ha eliminat \"{{name}}\" dels fitxers recents", - "removeFailed": "No s'ha pogut eliminar l'opció.", - "unpinnedSuccessfully": "S'ha desfixat \"{{name}}\" correctament", - "unpinFailed": "No s'ha pogut desancorar la fixació.", - "removedShortcut": "S'ha eliminat la drecera \"{{name}}\"", - "removeShortcutFailed": "No s'ha pogut eliminar la drecera.", - "clearedAllRecentFiles": "S'han esborrat tots els fitxers recents", - "clearFailed": "Error en esborrar", - "removeFromRecentFiles": "Elimina dels fitxers recents", - "clearAllRecentFiles": "Esborra tots els fitxers recents", - "unpinFile": "Desfixar fitxer", - "removeShortcut": "Elimina la drecera", - "pinFile": "Fitxer fixat", - "addToShortcuts": "Afegeix a les dreceres", - "pasteFailed": "No s'ha pogut enganxar", - "noUndoableActions": "Sense accions desfaibles", - "undoCopySuccess": "Operació de còpia desfeta: S'han suprimit {{count}} fitxers copiats", - "undoCopyFailedDelete": "Error en desfer: no s'han pogut suprimir els fitxers copiats.", - "undoCopyFailedNoInfo": "Error en desfer: no s'ha pogut trobar la informació del fitxer copiat", - "undoMoveSuccess": "Operació de moviment desfeta: S'han tornat a moure els fitxers {{count}} a la ubicació original.", - "undoMoveFailedMove": "Desfer ha fallat: no s'han pogut moure cap fitxer enrere", - "undoMoveFailedNoInfo": "Desfer ha fallat: no s'ha pogut trobar la informació del fitxer mogut", - "undoDeleteNotSupported": "L'operació de supressió no es pot desfer: els fitxers s'han suprimit permanentment del servidor.", - "undoTypeNotSupported": "Tipus d'operació de desfer no compatible", - "undoOperationFailed": "L'operació de desfer ha fallat", - "unknownError": "Error desconegut", - "confirm": "Confirma", - "find": "Troba...", - "replace": "Substitueix", - "downloadInstead": "Descarrega en comptes d'això", - "keyboardShortcuts": "Dreceres de teclat", - "searchAndReplace": "Cerca i substitueix", - "editing": "Edició", - "search": "Cerca", - "findNext": "Cerca el següent", - "findPrevious": "Cerca anterior", - "save": "Desa", - "selectAll": "Selecciona-ho tot", - "undo": "Desfer", - "redo": "Refer", - "moveLineUp": "Mou la línia cap amunt", - "moveLineDown": "Mou la línia cap avall", - "toggleComment": "Activa/desactiva el comentari", - "autoComplete": "Completar automàticament", - "imageLoadError": "No s'ha pogut carregar la imatge", - "startTyping": "Comença a escriure...", - "unknownSize": "Mida desconeguda", - "fileIsEmpty": "El fitxer està buit", - "largeFileWarning": "Avís de fitxer gran", - "largeFileWarningDesc": "Aquest fitxer té una mida de {{size}} , cosa que pot causar problemes de rendiment quan s'obre com a text.", - "fileNotFoundAndRemoved": "No s'ha trobat el fitxer \"{{name}}\" i s'ha eliminat dels fitxers recents/fixats.", - "failedToLoadFile": "No s'ha pogut carregar el fitxer: {{error}}", - "serverErrorOccurred": "S'ha produït un error del servidor. Torna-ho a provar més tard.", - "autoSaveFailed": "El desament automàtic ha fallat", - "fileAutoSaved": "Fitxer desat automàticament", - "moveFileFailed": "No s'ha pogut moure {{name}}", - "moveOperationFailed": "L'operació de moviment ha fallat", - "canOnlyCompareFiles": "Només es poden comparar dos fitxers", - "comparingFiles": "Comparació de fitxers: {{file1}} i {{file2}}", - "dragFailed": "L'operació d'arrossegament ha fallat", - "filePinnedSuccessfully": "Fitxer \"{{name}}\" fixat correctament", - "pinFileFailed": "No s'ha pogut fixar el fitxer", - "fileUnpinnedSuccessfully": "El fitxer \"{{name}}\" s'ha desanclat correctament", - "unpinFileFailed": "No s'ha pogut desancorar el fitxer", - "shortcutAddedSuccessfully": "Drecera de carpeta \"{{name}}\" afegida correctament", - "addShortcutFailed": "No s'ha pogut afegir la drecera", - "operationCompletedSuccessfully": "{{operation}} {{count}} elements correctament", - "operationCompleted": "{{operation}} {{count}} elements", - "downloadFileSuccess": "Fitxer {{name}} descarregat correctament", - "downloadFileFailed": "La descàrrega ha fallat", - "moveTo": "Mou a {{name}}", - "diffCompareWith": "Comparació de diferències amb {{name}}", - "dragOutsideToDownload": "Arrossega fora de la finestra per descarregar (fitxers{{count}})", - "newFolderDefault": "NovaCarpeta", - "newFileDefault": "NouFitxer.txt", - "successfullyMovedItems": "S'han mogut correctament {{count}} elements a {{target}}", - "move": "Moure", - "searchInFile": "Cerca en un fitxer (Ctrl+F)", - "showKeyboardShortcuts": "Mostra les dreceres de teclat", - "startWritingMarkdown": "Comença a escriure el teu contingut de rebaixes...", - "loadingFileComparison": "S'està carregant la comparació de fitxers...", - "reload": "Torna a carregar", - "compare": "Comparar", - "sideBySide": "Costat al costat", - "inline": "En línia", - "fileComparison": "Comparació de fitxers: {{file1}} vs {{file2}}", - "fileTooLarge": "Fitxer massa gran: {{error}}", - "sshConnectionFailed": "La connexió SSH ha fallat. Si us plau, comproveu la connexió a {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "No s'ha pogut carregar el fitxer: {{error}}", - "connecting": "Connectant...", - "connectedSuccessfully": "Connexió correcta", - "totpVerificationFailed": "La verificació TOTP ha fallat", - "warpgateVerificationFailed": "L'autenticació de Warpgate ha fallat", - "authenticationFailed": "L'autenticació ha fallat", - "verificationCodePrompt": "Codi de verificació:", - "changePermissions": "Canvia els permisos", - "currentPermissions": "Permisos actuals", - "owner": "propietari", - "group": "Grup", - "others": "Altres", - "read": "Llegir", - "write": "Escriure", - "execute": "Executar", - "permissionsChangedSuccessfully": "Els permisos s'han canviat correctament", - "failedToChangePermissions": "No s'han pogut canviar els permisos", - "name": "Nom", - "sortByName": "Nom", - "sortByDate": "Data de modificació", - "sortBySize": "Mida", - "ascending": "Ascendent", - "descending": "Descendent", - "root": "Arrel", - "new": "Nou", - "sortBy": "Ordena per", - "items": "Articles", - "selected": "Seleccionat", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", "octal": "Octal", - "storage": "Emmagatzematge", - "disk": "Disc", - "used": "Usat", - "of": "de", - "toggleSidebar": "Obre/tanca la barra lateral", - "cannotLoadPdf": "No es pot carregar el PDF", - "pdfLoadError": "S'ha produït un error en carregar aquest fitxer PDF.", - "loadingPdf": "S'està carregant el PDF...", - "loadingPage": "S'està carregant la pàgina..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copia a l'amfitrió…", - "moveToHost": "Moure a l'amfitrió…", - "copyItemsToHost": "Copia els elements {{count}} a l'amfitrió…", - "moveItemsToHost": "Mou elements {{count}} a l'amfitrió…", - "noHostsConnected": "No hi ha altres amfitrions de gestors de fitxers disponibles.", - "noHostsConnectedHint": "Afegiu un altre amfitrió SSH amb el Gestor de fitxers habilitat al Gestor d'amfitrions.", - "selectDestinationHost": "Selecciona l'amfitrió de destinació", - "destinationPath": "Camí de destinació", - "recentDestinations": "Destinacions recents", - "collapseRecentDestinations": "Replega les destinacions recents", - "expandRecentDestinations": "Expandeix les destinacions recents", - "browseFolders": "Navega per les carpetes de destinació", - "browseDestination": "Navega o introdueix la ruta", - "confirmCopy": "Còpia", - "confirmMove": "Moure", - "transferring": "Transferint…", - "compressing": "Comprimint…", - "extracting": "Extraient…", - "transferringItems": "Transferint {{current}} de {{total}} elements…", - "transferSuccess": "Transferència completada", - "transferError": "La transferència ha fallat", - "transferPartial": "Transferència completada amb errors {{count}}", - "transferPartialHint": "No s'ha pogut transferir: {{paths}}", - "itemsSummary": "{{count}} elements", - "destMustBeDirectory": "La destinació ha de ser un directori per a transferències de diversos elements", - "selectThisFolder": "Selecciona aquesta carpeta", - "browsePathWillBeCreated": "Aquesta carpeta encara no existeix. Es crearà quan comenci la transferència.", - "browsePathError": "No s'ha pogut obrir aquesta ruta a l'amfitrió de destinació.", - "goUp": "Puja", - "copyFolderToHost": "Copia la carpeta a l'amfitrió…", - "moveFolderToHost": "Mou la carpeta a l'amfitrió…", - "hostReady": "Llest", - "hostConnecting": "Connectant…", - "hostDisconnected": "No connectat", - "hostAuthRequired": "Cal autenticació: obriu primer el Gestor de fitxers en aquest amfitrió.", - "hostConnectionFailed": "La connexió ha fallat", - "metricsTitle": "Horaris de transferència", - "metricsPrepare": "Prepara la destinació: {{duration}}", - "metricsCompress": "Comprimir a la font: {{duration}}", - "metricsHopSourceRead": "Font → servidor: {{throughput}}", - "metricsHopDestSftpWrite": "Servidor → destí (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Servidor → destí (local): {{throughput}}", - "metricsTransfer": "Extrem a extrem: {{throughput}} ({{duration}})", - "metricsExtract": "Extracte a la destinació: {{duration}}", - "metricsSourceDelete": "Elimina de la font: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Compressió a l'amfitrió d'origen…", - "progressExtracting": "Extraient a la destinació…", - "progressTransferring": "Transferència de dades…", - "progressReconnecting": "Reconnectant…", - "parallelSegmentsLabel": "Carrils de transferència paral·lels", - "parallelSegmentsOption": "carrils {{count}}", - "parallelSegmentsHint": "Els fitxers grans es divideixen en blocs de 256 MB. Els carrils múltiples utilitzen connexions separades (com ara iniciar diverses transferències) per a un rendiment total més alt.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} carrils)", - "progressTransferringItems": "Transferència de fitxers ({{current}} de {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} fitxers", - "sourceNotDeletedPartial": "Fitxers font conservats (transferència parcial)", - "jumpHostLimitation": "Tots dos hosts han de ser accessibles des del servidor Termix. No s'admet l'encaminament directe d'host a host.", - "cancel": "Cancel·la", - "methodLabel": "Mètode de transferència", - "methodAuto": "Automàtic", - "methodTar": "Arxiu Tar", - "methodItemSftp": "SFTP per fitxer", - "methodAutoHint": "Tria SFTP tar o per fitxer en funció del nombre de fitxers, la mida i la compressibilitat. Els fitxers individuals sempre utilitzen SFTP en temps real.", - "methodTarHint": "Comprimir a l'origen, transferir un arxiu, extreure a la destinació. Requereix tar en ambdós hosts Unix.", - "methodItemSftpHint": "Transfereix cada fitxer individualment per SFTP. Funciona en tots els hosts, inclòs Windows.", - "methodPreviewLoading": "Càlcul del mètode de transferència…", - "methodPreviewError": "No s'ha pogut previsualitzar el mètode de transferència. El servidor encara triarà un mètode quan l'inicieu.", - "methodPreviewWillUseTar": "Utilitzarà: Arxiu Tar", - "methodPreviewWillUseItemSftp": "Utilitzarà: SFTP per fitxer", - "methodPreviewScanSummary": "{{fileCount}} fitxers, {{totalSize}} total (escanejats a l'amfitrió d'origen).", - "methodItemSftpLimitation": "Cada fitxer utilitza el mateix flux SFTP com una còpia d'un sol fitxer, un darrere l'altre. El progrés es combina en tots els fitxers, de manera que la barra es mou lentament durant els fitxers grans.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Heu triat SFTP per fitxer.", - "user_tar": "Has triat l'arxiu tar.", - "tar_unavailable": "Tar no està disponible en un o tots dos hosts; s'utilitzarà SFTP per fitxer.", - "windows_host": "Hi ha un host de Windows implicat; no s'utilitza tar.", - "auto_multi_large": "Automàtic: diversos fitxers, inclòs un fitxer gran ({{largestSize}}) amb dades compressibles: paquets de fitxers tar en una sola transferència.", - "auto_single_large_in_archive": "Automàtic: un fitxer gran ({{largestSize}}) en aquest conjunt — SFTP per fitxer.", - "auto_many_incompressible": "Automàtic: dades majoritàriament incompressibles — SFTP per fitxer.", - "auto_many_files": "Automàtic: molts fitxers ({{fileCount}}) — tar redueix la sobrecàrrega per fitxer.", - "auto_default": "Automàtic: SFTP per fitxer per a aquest conjunt." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Cancel·la", - "progressCancelling": "Cancel·lant…", - "progressStalled": "Estancat", - "resumedHint": "S'ha tornat a connectar a una transferència activa iniciada en una altra finestra.", - "transferCancelled": "Transferència cancel·lada", - "transferCancelledCopyHint": "És possible que hi hagi fitxers parcials a la destinació.", - "transferCancelledMoveHint": "És possible que hi hagi fitxers parcials a la destinació. No s'han eliminat els fitxers d'origen.", - "cleanupDestFiles": "Neteja la destinació", - "cleanupDestFilesSuccess": "S'han eliminat fitxers parcials de la destinació", - "cleanupDestFilesPartial": "No s'han pogut eliminar alguns fitxers parcials", - "cleanupDestFilesNothing": "Res a netejar a la destinació", - "cleanupDestFilesError": "La neteja ha fallat", - "retryTransfer": "Torna-ho a intentar", - "retryTransferError": "No s'ha pogut reintentar.", - "transferFailedRetryHint": "S'han conservat dades parcials a la destinació. El reintent es reprendrà quan es restableixi la connexió." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Sense túnels SSH", - "createFirstTunnelMessage": "Encara no has creat cap túnel SSH. Configureu les connexions del túnel al Gestor d'amfitrions per començar.", - "connected": "Connectat", - "disconnected": "Desconnectat", - "connecting": "Connectant...", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", "error": "Error", - "canceling": "Cancel·lant...", - "connect": "Connecta", - "disconnect": "Desconnecta", - "cancel": "Cancel·la", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Port local", - "remotePort": "Port remot", - "currentHostPort": "Port d'amfitrió actual", - "endpointPort": "Port de punt final", - "bindIp": "IP local", - "endpointSshConfig": "Configuració SSH del punt final", - "endpointSshHost": "Amfitrió SSH de punt final", - "endpointSshHostPlaceholder": "Seleccioneu un amfitrió configurat", - "endpointSshHostRequired": "Seleccioneu un host SSH de punt final per a cada túnel de client.", - "attempt": "Intent {{current}} de {{max}}", - "nextRetryIn": "Proper intent en {{seconds}} segons", - "clientTunnels": "Túnels de client", - "clientTunnel": "Túnel del client", - "addClientTunnel": "Afegeix un túnel de client", - "noClientTunnels": "No hi ha túnels de client configurats en aquest escriptori.", - "tunnelName": "Nom del túnel", - "remoteHost": "Amfitrió remot", - "autoStart": "Inici automàtic", - "clientAutoStartDesc": "S'inicia quan s'obre aquest client d'escriptori i roman connectat.", - "clientManualStartDesc": "Feu servir Start i Stop des d'aquesta fila. Termix no l'obrirà automàticament.", - "clientRemoteServerNote": "El reenviament remot pot requerir AllowTcpForwarding i GatewayPorts al servidor SSH del punt final. El port remot es tanca quan aquest escriptori es desconnecta.", - "clientTunnelStarted": "S'ha iniciat el túnel del client", - "clientTunnelStopped": "Túnel del client aturat", - "tunnelTestSucceeded": "La prova del túnel ha tingut èxit", - "tunnelTestFailed": "La prova del túnel ha fallat", - "localSaved": "Túnels de client desats", - "localSaveError": "No s'han pogut desar els túnels del client local.", - "invalidBindIp": "La IP local ha de ser una adreça IPv4 vàlida.", - "invalidLocalTargetIp": "La IP de destinació local ha de ser una adreça IPv4 vàlida.", - "invalidLocalPort": "El port local ha d'estar entre 1 i 65535.", - "invalidRemotePort": "El port remot ha d'estar entre 1 i 65535.", - "invalidLocalTargetPort": "El port de destinació local ha d'estar entre 1 i 65535.", - "invalidEndpointPort": "El port del punt final ha d'estar entre 1 i 65535.", - "duplicateAutoStartBind": "Només un túnel de client d'inici automàtic pot utilitzar {{bind}}.", - "manualControlError": "No s'ha pogut actualitzar l'estat del túnel.", - "active": "Actiu", - "start": "Inici", - "stop": "Atura", - "test": "Prova", - "type": "Tipus de túnel", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Comandament a distància (-R)", - "typeDynamic": "Dinàmic (-D)", - "typeServerLocalDesc": "De l'amfitrió actual al punt final.", - "typeServerRemoteDesc": "Punt final torna a l'amfitrió actual.", - "typeClientLocalDesc": "Ordinador local al punt final.", - "typeClientRemoteDesc": "Punt final de tornada a l'ordinador local.", - "typeClientDynamicDesc": "SOCKS a l'ordinador local.", - "typeDynamicDesc": "Reenvia el trànsit de SOCKS5 CONNECT a través de SSH", - "forwardDescriptionServerLocal": "Amfitrió actual {{sourcePort}} → punt final {{endpointPort}}.", - "forwardDescriptionServerRemote": "Punt final {{endpointPort}} → amfitrió actual {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS a l'amfitrió actual {{sourcePort}}.", - "forwardDescriptionClientLocal": "Local {{sourcePort}} → remot {{endpointPort}}.", - "forwardDescriptionClientRemote": "Remot {{sourcePort}} → local {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS al port local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → MITJONS via {{endpoint}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", - "autoNameClientDynamic": "MITJONS {{localPort}} via {{endpoint}}", - "route": "Ruta:", - "lastStarted": "Últim inici", - "lastTested": "Última prova", - "lastError": "Últim error", - "maxRetries": "Màxim d'intents", - "maxRetriesDescription": "Nombre màxim d'intents de reintent.", - "retryInterval": "Interval de reintent (segons)", - "retryIntervalDescription": "Temps d'espera entre intents de reintent.", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", "local": "Local", - "remote": "Remot", - "destination": "Destinació", - "host": "Amfitrió", + "remote": "Remote", + "destination": "Destination", + "host": "Host", "mode": "Mode", - "noHostSelected": "No s'ha seleccionat cap amfitrió", - "working": "Treballant..." + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Memòria", - "disk": "Disc", - "network": "Xarxa", - "uptime": "Temps de funcionament", - "processes": "Processos", - "available": "Disponible", - "free": "Gratuït", - "connecting": "Connectant...", - "connectionFailed": "No s'ha pogut connectar al servidor", - "naCpus": "CPU(s) N/A", - "cpuCores_one": "{{count}} Nucli", - "cpuCores_other": "{{count}} Nuclis", - "cpuUsage": "Ús de la CPU", - "memoryUsage": "Ús de memòria", - "diskUsage": "Ús del disc", - "failedToFetchHostConfig": "No s'ha pogut obtenir la configuració de l'amfitrió", - "serverOffline": "Servidor fora de línia", - "cannotFetchMetrics": "No es poden obtenir mètriques del servidor fora de línia", - "totpFailed": "La verificació TOTP ha fallat", - "noneAuthNotSupported": "Les estadístiques del servidor no admeten el tipus d'autenticació \"cap\".", - "load": "Carrega", - "systemInfo": "Informació del sistema", - "hostname": "Nom d'amfitrió", - "operatingSystem": "Sistema operatiu", - "kernel": "Nucli", - "seconds": "segons", - "networkInterfaces": "Interfícies de xarxa", - "noInterfacesFound": "No s'han trobat interfícies de xarxa", - "noProcessesFound": "No s'han trobat processos", - "loginStats": "Estadístiques d'inici de sessió SSH", - "noRecentLoginData": "Sense dades d'inici de sessió recents", - "executingQuickAction": "Executant {{name}}...", - "quickActionSuccess": "{{name}} s'ha completat correctament", - "quickActionFailed": "{{name}} ha fallat", - "quickActionError": "No s'ha pogut executar {{name}}", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Ports d'escolta", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adreça", - "process": "Procés", - "noData": "No hi ha dades de ports d'escolta" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Tallafocs", - "inactive": "Inactiu", - "policy": "Política", - "rules": "regles", - "noData": "No hi ha dades de tallafocs disponibles", - "action": "Acció", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Font", - "anywhere": "A qualsevol lloc" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Mitjana de càrrega", - "swap": "Intercanvi", - "architecture": "Arquitectura", - "refresh": "Actualitza", - "retry": "Torna-ho a intentar" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "SSH autoallotjat i gestió d'escriptori remot", - "loginTitle": "Inicia la sessió a Termix", - "registerTitle": "Crea un compte", - "forgotPassword": "Has oblidat la contrasenya?", - "rememberMe": "Recorda el dispositiu durant 30 dies (inclou TOTP)", - "noAccount": "No tens un compte?", - "hasAccount": "Ja tens un compte?", - "twoFactorAuth": "Autenticació de dos factors", - "enterCode": "Introduïu el codi de verificació", - "backupCode": "O bé feu servir un codi de còpia de seguretat", - "verifyCode": "Verifica el codi", - "redirectingToApp": "Redireccionant a l'aplicació...", - "sshAuthenticationRequired": "Autenticació SSH requerida", - "sshNoKeyboardInteractive": "Autenticació interactiva amb teclat no disponible", - "sshAuthenticationFailed": "Error d'autenticació", - "sshAuthenticationTimeout": "Temps d'espera d'autenticació", - "sshNoKeyboardInteractiveDescription": "El servidor no admet l'autenticació interactiva amb teclat. Si us plau, proporcioneu la vostra contrasenya o clau SSH.", - "sshAuthFailedDescription": "Les credencials proporcionades eren incorrectes. Torneu-ho a provar amb credencials vàlides.", - "sshTimeoutDescription": "S'ha esgotat el temps d'espera de l'intent d'autenticació. Torna-ho a intentar.", - "sshProvideCredentialsDescription": "Si us plau, proporcioneu les vostres credencials SSH per connectar-vos a aquest servidor.", - "sshPasswordDescription": "Introduïu la contrasenya per a aquesta connexió SSH.", - "sshKeyPasswordDescription": "Si la clau SSH està xifrada, introduïu la contrasenya aquí.", - "passphraseRequired": "Contrasenya obligatòria", - "passphraseRequiredDescription": "La clau SSH està xifrada. Introduïu la contrasenya per desbloquejar-la.", - "back": "Enrere", - "firstUser": "Primer usuari", - "firstUserMessage": "Ets el primer usuari i se't nomenarà administrador. Pots veure la configuració d'administrador al menú desplegable d'usuaris de la barra lateral. Si creus que es tracta d'un error, consulta els registres de Docker o crea una incidència de GitHub.", - "external": "Extern", - "loginWithExternal": "Inicia la sessió amb un proveïdor extern", - "loginWithExternalDesc": "Inicia la sessió amb el proveïdor d'identitat extern configurat", - "externalNotSupportedInElectron": "L'autenticació externa encara no és compatible amb l'aplicació Electron. Si us plau, utilitzeu la versió web per iniciar sessió a OIDC.", - "resetPasswordButton": "Restablir contrasenya", - "sendResetCode": "Enviar codi de restabliment", - "resetCodeDesc": "Introdueix el teu nom d'usuari per rebre un codi de restabliment de contrasenya. El codi es registrarà als registres del contenidor Docker.", - "resetCode": "Restableix el codi", - "verifyCodeButton": "Verifica el codi", - "enterResetCode": "Introduïu el codi de 6 dígits dels registres del contenidor Docker per a l'usuari:", - "newPassword": "Nova contrasenya", - "confirmNewPassword": "Confirma la contrasenya", - "enterNewPassword": "Introduïu la nova contrasenya per a l'usuari:", - "signUp": "Registra't", - "desktopApp": "Aplicació d'escriptori", - "loggingInToDesktopApp": "Inici de sessió a l'aplicació d'escriptori", - "loadingServer": "S'està carregant el servidor...", - "dataLossWarning": "Si restableixes la contrasenya d'aquesta manera, s'eliminaran tots els hosts SSH, les credencials i altres dades xifrades que hagis desat. Aquesta acció no es pot desfer. Només fes servir aquesta opció si has oblidat la contrasenya i no has iniciat la sessió.", - "authenticationDisabled": "Autenticació desactivada", - "authenticationDisabledDesc": "Tots els mètodes d'autenticació estan actualment desactivats. Poseu-vos en contacte amb el vostre administrador.", - "attemptsRemaining": "{{count}} intents restants" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifica la clau d'amfitrió SSH", - "keyChangedWarning": "Clau d'amfitrió SSH canviada", - "firstConnectionTitle": "Primera vegada que em connecto a aquest amfitrió", - "firstConnectionDescription": "No es pot establir l'autenticitat d'aquest amfitrió. Verifiqueu que l'empremta digital coincideixi amb el que espereu.", - "keyChangedDescription": "La clau d'amfitrió d'aquest servidor ha canviat des de la vostra última connexió. Això podria indicar un problema de seguretat.", - "previousKey": "Clau anterior", - "newFingerprint": "Nova empremta digital", - "fingerprint": "Empremta digital", - "verifyInstructions": "Si confieu en aquest amfitrió, feu clic a Accepta per continuar i desar aquesta empremta digital per a futures connexions.", - "securityWarning": "Avís de seguretat", - "acceptAndContinue": "Accepta i continua", - "acceptNewKey": "Accepta la clau nova i continua" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "No s'ha pogut connectar a la base de dades", - "unknownError": "Error desconegut", - "loginFailed": "Error d'inici de sessió", - "failedPasswordReset": "No s'ha pogut iniciar el restabliment de la contrasenya", - "failedVerifyCode": "No s'ha pogut verificar el codi de restabliment", - "failedCompleteReset": "No s'ha pogut completar el restabliment de la contrasenya", - "invalidTotpCode": "Codi TOTP no vàlid", - "failedOidcLogin": "No s'ha pogut iniciar l'inici de sessió a l'OIDC.", - "silentSigninOidcUnavailable": "S'ha sol·licitat l'inici de sessió silenciós, però l'inici de sessió a l'OIDC no està disponible.", - "failedUserInfo": "No s'ha pogut obtenir la informació de l'usuari després de l'inici de sessió", - "oidcAuthFailed": "L'autenticació OIDC ha fallat", - "invalidAuthUrl": "URL d'autorització no vàlida rebuda del backend", - "requiredField": "Aquest camp és obligatori", - "minLength": "La longitud mínima és {{min}}", - "passwordMismatch": "Les contrasenyes no coincideixen", - "passwordLoginDisabled": "L'inici de sessió amb nom d'usuari i contrasenya està actualment desactivat", - "sessionExpired": "La sessió ha caducat; si us plau, torneu a iniciar la sessió.", - "totpRateLimited": "Tarifa limitada: massa intents de verificació TOTP. Torna-ho a provar més tard.", - "totpRateLimitedWithTime": "Tarifa limitada: massa intents de verificació TOTP. Espereu {{time}} segons abans de tornar-ho a intentar.", - "resetCodeRateLimited": "Tarifa limitada: massa intents de verificació. Torna-ho a provar més tard.", - "resetCodeRateLimitedWithTime": "Taxa limitada: massa intents de verificació. Espereu {{time}} segons abans de tornar-ho a intentar.", - "authTokenSaveFailed": "No s'ha pogut desar el testimoni d'autenticació", - "failedToLoadServer": "No s'ha pogut carregar el servidor" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "El registre de comptes nous està desactivat actualment per un administrador. Si us plau, inicia la sessió o contacta amb un administrador.", - "userNotAllowed": "El vostre compte no està autoritzat per registrar-se. Poseu-vos en contacte amb un administrador.", - "databaseConnectionFailed": "No s'ha pogut connectar al servidor de la base de dades", - "resetCodeSent": "Restableix el codi enviat als registres de Docker", - "codeVerified": "Codi verificat correctament", - "passwordResetSuccess": "La contrasenya s'ha restablert correctament", - "loginSuccess": "Inici de sessió correcte", - "registrationSuccess": "Registre correcte" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Túnels d'escriptori locals dirigits a hosts SSH configurats.", - "c2sTunnelPresets": "Preajustos del túnel del client", - "c2sTunnelPresetsDesc": "Desa la llista de túnels locals d'aquest client d'escriptori com a predefinició de servidor amb nom o torna a carregar una predefinició en aquest client.", - "c2sTunnelPresetsUnavailable": "Els preajustos del túnel del client només estan disponibles al client d'escriptori.", - "c2sPresetName": "Nom predefinit", - "c2sPresetNamePlaceholder": "Nom predefinit del client", - "c2sPresetToLoad": "Predefinició per carregar", - "c2sNoPresetSelected": "No s'ha seleccionat cap predefinició", - "c2sNoPresets": "No s'han desat presets", - "c2sLoadPreset": "Carrega", - "c2sCurrentLocalConfig": "{{count}} túnel(s) de client local configurat(s) en aquest escriptori.", - "c2sPresetSyncNote": "Els preajustaments són instantànies explícites; carregar-ne una substitueix la llista de túnels de clients locals d'aquest client d'escriptori.", - "c2sPresetSaved": "S'ha desat la configuració predefinida del túnel del client", - "c2sPresetLoaded": "Predefinició del túnel del client carregada localment", - "c2sPresetRenamed": "S'ha canviat el nom de la predefinició del túnel del client", - "c2sPresetDeleted": "S'ha suprimit la configuració predefinida del túnel del client", - "c2sPresetLoadError": "No s'han pogut carregar els preajustos del túnel del client" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Llengua", - "keyPassword": "contrasenya clau", - "pastePrivateKey": "Enganxa la teva clau privada aquí...", - "localListenerHost": "127.0.0.1 (escolta localment)", - "localTargetHost": "127.0.0.1 (objectiu en aquest ordinador)", - "socksListenerHost": "127.0.0.1 (escoltador de SOCKS)", - "enterPassword": "Introdueix la teva contrasenya", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Tauler de control", - "loading": "S'està carregant el tauler de control...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Suport", - "discord": "Discòrdia", - "serverOverview": "Visió general del servidor", - "version": "Versió", - "upToDate": "Actualitzat", - "updateAvailable": "Actualització disponible", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Temps de funcionament", - "database": "Base de dades", - "healthy": "Saludable", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", "error": "Error", - "totalHosts": "Total d'amfitrions", - "totalTunnels": "Túnels totals", - "totalCredentials": "Credencials totals", - "recentActivity": "Activitat recent", - "reset": "Restablir", - "loadingRecentActivity": "S'està carregant l'activitat recent...", - "noRecentActivity": "Sense activitat recent", - "quickActions": "Accions ràpides", - "addHost": "Afegeix amfitrió", - "addCredential": "Afegeix credencials", - "adminSettings": "Configuració de l'administrador", - "userProfile": "Perfil d'usuari", - "serverStats": "Estadístiques del servidor", - "loadingServerStats": "S'estan carregant les estadístiques del servidor...", - "noServerData": "No hi ha dades del servidor disponibles", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalitza el tauler de control", - "dashboardSettings": "Configuració del tauler de control", - "enableDisableCards": "Activa/Desactiva les targetes", - "resetLayout": "Restableix els valors predeterminats", - "serverOverviewCard": "Visió general del servidor", - "recentActivityCard": "Activitat recent", - "networkGraphCard": "Gràfic de xarxa", - "networkGraph": "Gràfic de xarxa", - "quickActionsCard": "Accions ràpides", - "serverStatsCard": "Estadístiques del servidor", - "panelMain": "Principal", - "panelSide": "Costat", - "justNow": "ara mateix" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ESTABLE", - "hostsOnline": "Amfitrions en línia", - "activeTunnels": "Túnels actius", - "registerNewServer": "Registra un servidor nou", - "storeSshKeysOrPasswords": "Emmagatzemar claus o contrasenyes SSH", - "manageUsersAndRoles": "Gestionar usuaris i rols", - "manageYourAccount": "Gestiona el teu compte", - "hostStatus": "Estat de l'amfitrió", - "noHostsConfigured": "No hi ha cap amfitrió configurat", - "online": "EN LÍNIA", - "offline": "FORA DE LÍNIA", - "onlineLower": "En línia", - "nodes": "nodes {{count}}", - "add": "Afegeix:", - "commandPalette": "Paleta d'ordres", - "done": "Fet", - "editModeInstructions": "Arrossegueu les targetes per reordenar · Arrossegueu el divisor de columnes per canviar la mida de les columnes · Arrossegueu la vora inferior d'una targeta per canviar-ne l'alçada · Paperera per eliminar-la", - "empty": "Buit", - "clear": "Clar" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Afegeix amfitrió", - "addGroup": "Afegeix un grup", - "addLink": "Afegeix un enllaç", - "zoomIn": "Ampliar zoom", - "zoomOut": "Allunyar", - "resetView": "Restableix la vista", - "selectHost": "Selecciona l'amfitrió", - "chooseHost": "Trieu un amfitrió...", - "parentGroup": "Grup de pares", - "noGroup": "Sense grup", - "groupName": "Nom del grup", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", "color": "Color", - "source": "Font", - "target": "Objectiu", - "moveToGroup": "Moure al grup", - "selectGroup": "Selecciona el grup...", - "addConnection": "Afegeix una connexió", - "hostDetails": "Detalls de l'amfitrió", - "removeFromGroup": "Elimina del grup", - "addHostHere": "Afegeix l'amfitrió aquí", - "editGroup": "Edita el grup", - "delete": "Suprimeix", - "add": "Afegeix", - "create": "Crea", - "move": "Moure", - "connect": "Connecta", - "createGroup": "Crea un grup", - "selectSourcePlaceholder": "Selecciona la font...", - "selectTargetPlaceholder": "Selecciona l'objectiu...", - "invalidFile": "Fitxer no vàlid", - "hostAlreadyExists": "L'amfitrió ja és a la topologia", - "connectionExists": "La connexió ja existeix", - "unknown": "Desconegut", - "name": "Nom", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Estat", - "failedToAddNode": "No s'ha pogut afegir el node", - "sourceDifferentFromTarget": "La font i la destinació han de ser diferents", - "exportJSON": "Exporta JSON", - "importJSON": "Importa JSON", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Gestor de fitxers", - "tunnel": "Túnel", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Estadístiques del servidor", - "noNodes": "Encara no hi ha nodes" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker no està habilitat per a aquest amfitrió", - "validating": "Validant Docker...", - "connecting": "Connectant...", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "No s'ha pogut connectar a Docker", - "containerStarted": "Contenidor {{name}} iniciat", - "failedToStartContainer": "No s'ha pogut iniciar el contenidor {{name}}", - "containerStopped": "El contenidor {{name}} s'ha aturat", - "failedToStopContainer": "No s'ha pogut aturar el contenidor {{name}}", - "containerRestarted": "El contenidor {{name}} s'ha reiniciat", - "failedToRestartContainer": "No s'ha pogut reiniciar el contenidor {{name}}", - "containerPaused": "Contenidor {{name}} en pausa", - "containerUnpaused": "Contenidor {{name}} reactivat", - "failedToTogglePauseContainer": "No s'ha pogut activar o desactivar l'estat de pausa per al contenidor {{name}}", - "containerRemoved": "Contenidor {{name}} eliminat", - "failedToRemoveContainer": "No s'ha pogut eliminar el contenidor {{name}}", - "image": "Imatge", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", "ports": "Ports", - "noPorts": "Sense ports", - "start": "Inici", - "confirmRemoveContainer": "Esteu segur que voleu eliminar el contenidor '{{name}}'? Aquesta acció no es pot desfer.", - "runningContainerWarning": "Avís: Aquest contenidor està en execució. Si el suprimiu, primer s'aturarà el contenidor.", - "loadingContainers": "Carregant contenidors...", - "manager": "Gestor de Docker", - "autoRefresh": "Actualització automàtica", - "timestamps": "Marques de temps", - "lines": "Línies", - "filterLogs": "Filtra els registres...", - "refresh": "Actualitza", - "download": "Descarrega", - "clear": "Clar", - "logsDownloaded": "Registres descarregats correctament", - "last50": "Últims 50", - "last100": "Últims 100", - "last500": "Últims 500", - "last1000": "Últims 1000", - "allLogs": "Tots els registres", - "noLogsMatching": "No hi ha registres que coincideixin amb \"{{query}}\"", - "noLogsAvailable": "No hi ha registres disponibles", - "noContainersFound": "No s'han trobat contenidors", - "noContainersFoundHint": "No hi ha contenidors Docker disponibles en aquest amfitrió.", - "searchPlaceholder": "Cerca contenidors...", - "allStatuses": "Tots els estats", - "stateRunning": "Córrer", - "statePaused": "En pausa", - "stateExited": "Sortit", - "stateRestarting": "Reinici", - "noContainersMatchFilters": "No hi ha contenidors que coincideixin amb els vostres filtres", - "noContainersMatchFiltersHint": "Prova d'ajustar els criteris de cerca o de filtre", - "failedToFetchStats": "No s'han pogut obtenir les estadístiques del contenidor", - "containerNotRunning": "El contenidor no funciona", - "startContainerToViewStats": "Inicia el contenidor per veure les estadístiques", - "loadingStats": "S'estan carregant les estadístiques...", - "errorLoadingStats": "S'ha produït un error en carregar les estadístiques", - "noStatsAvailable": "No hi ha estadístiques disponibles", - "cpuUsage": "Ús de la CPU", - "current": "Actual", - "memoryUsage": "Ús de memòria", - "networkIo": "E/S de xarxa", - "input": "Entrada", - "output": "Sortida", - "blockIo": "Bloc d'E/S", - "read": "Llegir", - "write": "Escriure", - "pids": "PID", - "containerInformation": "Informació del contenidor", - "name": "Nom", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Estat", - "containerMustBeRunning": "El contenidor ha d'estar en execució per accedir a la consola.", - "verificationCodePrompt": "Introduïu el codi de verificació", - "totpVerificationFailed": "La verificació TOTP ha fallat. Torna-ho a provar.", - "warpgateVerificationFailed": "L'autenticació de Warpgate ha fallat. Torna-ho a intentar.", - "connectedTo": "Connectat a {{containerName}}", - "disconnected": "Desconnectat", - "consoleError": "Error de consola", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", "errorMessage": "Error: {{message}}", - "failedToConnect": "No s'ha pogut connectar al contenidor", - "console": "Consola", - "selectShell": "Selecciona l'intèrpret d'ordres", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "cendra", - "connect": "Connecta", - "disconnect": "Desconnecta", - "notConnected": "No connectat", - "clickToConnect": "Feu clic a Connecta per iniciar una sessió de shell", - "connectingTo": "Connectant a {{containerName}}...", - "containerNotFound": "No s'ha trobat el contenidor", - "backToList": "Torna a la llista", - "logs": "Registres", - "stats": "Estadístiques", - "consoleTab": "Consola", - "startContainerToAccess": "Inicieu el contenidor per accedir a la consola" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Usuaris", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", "sectionSessions": "Sessions", - "sectionRoles": "Rols", - "sectionDatabase": "Base de dades", - "sectionApiKeys": "Claus API", - "allowRegistration": "Permetre el registre d'usuaris", - "allowRegistrationDesc": "Permet que els nous usuaris s'autoregistrin", - "allowPasswordLogin": "Permet l'inici de sessió amb contrasenya", - "allowPasswordLoginDesc": "Inici de sessió amb nom d'usuari/contrasenya", - "oidcAutoProvision": "Autoprovisió OIDC", - "oidcAutoProvisionDesc": "Crea comptes automàticament per a usuaris d'OIDC fins i tot quan el registre està desactivat", - "allowPasswordReset": "Permet el restabliment de la contrasenya", - "allowPasswordResetDesc": "Restableix el codi a través dels registres de Docker", - "sessionTimeout": "Temps d'espera de la sessió", - "hours": "hores", - "sessionTimeoutRange": "Mín 1 h · Màx 720 h", - "monitoringDefaults": "Monitorització dels valors predeterminats", - "statusCheck": "Comprovació d'estat", - "metrics": "Mètriques", - "sec": "segons", - "logLevel": "Nivell de registre", - "enableGuacamole": "Activa el guacamole", - "enableGuacamoleDesc": "Escriptori remot RDP/VNC", - "guacdUrl": "URL de guacd", - "oidcDescription": "Configura OpenID Connect per a SSO. Els camps marcats amb un * són obligatoris.", - "oidcClientId": "ID de client", - "oidcClientSecret": "Secret del client", - "oidcAuthUrl": "URL d'autorització", - "oidcIssuerUrl": "URL de l'emissor", - "oidcTokenUrl": "URL del testimoni", - "oidcUserIdentifier": "Ruta d'identificació d'usuari", - "oidcDisplayName": "Camí del nom de visualització", - "oidcScopes": "Àmbits", - "oidcUserinfoUrl": "Substitueix l'URL d'informació de l'usuari", - "oidcAllowedUsers": "Usuaris permesos", - "oidcAllowedUsersDesc": "Un correu electrònic per línia. Deixeu-ho en blanc per permetre-ho tot.", - "removeOidc": "Elimina", - "usersCount": "{{count}} usuaris", - "createUser": "Crea", - "newRole": "Nou rol", - "roleName": "Nom", - "roleDisplayName": "Nom de visualització", - "roleDescription": "Descripció", - "rolesCount": "rols {{count}}", - "createRole": "Crea", - "creating": "Creant...", - "exportDatabase": "Exporta la base de dades", - "exportDatabaseDesc": "Baixeu una còpia de seguretat de tots els hosts, credencials i configuracions", - "export": "Exporta", - "exporting": "Exportant...", - "importDatabase": "Importa la base de dades", - "importDatabaseDesc": "Restaurar des d'un fitxer de còpia de seguretat .sqlite", - "importDatabaseSelected": "Seleccionat: {{name}}", - "selectFile": "Selecciona un fitxer", - "changeFile": "Canvi", - "import": "Importa", - "importing": "Important...", - "apiKeysCount": "tecles {{count}}", - "newApiKey": "Nova clau API", - "apiKeyCreatedWarning": "Clau creada: copia-la ara, no es tornarà a mostrar.", - "apiKeyName": "Nom", - "apiKeyUser": "Usuari", - "apiKeySelectUser": "Selecciona un usuari...", - "apiKeyExpiresAt": "Caduca a", - "createKey": "Crea una clau", - "apiKeyNoExpiry": "Sense caducitat", - "revokedBadge": "REVOCAT", - "authTypeDual": "Doble autenticació", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", "authTypeLocal": "Local", - "adminStatusAdministrator": "Administrador/a", - "adminStatusRegularUser": "Usuari habitual", - "adminBadge": "ADMINISTRADOR", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "PERSONALITZAT", - "youBadge": "TU", - "sessionsActive": "{{count}} actiu", - "sessionActive": "Actiu: {{time}}", - "sessionExpires": "Caducitat: {{time}}", - "revokeAll": "Tot", - "revokeAllSessionsSuccess": "Totes les sessions per a l'usuari revocades", - "revokeAllSessionsFailed": "No s'han pogut revocar les sessions", - "revokeSessionFailed": "No s'ha pogut revocar la sessió", - "addRole": "Afegeix un rol", - "noCustomRoles": "No s'han definit rols personalitzats", - "removeRoleFailed": "No s'ha pogut eliminar el rol", - "assignRoleFailed": "No s'ha pogut assignar el rol", - "deleteRoleFailed": "No s'ha pogut suprimir el rol", - "userAdminAccess": "Administrador/a", - "userAdminAccessDesc": "Accés complet a tots els paràmetres d'administració", - "userRoles": "Rols", - "revokeAllUserSessions": "Revoca totes les sessions", - "revokeAllUserSessionsDesc": "Forçar el reinici de sessió en tots els dispositius", - "revoke": "Revocar", - "deleteUserWarning": "Eliminar aquest usuari és permanent.", - "deleteUser": "Suprimeix {{username}}", - "deleting": "S'està suprimint...", - "deleteUserFailed": "No s'ha pogut suprimir l'usuari", - "deleteUserSuccess": "L'usuari \"{{username}}\" s'ha suprimit", - "deleteRoleSuccess": "Rol \"{{name}}\" suprimit", - "revokeKeySuccess": "Clau \"{{name}}\" revocada", - "revokeKeyFailed": "No s'ha pogut revocar la clau", - "copiedToClipboard": "Copiat al porta-retalls", - "done": "Fet", - "createUserTitle": "Crea un usuari", - "createUserDesc": "Crea un compte local nou.", - "createUserUsername": "Nom d'usuari", - "createUserPassword": "Contrasenya", - "createUserPasswordHint": "Mínim 6 caràcters.", - "createUserEnterUsername": "Introdueix el nom d'usuari", - "createUserEnterPassword": "Introdueix la contrasenya", - "createUserSubmit": "Crea un usuari", - "editUserTitle": "Gestiona l'usuari: {{username}}", - "editUserDesc": "Edita els rols, l'estat d'administrador, les sessions i la configuració del compte.", - "editUserUsername": "Nom d'usuari", - "editUserAuthType": "Tipus d'autorització", - "editUserAdminStatus": "Estat d'administrador", - "editUserUserId": "ID d'usuari", - "linkAccountTitle": "Enllaça l'OIDC al compte de contrasenya", - "linkAccountDesc": "Fusiona el compte OIDC {{username}} amb un compte local existent.", - "linkAccountWarningTitle": "Això farà:", - "linkAccountEffect1": "Suprimeix el compte només d'OIDC", - "linkAccountEffect2": "Afegeix l'inici de sessió OIDC al compte de destinació", - "linkAccountEffect3": "Permet l'inici de sessió amb OIDC i contrasenya", - "linkAccountTargetUsername": "Nom d'usuari de destinació", - "linkAccountTargetPlaceholder": "Introduïu el nom d'usuari del compte local al qual voleu enllaçar", - "linkAccounts": "Enllaçar comptes", - "linkAccountSuccess": "Compte OIDC vinculat a \"{{username}}\"", - "linkAccountFailed": "No s'ha pogut enllaçar el compte OIDC", - "linkAccountInProgress": "Enllaçant...", - "saving": "Desant...", - "updateRegistrationFailed": "No s'ha pogut actualitzar la configuració del registre", - "updatePasswordLoginFailed": "No s'ha pogut actualitzar la configuració d'inici de sessió amb contrasenya", - "updateOidcAutoProvisionFailed": "No s'ha pogut actualitzar la configuració d'aprovisionament automàtic d'OIDC.", - "updatePasswordResetFailed": "No s'ha pogut actualitzar la configuració de restabliment de la contrasenya", - "sessionTimeoutRange2": "El temps d'espera de la sessió ha d'estar entre 1 i 720 hores", - "sessionTimeoutSaved": "S'ha desat el temps d'espera de la sessió", - "sessionTimeoutSaveFailed": "No s'ha pogut desar el temps d'espera de la sessió", - "monitoringIntervalInvalid": "Valors d'interval no vàlids", - "monitoringSaved": "Configuració de monitorització desada", - "monitoringSaveFailed": "No s'ha pogut desar la configuració de monitorització", - "guacamoleSaved": "Configuració del guacamole desada", - "guacamoleSaveFailed": "No s'ha pogut desar la configuració del guacamole", - "guacamoleUpdateFailed": "No s'ha pogut actualitzar la configuració del guacamole", - "logLevelUpdateFailed": "No s'ha pogut actualitzar el nivell de registre", - "oidcSaved": "Configuració OIDC desada", - "oidcSaveFailed": "No s'ha pogut desar la configuració OIDC.", - "oidcRemoved": "Configuració OIDC eliminada", - "oidcRemoveFailed": "No s'ha pogut eliminar la configuració OIDC.", - "createUserRequired": "Cal un nom d'usuari i una contrasenya", - "createUserPasswordTooShort": "La contrasenya ha de tenir com a mínim 6 caràcters", - "createUserSuccess": "L'usuari \"{{username}}\" ha estat creat", - "createUserFailed": "No s'ha pogut crear l'usuari", - "updateAdminStatusFailed": "No s'ha pogut actualitzar l'estat de l'administrador", - "allSessionsRevoked": "Totes les sessions revocades", - "revokeSessionsFailed": "No s'han pogut revocar les sessions", - "createRoleRequired": "El nom i el nom de visualització són obligatoris", - "createRoleSuccess": "S'ha creat el rol \"{{name}}\"", - "createRoleFailed": "No s'ha pogut crear el rol", - "apiKeyNameRequired": "El nom de la clau és obligatori", - "apiKeyUserRequired": "Cal l'ID d'usuari", - "apiKeyCreatedSuccess": "Clau API \"{{name}}\" creada", - "apiKeyCreateFailed": "No s'ha pogut crear la clau de l'API", - "exportSuccess": "La base de dades s'ha exportat correctament", - "exportFailed": "L'exportació de la base de dades ha fallat", - "importSelectFile": "Si us plau, seleccioneu primer un fitxer", - "importCompleted": "Importació completada: {{total}} elements importats, {{skipped}} omesos", - "importFailed": "La importació ha fallat: {{error}}", - "importError": "La importació de la base de dades ha fallat" + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Amfitrió", - "hostPlaceholder": "192.168.1.1 o example.com", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nom d'usuari", - "usernamePlaceholder": "nom d'usuari", - "authLabel": "Aut.", - "passwordLabel": "Contrasenya", - "passwordPlaceholder": "contrasenya", - "privateKeyLabel": "Clau privada", - "privateKeyPlaceholder": "Enganxa la clau privada...", - "credentialLabel": "Credencial", - "credentialPlaceholder": "Selecciona una credencial desada", - "connectToTerminal": "Connecta't al terminal", - "connectToFiles": "Connecta't a fitxers" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "No s'ha seleccionat cap terminal", - "noTerminalSelectedHint": "Obriu una pestanya de terminal SSH per veure el seu historial d'ordres", - "searchPlaceholder": "Historial de cerca...", - "clearAll": "Esborra-ho tot", - "noHistoryEntries": "Sense entrades a l'historial", - "trackingDisabled": "El seguiment de l'historial està desactivat", - "trackingDisabledHint": "Activa-ho a la configuració del teu perfil per enregistrar ordres." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Enregistrament de claus", - "recordToTerminals": "Registre als terminals", - "selectAll": "Tot", - "selectNone": "Cap", - "noTerminalTabsOpen": "No hi ha pestanyes de terminal obertes", - "selectTerminalsAbove": "Seleccioneu els terminals anteriors", - "broadcastInputPlaceholder": "Escriu aquí per emetre les pulsacions de tecles...", - "stopRecording": "Atura l'enregistrament", - "startRecording": "Comença a gravar", - "settingsTitle": "Configuració", - "enableRightClickCopyPaste": "Activa la funció de copiar/enganxar amb el clic dret" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Disseny", - "selectLayoutAbove": "Selecciona un disseny a dalt", - "selectLayoutHint": "Trieu quants panells voleu mostrar", - "panesTitle": "Panells", - "openTabsTitle": "Obre les pestanyes", - "dragTabsHint": "Arrossegueu pestanyes als panells superiors o feu servir Assignació ràpida", - "dropHere": "Deixa anar aquí", - "emptyPane": "Buit", - "dashboard": "Tauler de control", - "clearSplitScreen": "Pantalla dividida clara", - "quickAssign": "Assignació ràpida", - "alreadyAssigned": "Panell {{index}}", - "splitTab": "Pestanya dividida", - "addToSplit": "Afegeix a Dividir", - "removeFromSplit": "Elimina de Split", - "assignToPane": "Assigna al panell" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Crea un fragment", - "createSnippetDescription": "Crea un nou fragment d'ordre per a una execució ràpida", - "nameLabel": "Nom", - "namePlaceholder": "p. ex., reiniciar Nginx", - "descriptionLabel": "Descripció", - "descriptionPlaceholder": "Descripció opcional", - "optional": "Opcional", - "folderLabel": "Carpeta", - "noFolder": "Sense carpeta (Sense categoria)", - "commandLabel": "Comandament", - "commandPlaceholder": "p. ex., sudo systemctl reinicia nginx", - "cancel": "Cancel·la", - "createSnippetButton": "Crea un fragment", - "createFolderTitle": "Crea una carpeta", - "createFolderDescription": "Organitza els teus fragments en carpetes", - "folderNameLabel": "Nom de la carpeta", - "folderNamePlaceholder": "p. ex., ordres del sistema, scripts de Docker", - "folderColorLabel": "Color de la carpeta", - "folderIconLabel": "Icona de carpeta", - "previewLabel": "Vista prèvia", - "folderNameFallback": "Nom de la carpeta", - "createFolderButton": "Crea una carpeta", - "targetTerminals": "Terminals de destinació", - "selectAll": "Tot", - "selectNone": "Cap", - "noTerminalTabsOpen": "No hi ha pestanyes de terminal obertes", - "searchPlaceholder": "Fragments de cerca...", - "newSnippet": "Nou fragment", - "newFolder": "Nova carpeta", - "run": "Corre", - "noSnippetsInFolder": "No hi ha fragments en aquesta carpeta", - "uncategorized": "Sense categoria", - "editSnippetTitle": "Edita el fragment", - "editSnippetDescription": "Actualitza aquest fragment d'ordres", - "saveSnippetButton": "Desa els canvis", - "createSuccess": "Fragment creat correctament", - "createFailed": "No s'ha pogut crear el fragment", - "updateSuccess": "Fragment actualitzat correctament", - "updateFailed": "No s'ha pogut actualitzar el fragment", - "deleteFailed": "No s'ha pogut suprimir el fragment", - "folderCreateSuccess": "Carpeta creada correctament", - "folderCreateFailed": "No s'ha pogut crear la carpeta", - "editFolderTitle": "Edita la carpeta", - "editFolderDescription": "Canvia el nom o l'aspecte d'aquesta carpeta", - "saveFolderButton": "Desa els canvis", - "editFolder": "Edita la carpeta", - "deleteFolder": "Suprimeix la carpeta", - "folderDeleteSuccess": "S'ha suprimit la carpeta \"{{name}}\"", - "folderDeleteFailed": "No s'ha pogut suprimir la carpeta", - "folderEditSuccess": "La carpeta s'ha actualitzat correctament", - "folderEditFailed": "No s'ha pogut actualitzar la carpeta", - "confirmRunMessage": "Executar \"{{name}}\"?", - "confirmRunButton": "Corre", - "runSuccess": "Ha executat \"{{name}}\" en terminals {{count}}", - "copySuccess": "S'ha copiat \"{{name}}\" al porta-retalls", - "shareTitle": "Compartir fragment", - "shareUser": "Usuari", - "shareRole": "Rol", - "selectUser": "Selecciona un usuari...", - "selectRole": "Selecciona un rol...", - "shareSuccess": "Fragment compartit correctament", - "shareFailed": "No s'ha pogut compartir el fragment", - "revokeSuccess": "Accés revocat", - "revokeFailed": "No s'ha pogut revocar l'accés", - "currentAccess": "Accés actual", - "shareLoadError": "No s'han pogut carregar les dades compartides", - "loading": "S'està carregant...", - "close": "Tanca", - "reorderFailed": "No s'ha pogut desar l'ordre del fragment" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Compte", - "sectionAppearance": "Aspecte", - "sectionSecurity": "Seguretat", - "sectionApiKeys": "Claus API", - "sectionC2sTunnels": "Túnels C2S", - "usernameLabel": "Nom d'usuari", - "roleLabel": "Rol", - "roleAdministrator": "Administrador/a", - "authMethodLabel": "Mètode d'autenticació", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Activat", - "twoFaOff": "Desactivat", - "versionLabel": "Versió", - "deleteAccount": "Suprimeix el compte", - "deleteAccountDescription": "Elimina el teu compte permanentment", - "deleteButton": "Suprimeix", - "deleteAccountPermanent": "Aquesta acció és permanent i no es pot desfer.", - "deleteAccountWarning": "Totes les sessions, els amfitrions, les credencials i la configuració s'eliminaran permanentment.", - "confirmPasswordDeletePlaceholder": "Introdueix la teva contrasenya per confirmar", - "languageLabel": "Llengua", - "themeLabel": "Tema", - "fontSizeLabel": "Mida de la lletra", - "accentColorLabel": "Color d'accent", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Autocompletar ordres", - "commandAutocompleteDesc": "Mostra l'autocompleció mentre s'escriu", - "historyTracking": "Seguiment de l'historial", - "historyTrackingDesc": "Comandes del terminal de pista", - "syntaxHighlighting": "Ressaltat de sintaxi", - "syntaxHighlightingDesc": "Ressaltar la sortida del terminal", - "commandPalette": "Paleta d'ordres", - "commandPaletteDesc": "Activa la drecera de teclat", - "reopenTabsOnLogin": "Reobrir pestanyes en iniciar sessió", - "reopenTabsOnLoginDesc": "Restaura les pestanyes obertes quan inicies la sessió o actualitzes la pàgina, fins i tot des d'un altre dispositiu", - "confirmTabClose": "Confirma el tancament de la pestanya", - "confirmTabCloseDesc": "Pregunta abans de tancar pestanyes del terminal", - "settingsSidebar": "Barra lateral", - "showHostTags": "Mostra les etiquetes de l'amfitrió", - "showHostTagsDesc": "Mostra les etiquetes a la llista d'amfitrions", - "hostTrayOnClick": "Feu clic per expandir les accions de l'amfitrió", - "hostTrayOnClickDesc": "Mostra sempre els botons de connexió; fes clic per ampliar les opcions de gestió en comptes de passar el cursor per sobre", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Mantingueu el rail de l'aplicació de la barra lateral esquerra sempre expandit en comptes d'expandir-se en passar el cursor per sobre", - "settingsSnippets": "Fragments", - "foldersCollapsed": "Carpetes replegades", - "foldersCollapsedDesc": "Replega les carpetes per defecte", - "confirmExecution": "Confirma l'execució", - "confirmExecutionDesc": "Confirma abans d'executar fragments", - "settingsUpdates": "Actualitzacions", - "disableUpdateChecks": "Desactiva les comprovacions d'actualització", - "disableUpdateChecksDesc": "Deixa de buscar actualitzacions", - "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "2FA està activat", - "totpDisabled": "Afegeix seguretat d'inici de sessió addicional", - "disable": "Desactiva", - "enable": "Activa", - "setupTotp": "Configura TOTP", - "qrCode": "Codi QR", - "totpInstructions": "Escaneja el codi QR o introdueix el secret a l'aplicació d'autenticació i, a continuació, introdueix el codi de 6 dígits.", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifica", - "changePassword": "Canvia la contrasenya", - "currentPasswordLabel": "Contrasenya actual", - "currentPasswordPlaceholder": "Contrasenya actual", - "newPasswordLabel": "Nova contrasenya", - "newPasswordPlaceholder": "Nova contrasenya", - "confirmPasswordLabel": "Confirma la nova contrasenya", - "confirmPasswordPlaceholder": "Confirma la nova contrasenya", - "updatePassword": "Actualitza la contrasenya", - "createApiKeyTitle": "Crea una clau API", - "createApiKeyDescription": "Genera una nova clau API per a l'accés programàtic.", - "apiKeyNameLabel": "Nom", - "apiKeyNamePlaceholder": "p. ex., canonada de CI", - "expiryDateLabel": "Data de caducitat", - "optional": "opcional", - "cancel": "Cancel·la", - "createKey": "Crea una clau", - "apiKeyCount": "tecles {{count}}", - "newKey": "Nova clau", - "noApiKeys": "Encara no hi ha claus API.", - "apiKeyActive": "Actiu", - "apiKeyUsageHint": "Inclou la teva clau a la", - "apiKeyUsageHintHeader": "capçalera.", - "apiKeyPermissionsHint": "Les claus hereten els permisos de l'usuari que les crea.", - "roleUser": "Usuari", - "authMethodDual": "Doble autenticació", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "No s'ha pogut iniciar la configuració del TOTP", - "totpEnter6Digits": "Introduïu un codi de 6 dígits", - "totpEnabledSuccess": "Autenticació de dos factors activada", - "totpInvalidCode": "Codi no vàlid, si us plau, torna-ho a intentar.", - "totpDisableInputRequired": "Introdueix el teu codi TOTP o contrasenya", - "totpDisabledSuccess": "Autenticació de dos factors desactivada", - "totpDisableFailed": "No s'ha pogut desactivar la 2FA", - "totpDisableTitle": "Desactiva la 2FA", - "totpDisablePlaceholder": "Introduïu el codi TOTP o la contrasenya", - "totpDisableConfirm": "Desactiva la 2FA", - "totpContinueVerify": "Continua a verificar", - "totpVerifyTitle": "Verifica el codi", - "totpBackupTitle": "Codis de còpia de seguretat", - "totpDownloadBackup": "Baixa els codis de còpia de seguretat", - "done": "Fet", - "secretCopied": "Secret copiat al porta-retalls", - "apiKeyNameRequired": "El nom de la clau és obligatori", - "apiKeyCreated": "Clau API \"{{name}}\" creada", - "apiKeyCreateFailed": "No s'ha pogut crear la clau de l'API", - "apiKeyUser": "Usuari", - "apiKeyExpires": "Caduca", - "apiKeyRevoked": "Clau API \"{{name}}\" revocada", - "apiKeyRevokeFailed": "No s'ha pogut revocar la clau de l'API", - "passwordFieldsRequired": "Calen contrasenyes actuals i noves", - "passwordMismatch": "Les contrasenyes no coincideixen", - "passwordTooShort": "La contrasenya ha de tenir com a mínim 6 caràcters", - "passwordUpdated": "La contrasenya s'ha actualitzat correctament", - "passwordUpdateFailed": "No s'ha pogut actualitzar la contrasenya", - "deletePasswordRequired": "Cal una contrasenya per eliminar el vostre compte", - "deleteFailed": "No s'ha pogut suprimir el compte", - "deleting": "S'està suprimint...", - "colorPickerTooltip": "Obre el selector de colors", - "themeSystem": "Sistema", - "themeLight": "Llum", - "themeDark": "Fosc", - "themeDracula": "Dràcula", - "themeCatppuccin": "Catpuccina", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solaritzat", - "themeTokyoNight": "Nit de Tòquio", - "themeOneDark": "Una foscor", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/cs_CZ.json b/src/ui/locales/translated/cs_CZ.json index 0ff93c6c..9bc5af32 100644 --- a/src/ui/locales/translated/cs_CZ.json +++ b/src/ui/locales/translated/cs_CZ.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Složky", - "folder": "Složka", - "password": "Heslo", - "key": "Klíč", - "sshPrivateKey": "Soukromý klíč SSH", - "upload": "Nahrát", - "keyPassword": "Heslo klíče", - "sshKey": "Klíč SSH", - "uploadPrivateKeyFile": "Nahrát soubor soukromého klíče", - "searchCredentials": "Hledat přihlašovací údaje…", - "addCredential": "Přidat přihlašovací údaje", - "caCertificate": "Certifikát CA (-cert.pub)", - "caCertificateDescription": "Volitelně: Nahrajte nebo vložte soubor certifikátu podepsaný CA (např. id_ed25519-cert.pub). Vyžadováno, pokud váš SSH server používá autorizaci založenou na certifikátech.", - "uploadCertFile": "Nahrát -cert.pub soubor", - "clearCert": "Vyčistit", - "certLoaded": "Certifikát načten", - "certPublicKeyLabel": "Certifikát CA", - "certTypeLabel": "Typ certifikátu", - "pasteOrUploadCert": "Vložit nebo nahrát certifikát -cert.pub...", - "hasCaCert": "Má CA certifikát", - "noCaCert": "Žádný certifikát CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Nepodařilo se načíst upozornění", - "failedToDismissAlert": "Nepodařilo se zrušit výstrahu" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Konfigurace serveru", - "description": "Konfigurace URL serveru Termix pro připojení k vašim službám backendu", - "serverUrl": "URL serveru", - "enterServerUrl": "Zadejte adresu URL serveru", - "saveFailed": "Nepodařilo se uložit konfiguraci", - "saveError": "Chyba při ukládání konfigurace", - "saving": "Ukládání...", - "saveConfig": "Uložit konfiguraci", - "helpText": "Zadejte URL, kde běží váš Termix server (např. http://localhost:30001 nebo https://your-server.com)", - "changeServer": "Změnit server", - "mustIncludeProtocol": "Adresa URL serveru musí začínat http:// nebo https:////", - "allowInvalidCertificate": "Povolit neplatný certifikát", - "allowInvalidCertificateDesc": "Používejte pouze pro důvěryhodné servery s vlastním hostováním a certifikáty s vlastním podpisem nebo certifikáty IP adresy.", - "useEmbedded": "Použít místní server", - "embeddedDesc": "Spustit Termix s vestavěným lokálním serverem (nepotřebujete vzdálený server)", - "embeddedConnecting": "Připojování k lokálnímu serveru...", - "embeddedNotReady": "Místní server není ještě připravený. Počkejte prosím chvíli a zkuste to znovu.", - "localServer": "Místní server" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Chyba kontroly verze", - "checkFailed": "Nepodařilo se zkontrolovat aktualizace", - "upToDate": "Aplikace je aktuální", - "currentVersion": "Používáte verzi {{version}}", - "updateAvailable": "Dostupná aktualizace", - "newVersionAvailable": "Je dostupná nová verze! Používáte {{current}}, ale {{latest}} je k dispozici.", - "betaVersion": "Beta verze", - "betaVersionDesc": "Používáte {{current}}, což je novější než poslední stabilní vydání {{latest}}.", - "releasedOn": "Vydáno v {{date}}", - "downloadUpdate": "Stáhnout aktualizaci", - "checking": "Kontrola aktualizací...", - "checkUpdates": "Zkontrolovat aktualizace", - "checkingUpdates": "Kontrola aktualizací...", - "updateRequired": "Vyžadována aktualizace" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Zavřít", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Pokračovat", - "maintenance": "Údržba", - "degraded": "Rozklad", - "error": "Chyba", - "warning": "Varování", - "unsavedChanges": "Neuložené změny", - "dismiss": "Odmítnout", - "loading": "Načítám...", - "optional": "Nepovinné", - "connect": "Připojit", - "copied": "Zkopírováno", - "connecting": "Připojování...", - "updateAvailable": "Dostupná aktualizace", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Otevřít v nové kartě", - "noReleases": "Žádné vydání", - "updatesAndReleases": "Aktualizace a vydání", - "newVersionAvailable": "Je k dispozici nová verze ({{version}}).", - "failedToFetchUpdateInfo": "Nepodařilo se načíst informace o aktualizaci", - "preRelease": "Předběžné vydání", - "noReleasesFound": "Nebyly nalezeny žádné verze.", - "cancel": "Zrušit", - "username": "Uživatelské jméno", - "login": "Přihlásit se", - "register": "Registrovat se", - "password": "Heslo", - "confirmPassword": "Potvrzení hesla", - "back": "Zpět", - "save": "Uložit", - "saving": "Ukládání...", - "delete": "Vymazat", - "rename": "Přejmenovat", - "edit": "Upravit", - "add": "Přidat", - "confirm": "Potvrdit", - "no": "Ne", - "or": "NEBO", - "next": "Další", - "previous": "Předchozí", - "refresh": "Aktualizovat", - "language": "Jazyk", - "checking": "Kontroluje...", - "checkingDatabase": "Kontrola připojení k databázi...", - "checkingAuthentication": "Kontrola ověřování...", - "backendReconnected": "Připojení k serveru obnoveno", - "connectionDegraded": "Připojení k serveru bylo ztraceno, obnovuje se…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Odebrat", - "create": "Vytvořit", - "update": "Aktualizovat", - "copy": "Kopírovat", - "copyFailed": "Kopírování do schránky se nezdařilo", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Obnovit", - "of": "z" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Domů", - "terminal": "Terminál", - "docker": "Dokovací modul", - "tunnels": "Tunely", - "fileManager": "Správce souborů", - "serverStats": "Statistiky serveru", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", - "userProfile": "Profil uživatele", - "splitScreen": "Rozdělit obrazovku", - "confirmClose": "Zavřít tuto aktivní relaci?", - "close": "Zavřít", - "cancel": "Zrušit", - "sshManager": "Správce SSH", - "cannotSplitTab": "Nelze rozdělit tuto kartu", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopírovat heslo", - "copySudoPassword": "Kopírovat heslo Sudo", - "passwordCopied": "Heslo zkopírováno do schránky", - "noPasswordAvailable": "Žádné heslo není k dispozici", - "failedToCopyPassword": "Kopírování hesla se nezdařilo", - "refreshTab": "Aktualizovat připojení", - "openFileManager": "Otevřít správce souborů", - "dashboard": "Nástěnka", - "networkGraph": "Graf sítě", - "quickConnect": "Rychlé připojení", - "sshTools": "Nástroje SSH", - "history": "Historie", - "hosts": "Hostitelé", - "snippets": "Úryvky", - "hostManager": "Správce hostitelů", - "credentials": "Pověření", - "connections": "Připojení", - "roleAdministrator": "Administrátor", - "roleUser": "Uživatel" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Hostitelé", - "noHosts": "Žádné SSH hostitele", - "retry": "Opakovat", - "refresh": "Aktualizovat", - "optional": "Nepovinné", - "downloadSample": "Stáhnout vzorek", - "failedToDeleteHost": "Nepodařilo se odstranit {{name}}", - "importSkipExisting": "Import (existující přeskočení)", - "connectionDetails": "Detaily připojení", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Vzdálená plocha", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Uživatelské jméno", - "folder": "Složka", - "tags": "Štítky", - "pin": "Připnout", - "addHost": "Přidat hostitele", - "editHost": "Upravit hostitele", - "cloneHost": "Klonovat hostitele", - "enableTerminal": "Povolit terminál", - "enableTunnel": "Povolit tunel", - "enableFileManager": "Povolit správce souborů", - "enableDocker": "Povolit Docker", - "defaultPath": "Výchozí cesta", - "connection": "Připojení", - "upload": "Nahrát", - "authentication": "Ověření", - "password": "Heslo", - "key": "Klíč", - "credential": "Pověření", - "none": "Nic", - "sshPrivateKey": "SSH soukromý klíč", - "keyType": "Typ klíče", - "uploadFile": "Nahrát soubor", - "tabGeneral": "Obecná ustanovení", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunely", - "tabDocker": "Dokovací modul", - "tabFiles": "Soubory", - "tabStats": "Statistiky", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Sdílení", - "tabAuthentication": "Ověření", - "terminal": "Terminál", - "tunnel": "Tunel", - "fileManager": "Správce souborů", - "serverStats": "Statistiky serveru", - "status": "Stav", - "folderRenamed": "Složka \"{{oldName}}\" byla úspěšně přejmenována na \"{{newName}}\"", - "failedToRenameFolder": "Složku se nepodařilo přejmenovat", - "movedToFolder": "Přesunuto do \"{{folder}}\"", - "editHostTooltip": "Upravit hostitele", - "statusChecks": "Kontrola stavu", - "metricsCollection": "Metrika kolekce", - "metricsInterval": "Interval sběru metrik", - "metricsIntervalDesc": "Jak často shromažďovat statistiky serveru (5s - 1h)", - "behavior": "Chování", - "themePreview": "Náhled vzhledu", - "theme": "Téma", - "fontFamily": "Rodina písma", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Mezery písmen", - "lineHeight": "Výška řádku", - "cursorStyle": "Styl kurzoru", - "cursorBlink": "Blikání kurzoru", - "scrollbackBuffer": "Posunovací vyrovnávací paměť", - "bellStyle": "Styl zvonku", - "rightClickSelectsWord": "Slovo vybere pravým tlačítkem", - "fastScrollModifier": "Rychlý modifikátor rolování", - "fastScrollSensitivity": "Citlivost rychlého rolování", - "sshAgentForwarding": "SSH Agent přesměrování", - "backspaceMode": "Režim Backspace", - "startupSnippet": "Spustit úryvek", - "selectSnippet": "Vybrat úryvek", - "forceKeyboardInteractive": "Vynutit interaktivní klávesnici", - "overrideCredentialUsername": "Přepsat pověření uživatelské jméno", - "overrideCredentialUsernameDesc": "Použij výše uvedené uživatelské jméno místo uživatelského jména", - "jumpHostChain": "Poštovní řetězec skoku", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Přidat port", - "addProxyNode": "Přidat uzel", - "proxyNode": "Proxy uzel", - "proxyType": "Typ proxy", - "quickActions": "Rychlé akce", - "sudoPasswordAutoFill": "Sudo heslo automatické vyplňování", - "sudoPassword": "Sudo heslo", - "keepaliveInterval": "Interval Keepalive (ms)", - "moshCommand": "Příkaz MOSH", - "environmentVariables": "Proměnné prostředí", - "addVariable": "Přidat proměnnou", - "docker": "Dokovací modul", - "copyTerminalUrl": "Kopírovat URL terminálu", - "copyFileManagerUrl": "Kopírovat URL správce souborů", - "copyRemoteDesktopUrl": "Kopírovat URL vzdálené plochy", - "failedToConnect": "Nepodařilo se připojit k konzoli", - "connect": "Připojit", - "disconnect": "Odpojit", - "start": "Začít", - "enableStatusCheck": "Povolit kontrolu stavu", - "enableMetrics": "Povolit metriku", - "bulkUpdateFailed": "Hromadná aktualizace selhala", - "selectAll": "Vybrat vše", - "deselectAll": "Zrušit výběr všech", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Bezpečná skořepina", - "virtualNetwork": "Virtuální síť", - "unencryptedShell": "Nešifrovaný shell", - "addressIp": "Adresa / IP", - "friendlyName": "Název přátel", - "folderAndAdvanced": "Složka & Rozšířené", - "privateNotes": "Soukromé poznámky", - "privateNotesPlaceholder": "Podrobnosti o tomto serveru...", - "pinToTop": "Připnout nahoru", - "pinToTopDesc": "Vždy zobrazit tohoto hostitele v horní části seznamu", - "portKnockingSequence": "Posloupnost portu Knocking", - "addKnockBtn": "Přidat Knock", - "noPortKnocking": "Není nakonfigurován žádný port.", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Zpoždění po (ms)", - "useSocks5Proxy": "Použít SOCKS5 Proxy", - "useSocks5ProxyDesc": "Trasa připojení přes proxy server", - "proxyHost": "Proxy server", - "proxyPort": "Port proxy serveru", - "proxyUsername": "Proxy uživatelské jméno", - "proxyPassword": "Proxy heslo", - "proxySingleMode": "Jediný proxy", - "proxyChainMode": "Proxy řetězec", - "you": "Vy", - "jumpHostChainLabel": "Poštovní řetězec skoku", - "addJumpBtn": "Přidat skok", - "noJumpHosts": "Není nakonfigurován žádný skokový hostitel.", - "selectAServer": "Vyberte server...", - "sshPort": "SSH port", - "authMethod": "Metoda ověření", - "storedCredential": "Uložený autor", - "selectACredential": "Vyberte pověření...", - "keyTypeLabel": "Typ klíče", - "keyTypeAuto": "Automatické rozpoznání", - "keyPasteTab": "Vložit", - "keyUploadTab": "Nahrát", - "keyFileLoaded": "Klíčový soubor byl načten", - "keyUploadClick": "Klikněte pro nahrání .pem / .key / .ppk", - "clearKey": "Vymazat klíč", - "keySaved": "SSH klíč uložen", - "keyReplaceNotice": "pro nahrazení vložte nový klíč níže", - "keyPassphraseSaved": "Heslo bylo uloženo, zadejte pro změnu", - "replaceKey": "Nahradit klíč", - "forceKeyboardInteractiveLabel": "Vynutit interaktivitu klávesnice", - "forceKeyboardInteractiveShortDesc": "Vynutit zadání manuálního hesla, i když jsou přítomny klíče", - "terminalAppearance": "Vzhled terminálu", - "colorTheme": "Barevný motiv", - "fontFamilyLabel": "Rodina písma", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Styl kurzoru", - "letterSpacingPx": "Mezery písmen (px)", - "lineHeightLabel": "Výška řádku", - "bellStyleLabel": "Styl zvonku", - "backspaceModeLabel": "Režim Backspace", - "cursorBlinking": "Blikání kurzoru", - "cursorBlinkingDesc": "Povolit blikání animace pro koncový kurzor", - "rightClickSelectsWordLabel": "Vybere slovo pravým tlačítkem myši", - "rightClickSelectsWordShortDesc": "Vyberte slovo pod kurzorem na pravé tlačítko myši", - "behaviorAndAdvanced": "Chování a pokročilé", - "scrollbackBufferLabel": "Posunovací vyrovnávací paměť", - "scrollbackMaxLines": "Maximální počet řádků v historii", - "sshAgentForwardingLabel": "SSH Agent přesměrování", - "sshAgentForwardingShortDesc": "Předat lokální SSH klíče tomuto hostiteli", - "enableAutoMosh": "Povolit Auto-Mosh", - "enableAutoMoshDesc": "Preferovat Mosh nad SSH, pokud je k dispozici", - "enableAutoTmux": "Povolit Auto-Tmux", - "enableAutoTmuxDesc": "Automaticky spustit nebo připojit k tmux relaci", - "sudoPasswordAutoFillLabel": "Automatické vyplnění hesel Sudo", - "sudoPasswordAutoFillShortDesc": "Automaticky zadat heslo při vyzvání", - "sudoPasswordLabel": "Sudo heslo", - "environmentVariablesLabel": "Proměnné prostředí", - "addVariableBtn": "Přidat proměnnou", - "noEnvVars": "Nejsou nastaveny žádné proměnné prostředí.", - "fastScrollModifierLabel": "Rychlý modifikátor rolování", - "fastScrollSensitivityLabel": "Citlivost rychlého rolování", - "moshCommandLabel": "Příkaz Mosh", - "startupSnippetLabel": "Spustit snippet", - "keepaliveIntervalLabel": "Interval udržování aktivity (sekundy)", - "maxKeepaliveMisses": "Maximální počet neúspěšných", - "tunnelSettings": "Nastavení tunelu", - "enableTunneling": "Povolit tunely", - "enableTunnelingDesc": "Povolit SSH tunelu pro tohoto hostitele", - "serverTunnelsSection": "Tuny serveru", - "addTunnelBtn": "Přidat tunel", - "noTunnelsConfigured": "Žádné tunely nastaveny.", - "tunnelLabel": "Tunel {{number}}", - "tunnelType": "Typ tunelu", - "tunnelModeLocalDesc": "Přeposlat lokální port na port na vzdáleném serveru (nebo hostiteli dosažitelný).", - "tunnelModeRemoteDesc": "Přeposlat port na vzdálený server zpět na místní port na vašem počítači.", - "tunnelModeDynamicDesc": "Vytvořte SOCKS5 proxy na místním portu pro dynamické přesměrování portů.", - "sameHost": "Tento hostitel (přímý tunel)", - "endpointHost": "Hostitel koncových bodů", - "endpointPort": "Port koncového bodu", - "bindHost": "Svázat hostitele", - "sourcePort": "Zdrojový port", - "maxRetries": "Maximální počet opakování", - "retryIntervalS": "Opakovat interval (s)", - "autoStartLabel": "Automatické spuštění", - "autoStartDesc": "Automaticky připojit tento tunel po načtení hostitele", - "tunnelConnecting": "Připojování tunelu...", - "tunnelDisconnected": "Tunel odpojen", - "failedToConnectTunnel": "Připojení se nezdařilo", - "failedToDisconnectTunnel": "Nepodařilo se odpojit", - "dockerIntegration": "Integrace Dockeru", - "enableDockerMonitor": "Povolit Docker", - "enableDockerMonitorDesc": "Sledovat a spravovat kontejnery na tomto hostiteli pomocí Dockeru", - "enableFileManagerMonitor": "Povolit správce souborů", - "enableFileManagerMonitorDesc": "Procházet a spravovat soubory na tomto hostiteli přes SFTP", - "defaultPathLabel": "Výchozí cesta", - "fileManagerPathHint": "Adresář, který se otevře při spuštění správce souborů pro tohoto hostitele.", - "statusChecksLabel": "Kontrola stavu", - "enableStatusChecks": "Povolit kontrolu stavu", - "enableStatusChecksDesc": "Pravidelně ping tohoto hostitele k ověření dostupnosti", - "useGlobalInterval": "Použít globální Interval", - "useGlobalIntervalDesc": "Přepsat interval kontroly stavu na serveru", - "checkIntervalS": "Interval kontroly (s)", - "checkIntervalDesc": "Sekund mezi každým pingem připojení", - "metricsCollectionLabel": "Metrika kolekce", - "enableMetricsLabel": "Povolit metriku", - "enableMetricsDesc": "Sbírat CPU, RAM, disky a využití sítě od tohoto hostitele", - "useGlobalMetrics": "Použít globální Interval", - "useGlobalMetricsDesc": "Přepsat metrický interval celého serveru", - "metricsIntervalS": "Interval (intervaly)", - "metricsIntervalDesc2": "Vteřiny mezi metrickými snímky", - "visibleWidgets": "Viditelné widgety", - "cpuUsageLabel": "Využití CPU", - "cpuUsageDesc": "Procenta CPU, průměrné zatížení, sparkline graf", - "memoryLabel": "Využití paměti", - "memoryDesc": "Použití RAM, výměna v mezipaměti", - "storageLabel": "Využití disku", - "storageDesc": "Využití disku na přípojný bod", - "networkLabel": "Síťová rozhraní", - "networkDesc": "Seznam rozhraní a šířka pásma", - "uptimeLabel": "Doba provozu", - "uptimeDesc": "Doba provozu a doba spuštění systému", - "systemInfoLabel": "Systémové informace", - "systemInfoDesc": "OS, jádro, název hostitele, architektura", - "recentLoginsLabel": "Poslední přihlášení", - "recentLoginsDesc": "Úspěšné a neúspěšné přihlášení", - "topProcessesLabel": "Nejlepší procesy", - "topProcessesDesc": "PID, CPU%, MEM%, příkaz", - "listeningPortsLabel": "Přístavy poslech", - "listeningPortsDesc": "Otevřít porty s procesem a stavem", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, status SELinux", - "quickActionsLabel": "Rychlé akce", - "quickActionsToolbar": "Rychlé akce se zobrazí jako tlačítka ve stavové liště serveru pro provedení příkazu jedním kliknutím.", - "noQuickActions": "Zatím žádné rychlé akce.", - "buttonLabel": "Popisek tlačítka", - "selectSnippetPlaceholder": "Vybrat snippet...", - "addActionBtn": "Přidat akci", - "hostSharedSuccessfully": "Hostitel úspěšně sdílen", - "failedToShareHost": "Nepodařilo se sdílet hostitele", - "accessRevoked": "Přístup zrušen", - "failedToRevokeAccess": "Nepodařilo se zrušit přístup", - "cancelBtn": "Zrušit", - "savingBtn": "Ukládání...", - "addHostBtn": "Přidat hostitele", - "hostUpdated": "Hostitel byl aktualizován", - "hostCreated": "Hostitel vytvořen", - "failedToSave": "Nepodařilo se uložit hostitele", - "credentialUpdated": "Útvar aktualizován", - "credentialCreated": "Pověření vytvořeno", - "failedToSaveCredential": "Nepodařilo se uložit pověření", - "backToHosts": "Zpět na hostitele", - "backToCredentials": "Zpět na pověření", - "pinned": "Připnuté", - "noHostsFound": "Nenalezeny žádné hostitele", - "tryDifferentTerm": "Vyzkoušejte jiný výraz", - "addFirstHost": "Pro začátek přidejte svého prvního hostitele", - "noCredentialsFound": "Nebyly nalezeny žádné přihlašovací údaje", - "addCredentialBtn": "Přidat pověření", - "updateCredentialBtn": "Aktualizovat pověření", - "features": "Vlastnosti", - "noFolder": "(Žádná složka)", - "deleteSelected": "Vymazat", - "exitSelection": "Ukončit výběr", - "importSkip": "Import (existující přeskočení)", - "importOverwrite": "Importovat (přepsat)", - "collapseBtn": "Sbalit", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", "importExportBtn": "Import / Export", - "hostStatusesRefreshed": "Stav hostitele obnoven", - "failedToRefreshHosts": "Nepodařilo se obnovit hostitele", - "movedHostTo": "Přesunuto {{host}} do \"{{folder}}\"", - "failedToMoveHost": "Přesun hostitele se nezdařil", - "folderRenamedTo": "Složka přejmenována na \"{{name}}\"", - "deletedFolder": "Smazaná složka \"{{name}}\"", - "failedToDeleteFolder": "Nepodařilo se odstranit složku", - "deleteAllInFolder": "Odstranit všechny hostitele v \"{{name}}\"? Tuto akci nelze vrátit zpět.", - "deletedHost": "Smazáno {{name}}", - "copiedToClipboard": "Zkopírováno do schránky", - "terminalUrlCopied": "URL adresa terminálu byla zkopírována", - "fileManagerUrlCopied": "URL správce souborů zkopírováno", - "tunnelUrlCopied": "URL tunelu zkopírována", - "dockerUrlCopied": "Adresa URL doku zkopírována", - "serverStatsUrlCopied": "URL statistiky serveru zkopírováno", - "rdpUrlCopied": "RDP URL zkopírováno", - "vncUrlCopied": "VNC URL zkopírováno", - "telnetUrlCopied": "URL adresa Telnet zkopírována", - "remoteDesktopUrlCopied": "URL vzdálené plochy zkopírována", - "expandActions": "Rozbalit akce", - "collapseActions": "Akce sbalení", - "wakeOnLanAction": "Probuzení přes LAN", - "wakeOnLanSuccess": "Magický balíček odeslán na {{name}}", - "wakeOnLanError": "Nepodařilo se odeslat magický paket", - "cloneHostAction": "Klonovat hostitele", - "copyAddress": "Kopírovat adresu", - "copyLink": "Kopírovat odkaz", - "copyTerminalUrlAction": "Kopírovat URL terminálu", - "copyFileManagerUrlAction": "Kopírovat URL správce souborů", - "copyTunnelUrlAction": "Kopírovat URL tunelu", - "copyDockerUrlAction": "Kopírovat URL Dockeru", - "copyServerStatsUrlAction": "Kopírovat URL statistiky serveru", - "copyRdpUrlAction": "Kopírovat URL RDP", - "copyVncUrlAction": "Kopírovat VNC URL", - "copyTelnetUrlAction": "Kopírovat URL Telnet", - "copyRemoteDesktopUrlAction": "Kopírovat URL vzdálené plochy", - "deleteCredentialConfirm": "Smazat pověření \"{{name}}\"?", - "deletedCredential": "Smazáno {{name}}", - "deploySSHKeyTitle": "Nasadit SSH klíč", - "deployingBtn": "Nasazování...", - "deployBtn": "Publikovat", - "failedToDeployKey": "Nepodařilo se nasadit klíč", - "deleteHostsConfirm": "Odstranit {{count}} hostitele{{plural}}? Tuto akci nelze vrátit zpět.", - "movedToRoot": "Přesunuto do root", - "failedToMoveHosts": "Přesunutí hostitelů se nezdařilo", - "enableTerminalFeature": "Povolit terminál", - "disableTerminalFeature": "Zakázat terminál", - "enableFilesFeature": "Povolit soubory", - "disableFilesFeature": "Zakázat soubory", - "enableTunnelsFeature": "Povolit tunely", - "disableTunnelsFeature": "Zakázat tunely", - "enableDockerFeature": "Povolit Docker", - "disableDockerFeature": "Zakázat Docker", - "addTagsPlaceholder": "Přidat štítky...", - "authDetails": "Podrobnosti o ověření", - "credType": "Typ", - "generateKeyPairDesc": "Generovat nové páry klíčů, soukromé i veřejné klíče budou vyplněny automaticky.", - "generatingKey": "Generování", - "generateLabel": "Generovat {{label}}", - "uploadFileBtn": "Nahrát soubor", - "keyPassphraseOptional": "Heslo klíče (volitelné)", - "sshPublicKeyOptional": "Veřejný klíč SSH (nepovinný)", - "publicKeyGenerated": "Vygenerovaný veřejný klíč", - "failedToGeneratePublicKey": "Nepodařilo se odvodit veřejný klíč", - "publicKeyCopied": "Veřejný klíč byl zkopírován", - "keyPairGenerated": "{{label}} vygenerovaný pár klíčů", - "failedToGenerateKeyPair": "Nepodařilo se vygenerovat pár klíčů", - "searchHostsPlaceholder": "Hledat hostitele, adresy, značky…", - "searchCredentialsPlaceholder": "Hledat přístupové údaje…", - "refreshBtn": "Aktualizovat", - "addTag": "Přidat štítky...", - "deleteConfirmBtn": "Vymazat", - "tunnelRequirementsText": "Server SSH musí mít GatewayPorts ano, AllowTcpForwarding ano a PermitRootLogin yes nastaveno v /etc/ssh/sshd_config.", - "deleteHostConfirm": "Smazat \"{{name}}\"?", - "enableAtLeastOneProtocol": "Povolte alespoň jeden protokol výše pro nastavení ověřování a připojení.", - "keyPassphrase": "Heslo klíče", - "connectBtn": "Připojit", - "disconnectBtn": "Odpojit", - "basicInformation": "Základní informace", - "authDetailsSection": "Podrobnosti o ověření", - "credTypeLabel": "Typ", - "hostsTab": "Hostitelé", - "credentialsTab": "Pověření", - "selectMultiple": "Vybrat více", - "selectHosts": "Vybrat hostitele", - "connectionLabel": "Připojení", - "authenticationLabel": "Ověření", - "generateKeyPairTitle": "Generovat párování klíče", - "generateKeyPairDescription": "Generovat nové páry klíčů, soukromé i veřejné klíče budou vyplněny automaticky.", - "generateFromPrivateKey": "Generovat ze soukromého klíče", - "refreshBtn2": "Aktualizovat", - "exitSelectionTitle": "Ukončit výběr", - "exportAll": "Exportovat vše", - "addHostBtn2": "Přidat hostitele", - "addCredentialBtn2": "Přidat pověření", - "checkingHostStatuses": "Kontrola stavu hostitele...", - "pinnedSection": "Připnuté", - "hostsExported": "Hostitelé úspěšně exportováni", - "exportFailed": "Export hostitelů se nezdařilo", - "sampleDownloaded": "Ukázkový soubor stažen", - "failedToDeleteCredential2": "Nepodařilo se odstranit pověření", - "noFolderOption": "(Žádná složka)", - "nSelected": "{{count}} vybrán", - "featuresMenu": "Vlastnosti", - "moveMenu": "Přesunout", - "cancelSelection": "Zrušit", - "deployDialogDesc": "Nasadit {{name}} do autorizovaných klíčů hostitele.", - "targetHostLabel": "Hostitel cíle", - "selectHostOption": "Vyberte hostitele...", - "keyDeployedSuccess": "Klíč byl úspěšně vložen", - "failedToDeployKey2": "Nepodařilo se nasadit klíč", - "deletedCount": "Smazáno {{count}} hostitelů", - "failedToDeleteCount": "Nepodařilo se odstranit {{count}} hostitele", - "duplicatedHost": "Duplikováno \"{{name}} \" \"", - "failedToDuplicateHost": "Duplikace hostitele se nezdařila", - "updatedCount": "Aktualizováno {{count}} hostitelů", - "friendlyNameLabel": "Název přátel", - "descriptionLabel": "L 343, 22.12.2009, s. 1).", - "loadingHost": "Načítání hostitele...", - "loadingHosts": "Načítání hostitelů...", - "loadingCredentials": "Načítání pověření...", - "noHostsYet": "Zatím žádné hostitele", - "noHostsMatchSearch": "Žádné hostitele neodpovídají vašemu hledání", - "hostNotFound": "Hostitel nenalezen", - "searchHosts": "Hledat v hostech...", - "sortHosts": "Seřadit hostitele", - "sortDefault": "Výchozí pořadí", - "sortNameAsc": "Jméno (A → Z)", - "sortNameDesc": "Jméno (Z → A)", - "sortIpAsc": "IP adresa (vzestupně)", - "sortIpDesc": "IP adresa (popis)", - "sortOnlineFirst": "Online první", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Připnuto jako první", - "filterHosts": "Filtrovat hostitele", - "filterClearAll": "Vymazat filtry", - "filterStatusGroup": "Postavení", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Připnuto", - "filterAuthGroup": "Typ autorizace", - "filterAuthPassword": "Heslo", - "filterAuthKey": "SSH klíč", - "filterAuthCredential": "Pověření", - "filterAuthNone": "Žádný", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokol", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "PRV", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Funkce", - "filterFeatureTerminal": "Terminál", - "filterFeatureFileManager": "Správce souborů", - "filterFeatureTunnel": "Tunel", - "filterFeatureDocker": "Přístavní dělník", - "filterTagsGroup": "Štítky", - "shareHost": "Sdílet hostitele", - "shareHostTitle": "Sdílet: {{name}}", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Tento hostitel musí použít pověření k povolení sdílení. Upravte hostitele a nejprve přiřaďte pověření jako první." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Připojení", - "authentication": "Ověření", - "connectionSettings": "Nastavení připojení", - "displaySettings": "Nastavení zobrazení", - "audioSettings": "Nastavení zvuku", - "rdpPerformance": "Výkon RDP", - "deviceRedirection": "Přesměrování zařízení", - "session": "Relace", - "gateway": "Brána", - "remoteApp": "Vzdálená aplikace", - "clipboard": "Schránka", - "sessionRecording": "Záznam relace", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Nastavení VNC", - "terminalSettings": "Nastavení terminálu", - "rdpPort": "RDP port", - "username": "Uživatelské jméno", - "password": "Heslo", - "domain": "Doména", - "securityMode": "Bezpečnostní režim", - "colorDepth": "Hloubka barev", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Výška", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Metoda změny velikosti", - "clientName": "Název klienta", - "initialProgram": "Počáteční program", - "serverLayout": "Rozložení serveru", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Port brány", - "gatewayUsername": "Uživatelské jméno brány", - "gatewayPassword": "Heslo brány", - "gatewayDomain": "Doména brány", - "remoteAppProgram": "Program RemoteApp", - "workingDirectory": "Pracovní adresář", - "arguments": "Argumenty", - "normalizeLineEndings": "Normalizovat konce řádků", - "recordingPath": "Cesta k nahrávání", - "recordingName": "Název záznamu", - "macAddress": "MAC adresa", - "broadcastAddress": "Vysílací adresa", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Čas čekání (s)", - "driveName": "Název disku", - "drivePath": "Cesta k disku", - "ignoreCertificate": "Ignorovat certifikát", - "ignoreCertificateDesc": "Povolit spojení hostiteli s certifikáty podepsanými vlastními", - "forceLossless": "Vynutit ztrátu", - "forceLosslessDesc": "Vynutit bezztrátové kódování obrázků (vyšší kvalita, větší šířka pásma)", - "disableAudio": "Zakázat zvuk", - "disableAudioDesc": "Ztlumit všechny zvuky ze vzdálené relace", - "enableAudioInput": "Povolit vstup zvuku (Micropfon)", - "enableAudioInputDesc": "Přeposlat lokální mikrofon do vzdálené relace", - "wallpaper": "Tapeta", - "wallpaperDesc": "Zobrazit tapetu plochy (vypnutí zlepšuje výkon)", - "theming": "Motivy", - "themingDesc": "Povolit vizuální motivy a styly", - "fontSmoothing": "Vyhlazování písma", - "fontSmoothingDesc": "Povolit vykreslování písma ClearType", - "fullWindowDrag": "Přetažení celého okna", - "fullWindowDragDesc": "Zobrazit obsah okna při přetažení", - "desktopComposition": "Složení plochy", - "desktopCompositionDesc": "Povolit efekty Aero skla", - "menuAnimations": "Animace menu", - "menuAnimationsDesc": "Povolit prolínání nabídky a animace snímků", - "disableBitmapCaching": "Vypnout mezipaměť bitmap", - "disableBitmapCachingDesc": "Vypnout vyrovnávací paměť bitmap (může pomoci s rukávy)", - "disableOffscreenCaching": "Zakázat ukládání do mezipaměti mimo obrazovku", - "disableOffscreenCachingDesc": "Vypnout vyrovnávací paměť mimo obrazovku", - "disableGlyphCaching": "Zakázat ukládání do mezipaměti Glyph", - "disableGlyphCachingDesc": "Vypnout cache glyph", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Použít vzdálené grafické potrubí", - "enablePrinting": "Povolit tisk", - "enablePrintingDesc": "Přesměrovat místní tiskárny na vzdálenou relaci", - "enableDriveRedirection": "Povolit přesměrování disku", - "enableDriveRedirectionDesc": "Mapa místní složky jako disk ve vzdálené relaci", - "createDrivePath": "Vytvořit cestu k disku", - "createDrivePathDesc": "Automaticky vytvořit složku, pokud neexistuje", - "disableDownload": "Zakázat stahování", - "disableDownloadDesc": "Zabránit stahování souborů ze vzdálené relace", - "disableUpload": "Zakázat nahrávání", - "disableUploadDesc": "Zabránit nahrávání souborů do vzdálené relace", - "enableTouch": "Povolit dotyk", - "enableTouchDesc": "Povolit přesměrování dotyku", - "consoleSession": "Relace konzole", - "consoleSessionDesc": "Připojit ke konzoli (relace 0) místo nové relace", - "sendWolPacket": "Poslat WOL paket", - "sendWolPacketDesc": "Poslat kouzelný paket probudit tohoto hostitele před připojením", - "disableCopy": "Zakázat kopírování", - "disableCopyDesc": "Zabránit kopírování textu ze vzdálené relace", - "disablePaste": "Zakázat vložení", - "disablePasteDesc": "Zabránit vkládání textu do vzdálené relace", - "createPathIfMissing": "Vytvořit cestu, pokud chybí", - "createPathIfMissingDesc": "Automaticky vytvořit adresář pro nahrávání", - "excludeOutput": "Vynechat výstup", - "excludeOutputDesc": "Nezaznamenávat výstup obrazovky (pouze metadata)", - "excludeMouse": "Vyloučit myš", - "excludeMouseDesc": "Nezaznamenávat pohyby myší", - "includeKeystrokes": "Zahrnout čáry kláves", - "includeKeystrokesDesc": "Zaznamenávat kromě výstupu obrazovky tahy kláves", - "vncPort": "VNC port", - "vncPassword": "VNC heslo", - "vncUsernameOptional": "Uživatelské jméno (volitelné)", - "vncLeaveBlank": "Ponechte prázdné, pokud není požadováno", - "cursorMode": "Režim kurzoru", - "swapRedBlue": "Prohodit červenou/modrou", - "swapRedBlueDesc": "Prohodit kanály červené a modré barvy (opraví některé problémy s barvou)", - "readOnly": "Pouze pro čtení", - "readOnlyDesc": "Zobrazit vzdálenou obrazovku bez odeslání vstupu", - "telnetPort": "Port Telnet", - "terminalType": "Typ terminálu", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Barevné schéma", - "backspaceKey": "Klíč Backspace", - "saveHostFirst": "Nejdříve uložte hostitele.", - "sharingOptionsAfterSave": "Možnosti sdílení jsou dostupné po uložení hostitele.", - "sharingLoadError": "Nepodařilo se načíst sdílená data. Zkontrolujte připojení a zkuste to znovu.", - "shareHostSection": "Sdílet hostitele", - "shareWithUser": "Sdílet s uživatelem", - "shareWithRole": "Sdílet s rolí", - "selectUser": "Vybrat uživatele", - "selectRole": "Vybrat roli", - "selectUserOption": "Vyberte uživatele...", - "selectRoleOption": "Vyberte roli...", - "permissionLevel": "Úroveň oprávnění", - "expiresInHours": "Vyprší za hodiny", - "noExpiryPlaceholder": "Ponechte prázdné pro vypršení platnosti", - "shareBtn": "Sdílet", - "currentAccess": "Aktuální přístup", - "typeHeader": "Typ", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Právo", - "grantedByHeader": "Uděleno", - "expiresHeader": "Vyprší", - "noAccessEntries": "Zatím žádné přístupové položky.", - "expiredLabel": "Vypršela platnost", - "neverLabel": "Nikdy", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Zrušit", - "savingBtn": "Ukládání...", - "updateHostBtn": "Hostitel aktualizace", - "addHostBtn": "Přidat hostitele" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Hledat hostitele, příkazy nebo nastavení...", - "quickActions": "Rychlé akce", - "hostManager": "Správce hostitelů", - "hostManagerDesc": "Spravovat, přidávat nebo upravovat hostitele", - "addNewHost": "Přidat nového hostitele", - "addNewHostDesc": "Registrovat nového hostitele", - "adminSettings": "Nastavení správce", - "adminSettingsDesc": "Konfigurace systémových preferencí a uživatelů", - "userProfile": "Profil uživatele", - "userProfileDesc": "Spravujte svůj účet a nastavení", - "addCredential": "Přidat pověření", - "addCredentialDesc": "Uložit SSH klíče nebo hesla", - "recentActivity": "Nedávná aktivita", - "serversAndHosts": "Servery a hostitelé", - "noHostsFound": "Nenalezen žádný hostitel \"{{search}}\"", - "links": "Odkazy", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Vybrat", - "toggleWith": "Přepnout na" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Žádná karta nebyla přiřazena", - "focusedPane": "Aktivní panel" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Žádné spojení", - "noConnectionsDesc": "Otevřete terminál, správce souborů nebo vzdálenou plochu a zobrazte zde připojení.", - "connectedFor": "Připojeno pro {{duration}}", - "connected": "Připojeno", - "disconnected": "Odpojeno", - "closeTab": "Zavřít kartu", - "closeConnection": "Zavřete spojení", - "forgetTab": "Zapomenout", - "removeBackground": "Odstranit", - "reconnect": "Znovu se připojte", - "reopenTab": "Znovu otevřít", - "sectionOpen": "OTEVŘENO", - "sectionBackground": "Pozadí", - "backgroundDesc": "Relace přetrvávají 30 minut po odpojení a lze je znovu připojit.", - "persisted": "Přetrvává v pozadí", - "expiresIn": "Platnost vyprší za {{duration}}", - "search": "Hledat spojení...", - "noSearchResults": "Vašemu vyhledávání neodpovídají žádná spojení" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Připojování k {{type}} relaci...", - "connectionError": "Chyba připojení", - "connectionFailed": "Připojení se nezdařilo", - "failedToConnect": "Nepodařilo se získat token pro připojení", - "hostNotFound": "Hostitel nenalezen", - "noHostSelected": "Nebyl vybrán žádný hostitel", - "reconnect": "Znovu připojit", - "retry": "Opakovat", - "guacdUnavailable": "Vzdálená stolní služba (guacd) není k dispozici. Ujistěte se, že guacd běží a je správně dostupný a konfigurovaný v nastavení správce.", - "ctrlAltDel": "Ctrl + Alt+Del", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl + Alt+Del", - "winL": "Win+L (nárazová obrazovka)", - "winKey": "Windows klíč", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Přesunout", - "win": "Výhra", - "stickyActive": "{{key}} (zavřeno - klikněte pro vydání)", - "stickyInactive": "{{key}} (klikněte pro lati)", - "esc": "Úniky", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Domů", - "end": "Ukončit", - "pageUp": "Stránka nahoru", - "pageDown": "Stránka dolů", - "arrowUp": "Šipka nahoru", - "arrowDown": "Šipka dolů", - "arrowLeft": "Šipka vlevo", - "arrowRight": "Šipka vpravo", - "fnToggle": "Funkční klíče", - "reconnect": "Znovu připojit relaci", - "collapse": "Sbalit panel nástrojů", - "expand": "Rozbalit panel nástrojů", - "dragHandle": "Přetažením změnit pozici" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Připojit k hostiteli", - "clear": "Vyčistit", - "paste": "Vložit", - "reconnect": "Znovu připojit", - "connectionLost": "Spojení ztraceno", - "connected": "Připojeno", - "clipboardWriteFailed": "Kopírování do schránky se nezdařilo. Ujistěte se, že stránka je vedena přes HTTPS nebo localhost.", - "clipboardReadFailed": "Nepodařilo se přečíst ze schránky. Ujistěte se, že máte oprávnění na schránku.", - "clipboardHttpWarning": "Vložit vyžaduje HTTPS. Použijte Ctrl+Shift+V nebo použijte Termix přes HTTPS.", - "unknownError": "Došlo k neznámé chybě", - "websocketError": "Chyba připojení k WebSocket", - "connecting": "Připojování...", - "noHostSelected": "Nebyl vybrán žádný hostitel", - "reconnecting": "Znovu se připojuji... ({{attempt}}/{{max}})", - "reconnected": "Znovu připojeno", - "tmuxSessionCreated": "Tmux relace vytvořena: {{name}}", - "tmuxSessionAttached": "připojena relace tmuxu: {{name}}", - "tmuxUnavailable": "tmux není na vzdáleném hostiteli nainstalován, vrátí se zpět na standardní shell", - "tmuxSessionPickerTitle": "tmux relace", - "tmuxSessionPickerDesc": "Existující tmux relace nalezeny na tomto hostiteli. Vyberte jednu pro připojení nebo vytvoření nové relace.", - "tmuxWindows": "Okna", - "tmuxWindowCount": "Okno {{count}}", - "tmuxAttached": "Připojení klienti", - "tmuxAttachedCount": "{{count}} připojeno", - "tmuxLastActivity": "Poslední aktivita", - "tmuxTimeJustNow": "právě teď", - "tmuxTimeMinutes": "{{count}}před m", - "tmuxTimeHours": "{{count}}před hodinami", - "tmuxTimeDays": "Před {{count}}dny", - "tmuxCreateNew": "Začít novou relaci", - "tmuxCopyHint": "Upravte výběr a stiskněte klávesu Enter pro kopírování do schránky", - "tmuxDetach": "Odpojit od tmux relace", - "tmuxDetached": "Odpojeno od tmux relace", - "maxReconnectAttemptsReached": "Dosažen maximální počet pokusů o opětovné připojení", - "closeTab": "Zavřít", - "connectionTimeout": "Časový limit připojení", - "terminalTitle": "Terminál - {{host}}", - "terminalWithPath": "Terminál - {{host}}:{{path}}", - "runTitle": "Běží {{command}} - {{host}}", - "totpRequired": "Vyžadováno dvoufaktorové ověření", - "totpCodeLabel": "Ověřovací kód", - "totpVerify": "Ověřit", - "warpgateAuthRequired": "Vyžadováno ověření Warpgate", - "warpgateSecurityKey": "Bezpečnostní klíč", - "warpgateAuthUrl": "Ověřovací URL", - "warpgateOpenBrowser": "Otevřít v prohlížeči", - "warpgateContinue": "Dokončil jsem ověření", - "opksshAuthRequired": "Vyžadováno ověření OPKSSH", - "opksshAuthDescription": "Dokončete ověření ve vašem prohlížeči, abyste mohli pokračovat. Tato relace zůstane v platnosti 24 hodin.", - "opksshOpenBrowser": "Otevřete prohlížeč pro ověření", - "opksshWaitingForAuth": "Čekání na ověřování v prohlížeči...", - "opksshAuthenticating": "Zpracovávání ověřování...", - "opksshTimeout": "Vypršel časový limit ověření. Zkuste to prosím znovu.", - "opksshAuthFailed": "Ověření se nezdařilo. Zkontrolujte prosím vaše přihlašovací údaje a zkuste to znovu.", - "opksshSignInWith": "Přihlásit se pomocí {{provider}}", - "sudoPasswordPopupTitle": "Vložit heslo?", - "websocketAbnormalClose": "Připojení bylo neočekávaně uzavřeno. Může to být způsobeno problémy s konfigurací reverzního proxy nebo SSL. Zkontrolujte protokoly serveru.", - "connectionLogTitle": "Protokol připojení", - "connectionLogCopy": "Kopírovat protokoly do schránky", - "connectionLogEmpty": "Zatím žádné protokoly připojení", - "connectionLogWaiting": "Čekání na protokoly připojení...", - "connectionLogCopied": "Záznamy kontaktů zkopírovány do schránky", - "connectionLogCopyFailed": "Kopírování protokolů do schránky se nezdařilo", - "connectionRejected": "Připojení odmítnuto serverem. Zkontrolujte prosím ověření a nastavení sítě.", - "hostKeyRejected": "Ověření SSH hostitele bylo zamítnuto. Připojení bylo zrušeno.", - "sessionTakenOver": "Relace byla otevřena v jiné kartě. Znovu připojuji..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nebyl vybrán žádný hostitel", - "initializingEditor": "Inicializace editoru...", - "file": "Soubor", - "folder": "Složka", - "uploadFile": "Nahrát soubor", - "downloadFile": "Stáhnout", - "extractArchive": "Rozbalit archiv", - "extractingArchive": "Rozbaluji {{name}}...", - "archiveExtractedSuccessfully": "{{name}} byla úspěšně extrahována", - "extractFailed": "Extrahování se nezdařilo", - "compressFile": "Komprimovat soubor", - "compressFiles": "Komprimovat soubory", - "compressFilesDesc": "Komprimovat {{count}} položky do archivu", - "archiveName": "Název archivu", - "enterArchiveName": "Zadejte název archivu...", - "compressionFormat": "Formát komprese", - "selectedFiles": "Vybrané soubory", - "andMoreFiles": "a {{count}} další...", - "compress": "Komprimovat", - "compressingFiles": "Komprimování {{count}} položek do {{name}}...", - "filesCompressedSuccessfully": "{{name}} úspěšně vytvořen", - "compressFailed": "Komprese selhala", - "edit": "Upravit", - "preview": "Náhled", - "previous": "Předchozí", - "next": "Další", - "pageXOfY": "Strana {{current}} z {{total}}", - "zoomOut": "Oddálit", - "zoomIn": "Přiblížit", - "newFile": "Nový soubor", - "newFolder": "Nová složka", - "rename": "Přejmenovat", - "uploading": "Nahrávám...", - "uploadingFile": "Nahrávání {{name}}...", - "fileName": "Název souboru", - "folderName": "Název složky", - "fileUploadedSuccessfully": "Soubor \"{{name}}\" byl úspěšně nahrán", - "failedToUploadFile": "Nepodařilo se nahrát soubor", - "fileDownloadedSuccessfully": "Soubor \"{{name}}\" byl úspěšně stažen", - "failedToDownloadFile": "Nepodařilo se stáhnout soubor", - "fileCreatedSuccessfully": "Soubor \"{{name}}\" byl úspěšně vytvořen", - "folderCreatedSuccessfully": "Složka \"{{name}}\" byla úspěšně vytvořena", - "failedToCreateItem": "Nepodařilo se vytvořit položku", - "operationFailed": "Operace {{operation}} se nezdařila pro {{name}}: {{error}}", - "failedToResolveSymlink": "Nepodařilo se vyřešit symbolický odkaz", - "itemsDeletedSuccessfully": "{{count}} položky byly úspěšně odstraněny", - "failedToDeleteItems": "Odstranění položek se nezdařilo", - "sudoPasswordRequired": "Vyžadováno heslo administrátora", - "enterSudoPassword": "Pro pokračování v této operaci zadejte heslo", - "sudoPassword": "Sudo heslo", - "sudoOperationFailed": "Operace Sudo selhala", - "sudoAuthFailed": "Sudo ověření se nezdařilo", - "dragFilesToUpload": "Pro nahrání přetáhněte soubory sem", - "emptyFolder": "Tato složka je prázdná", - "searchFiles": "Hledat soubory...", - "upload": "Nahrát", - "selectHostToStart": "Vyberte hostitele pro spuštění správy souborů", - "sshRequiredForFileManager": "Správce souborů vyžaduje SSH. Tento server nemá povoleno SSH.", - "failedToConnect": "Nepodařilo se připojit k SSH", - "failedToLoadDirectory": "Nepodařilo se načíst adresář", - "noSSHConnection": "Není k dispozici žádné SSH připojení", - "copy": "Kopírovat", - "cut": "Vyjmout", - "paste": "Vložit", - "copyPath": "Kopírovat cestu", - "copyPaths": "Kopírovat cesty", - "delete": "Vymazat", - "properties": "Vlastnosti", - "refresh": "Aktualizovat", - "downloadFiles": "Stáhnout {{count}} soubory do prohlížeče", - "copyFiles": "Kopírovat {{count}} položky", - "cutFiles": "Vyjmout {{count}} položek", - "deleteFiles": "Odstranit položky {{count}}", - "filesCopiedToClipboard": "{{count}} položky zkopírovány do schránky", - "filesCutToClipboard": "{{count}} položky oříznuty do schránky", - "pathCopiedToClipboard": "Cesta zkopírována do schránky", - "pathsCopiedToClipboard": "{{count}} cesty zkopírovány do schránky", - "failedToCopyPath": "Nepodařilo se zkopírovat cestu do schránky", - "movedItems": "Přesunuto {{count}} položek", - "failedToDeleteItem": "Odstranění položky se nezdařilo", - "itemRenamedSuccessfully": "{{type}} úspěšně přejmenován", - "failedToRenameItem": "Nepodařilo se přejmenovat položku", - "download": "Stáhnout", - "permissions": "Práva", - "size": "Velikost", - "modified": "Upraveno", - "path": "Cesta", - "confirmDelete": "Jste si jisti, že chcete odstranit {{name}}?", - "permissionDenied": "Oprávnění odepřeno", - "serverError": "Chyba serveru", - "fileSavedSuccessfully": "Soubor byl úspěšně uložen", - "failedToSaveFile": "Nepodařilo se uložit soubor", - "confirmDeleteSingleItem": "Jste si jisti, že chcete trvale odstranit \"{{name}}\"?", - "confirmDeleteMultipleItems": "Jste si jisti, že chcete trvale odstranit {{count}} položky?", - "confirmDeleteMultipleItemsWithFolders": "Opravdu chcete trvale odstranit {{count}} položky? To zahrnuje složky a jejich obsah.", - "confirmDeleteFolder": "Jste si jisti, že chcete trvale odstranit složku \"{{name}}\" a její obsah?", - "permanentDeleteWarning": "Tuto akci nelze vrátit zpět. Položka (položky) bude trvale odstraněna ze serveru.", - "recent": "Nedávné", - "pinned": "Připnuté", - "folderShortcuts": "Zástupce složky", - "failedToReconnectSSH": "Nepodařilo se znovu připojit SSH relaci", - "openTerminalHere": "Otevřít terminál", - "run": "Spustit", - "openTerminalInFolder": "Otevřít terminál v této složce", - "openTerminalInFileLocation": "Otevřít terminál v umístění souboru", - "runningFile": "Spuštěno - {{file}}", - "onlyRunExecutableFiles": "Spouštěcí soubory lze spustit pouze", - "directories": "Adresáře", - "removedFromRecentFiles": "Odstraněno \"{{name}}\" z nedávných souborů", - "removeFailed": "Odstranění se nezdařilo", - "unpinnedSuccessfully": "Zrušeno \"{{name}}\" úspěšně", - "unpinFailed": "Odepnutí se nezdařilo", - "removedShortcut": "Odstraněný zástupce \"{{name}} \" \" \"", - "removeShortcutFailed": "Odstranění zástupce se nezdařilo", - "clearedAllRecentFiles": "Vymazány všechny nedávné soubory", - "clearFailed": "Vymazání se nezdařilo", - "removeFromRecentFiles": "Odstranit z nedávných souborů", - "clearAllRecentFiles": "Vymazat všechny nedávné soubory", - "unpinFile": "Odepnout soubor", - "removeShortcut": "Odstranit zástupce", - "pinFile": "Připnout soubor", - "addToShortcuts": "Přidat do zkratek", - "pasteFailed": "Vložení se nezdařilo", - "noUndoableActions": "Žádné nedělitelné akce", - "undoCopySuccess": "Chyba kopírování: Odstraněno {{count}} zkopírováno soubory", - "undoCopyFailedDelete": "Zpět se nezdařilo: Nelze odstranit žádné zkopírované soubory", - "undoCopyFailedNoInfo": "Zpět se nezdařilo: Nelze najít zkopírované informace", - "undoMoveSuccess": "Neprovedená operace: Přesunuto {{count}} soubory zpět do původního umístění", - "undoMoveFailedMove": "Zpět se nezdařilo: Nelze přesunout žádné soubory zpět", - "undoMoveFailedNoInfo": "Zpět se nezdařilo: Informace o přesunu souboru nelze najít", - "undoDeleteNotSupported": "Nelze vrátit zpět: Soubory byly trvale odstraněny ze serveru", - "undoTypeNotSupported": "Nepodporovaný typ operace", - "undoOperationFailed": "Vrátit zpět operaci selhala", - "unknownError": "Neznámá chyba", - "confirm": "Potvrdit", - "find": "Najít...", - "replace": "Nahradit", - "downloadInstead": "Stáhnout místo", - "keyboardShortcuts": "Klávesové zkratky", - "searchAndReplace": "Hledat a nahradit", - "editing": "Úprava", - "search": "Hledat", - "findNext": "Najít další", - "findPrevious": "Najít předchozí", - "save": "Uložit", - "selectAll": "Vybrat vše", - "undo": "Zpět", - "redo": "Znovu", - "moveLineUp": "Posunout řádek nahoru", - "moveLineDown": "Posunout řádek dolů", - "toggleComment": "Přepnout komentář", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Nepodařilo se načíst obrázek", - "startTyping": "Začněte psát...", - "unknownSize": "Neznámá velikost", - "fileIsEmpty": "Soubor je prázdný", - "largeFileWarning": "Varování o velkém souboru", - "largeFileWarningDesc": "Tento soubor má velikost {{size}} , což může způsobit problémy s výkonem při otevření jako text.", - "fileNotFoundAndRemoved": "Soubor \"{{name}}\" nebyl nalezen a byl odstraněn z nedávných/připnutých souborů", - "failedToLoadFile": "Nepodařilo se načíst soubor: {{error}}", - "serverErrorOccurred": "Došlo k chybě serveru. Opakujte akci později.", - "autoSaveFailed": "Automatické ukládání se nezdařilo", - "fileAutoSaved": "Soubor automaticky uložen", - "moveFileFailed": "Přesun {{name}} se nezdařil", - "moveOperationFailed": "Přesunutí se nezdařilo", - "canOnlyCompareFiles": "Lze porovnat pouze dva soubory", - "comparingFiles": "Srovnávací soubory: {{file1}} a {{file2}}", - "dragFailed": "Přetažení se nezdařilo", - "filePinnedSuccessfully": "Soubor \"{{name}}\" byl úspěšně připnut", - "pinFileFailed": "Nepodařilo se připnout soubor", - "fileUnpinnedSuccessfully": "Soubor \"{{name}}\" byl úspěšně odepnut", - "unpinFileFailed": "Nepodařilo se uvolnit soubor", - "shortcutAddedSuccessfully": "Zástupce složky \"{{name}}\" byl úspěšně přidán", - "addShortcutFailed": "Nepodařilo se přidat zástupce", - "operationCompletedSuccessfully": "{{operation}} {{count}} položek úspěšně", - "operationCompleted": "{{operation}} {{count}} položek", - "downloadFileSuccess": "Soubor {{name}} byl úspěšně stažen", - "downloadFileFailed": "Stahování se nezdařilo", - "moveTo": "Přesunout do {{name}}", - "diffCompareWith": "Diff porovnání s {{name}}", - "dragOutsideToDownload": "Přetáhněte mimo okno ke stažení ({{count}} souborů)", - "newFolderDefault": "Nová složka", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Položky {{count}} byly úspěšně přesunuty do {{target}}", - "move": "Přesunout", - "searchInFile": "Hledat v souboru (Ctrl+F)", - "showKeyboardShortcuts": "Zobrazit klávesové zkratky", - "startWritingMarkdown": "Začněte psát svůj markdown obsah...", - "loadingFileComparison": "Načítání porovnání souborů...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Porovnat", - "sideBySide": "Boční strana", - "inline": "Řádek", - "fileComparison": "Porovnání souborů: {{file1}} vs {{file2}}", - "fileTooLarge": "Soubor je příliš velký: {{error}}", - "sshConnectionFailed": "Připojení SSH se nezdařilo. Zkontrolujte připojení k {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Nepodařilo se načíst soubor: {{error}}", - "connecting": "Připojování...", - "connectedSuccessfully": "Úspěšně připojeno", - "totpVerificationFailed": "Ověření TOTP se nezdařilo", - "warpgateVerificationFailed": "Ověření Warpgatu se nezdařilo", - "authenticationFailed": "Ověření se nezdařilo", - "verificationCodePrompt": "Ověřovací kód:", - "changePermissions": "Změnit oprávnění", - "currentPermissions": "Aktuální oprávnění", - "owner": "Vlastník", - "group": "Skupina", - "others": "Okurky salátové", - "read": "Číst", - "write": "Napsat", - "execute": "Spustit", - "permissionsChangedSuccessfully": "Oprávnění úspěšně změněna", - "failedToChangePermissions": "Změna oprávnění se nezdařila", - "name": "Název", - "sortByName": "Název", - "sortByDate": "Datum úpravy", - "sortBySize": "Velikost", - "ascending": "Vzestupně", - "descending": "sestupně", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", "root": "Root", - "new": "Nové", - "sortBy": "Seřadit podle", - "items": "Položky", - "selected": "Vybrané", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", - "octal": "Októra", - "storage": "Úložiště", + "octal": "Octal", + "storage": "Storage", "disk": "Disk", - "used": "Použité", - "of": "z", - "toggleSidebar": "Přepnout postranní panel", - "cannotLoadPdf": "Nelze načíst PDF", - "pdfLoadError": "Při načítání tohoto PDF souboru došlo k chybě.", - "loadingPdf": "Načítání PDF...", - "loadingPage": "Načítání stránky..." + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopírovat na hostitele…", - "moveToHost": "Přesunout na hostitele…", - "copyItemsToHost": "Zkopírovat {{count}} položek pro hostování…", - "moveItemsToHost": "Přesunout {{count}} položek na hostitele…", - "noHostsConnected": "Žádní další hostitelé souborů nejsou k dispozici.", - "noHostsConnectedHint": "Přidejte dalšího hostitele SSH s povoleným Správcem souborů ve Správci hostitelů.", - "selectDestinationHost": "Vyberte cílového hostitele", - "destinationPath": "Cílová cesta", - "recentDestinations": "Nedávné destinace", - "collapseRecentDestinations": "Sbalit nedávné cíle", - "expandRecentDestinations": "Rozbalit nedávné destinace", - "browseFolders": "Procházet cílové složky", - "browseDestination": "Procházet nebo zadat cestu", - "confirmCopy": "Kopie", - "confirmMove": "Pohyb", - "transferring": "Přenos…", - "compressing": "Komprese…", - "extracting": "Extrahování…", - "transferringItems": "Přenos {{current}} z {{total}} položek…", - "transferSuccess": "Převod dokončen", - "transferError": "Přenos se nezdařil", - "transferPartial": "Přenos dokončen s {{count}} chybami", - "transferPartialHint": "Nepodařilo se přenést: {{paths}}", - "itemsSummary": "{{count}} položek", - "destMustBeDirectory": "Cíl musí být adresář pro přenosy více položek.", - "selectThisFolder": "Vyberte tuto složku", - "browsePathWillBeCreated": "Tato složka zatím neexistuje. Bude vytvořena po zahájení přenosu.", - "browsePathError": "Tuto cestu se nepodařilo otevřít na cílovém hostiteli.", - "goUp": "Jdi nahoru", - "copyFolderToHost": "Zkopírovat složku na hostitele…", - "moveFolderToHost": "Přesunout složku na hostitele…", - "hostReady": "Připraveno", - "hostConnecting": "Připojování…", - "hostDisconnected": "Nepřipojeno", - "hostAuthRequired": "Vyžadováno ověření – nejprve otevřete Správce souborů na tomto hostiteli.", - "hostConnectionFailed": "Připojení se nezdařilo", - "metricsTitle": "Časy přestupů", - "metricsPrepare": "Připravit cíl: {{duration}}", - "metricsCompress": "Komprimovat u zdroje: {{duration}}", - "metricsHopSourceRead": "Zdroj → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → cíl (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → cíl (lokální): {{throughput}}", - "metricsTransfer": "Konec od konce: {{throughput}} ({{duration}})", - "metricsExtract": "Extrahovat v cíli: {{duration}}", - "metricsSourceDelete": "Odebrat ze zdroje: {{duration}}", - "metricsTotal": "Celkem: {{duration}}", - "progressCompressing": "Komprese na zdrojovém hostiteli…", - "progressExtracting": "Extrahování v cíli…", - "progressTransferring": "Přenos dat…", - "progressReconnecting": "Opětovné připojení…", - "parallelSegmentsLabel": "Paralelní přestupní pruhy", - "parallelSegmentsOption": "{{count}} jízdních pruhů", - "parallelSegmentsHint": "Velké soubory jsou rozděleny na bloky o velikosti 256 MB. Více linek používá samostatná připojení (například spuštění několika přenosů) pro vyšší celkovou propustnost.", - "progressTotalSpeed": "{{speed}} celkem ({{lanes}} pruhů)", - "progressTransferringItems": "Přenos souborů ({{current}} z {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} souborů", - "sourceNotDeletedPartial": "Zdrojové soubory zachovány (částečný přenos)", - "jumpHostLimitation": "Oba hostitelé musí být dosažitelné ze serveru Termix. Přímé směrování mezi hostiteli není podporováno.", - "cancel": "Zrušit", - "methodLabel": "Způsob přenosu", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Archiv Tar", - "methodItemSftp": "SFTP pro jednotlivé soubory", - "methodAutoHint": "Vybírá tar nebo SFTP pro jednotlivé soubory na základě počtu souborů, velikosti a komprimovatelnosti. Jednotlivé soubory vždy používají streamovaný SFTP.", - "methodTarHint": "Komprimace ve zdroji, přenos jednoho archivu, extrakce v cíli. Vyžaduje tar na obou unixových hostitelích.", - "methodItemSftpHint": "Přeneste každý soubor jednotlivě přes SFTP. Funguje na všech hostitelích včetně Windows.", - "methodPreviewLoading": "Výpočet metody převodu…", - "methodPreviewError": "Nepodařilo se zobrazit náhled metody přenosu. Server si metodu vybere i po spuštění.", - "methodPreviewWillUseTar": "Použiji: archiv Tar", - "methodPreviewWillUseItemSftp": "Použije se: SFTP pro jednotlivé soubory", - "methodPreviewScanSummary": "{{fileCount}} souborů, {{totalSize}} celkem (naskenováno na zdrojovém hostiteli).", - "methodItemSftpLimitation": "Každý soubor používá stejný SFTP stream jako kopie jednoho souboru, jeden po druhém. Průběh je sloučen napříč všemi soubory, takže se ukazatel u velkých souborů pohybuje pomalu.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Zvolili jste SFTP pro jednotlivé soubory.", - "user_tar": "Zvolili jste archiv tar.", - "tar_unavailable": "Tar není k dispozici na jednom nebo obou hostitelích — místo toho bude použit protokol SFTP pro jednotlivé soubory.", - "windows_host": "Je zapojen hostitelský systém Windows – tar se nepoužívá.", - "auto_multi_large": "Automaticky: více souborů včetně velkého souboru ({{largestSize}}) s komprimovatelnými daty — tar se sbalí do jednoho přenosu.", - "auto_single_large_in_archive": "Automaticky: jeden velký soubor ({{largestSize}}) v této sadě – SFTP pro každý soubor.", - "auto_many_incompressible": "Auto: většinou nekomprimovatelná data – SFTP pro jednotlivé soubory.", - "auto_many_files": "Auto: mnoho souborů ({{fileCount}}) – tar snižuje režii pro každý soubor.", - "auto_default": "Automaticky: SFTP pro jednotlivé soubory pro tuto sadu." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Zrušit", - "progressCancelling": "Zrušení…", - "progressStalled": "Zastaveno", - "resumedHint": "Znovu připojeno k aktivnímu přenosu zahájenému v jiném okně.", - "transferCancelled": "Převod zrušen", - "transferCancelledCopyHint": "V cílovém úložišti mohou zůstat jen částečné soubory.", - "transferCancelledMoveHint": "V cílovém umístění mohou zůstat jen některé soubory. Zdrojové soubory nebyly odstraněny.", - "cleanupDestFiles": "Vyčistit cíl", - "cleanupDestFilesSuccess": "Z cílového umístění byly odstraněny částečné soubory.", - "cleanupDestFilesPartial": "Některé částečné soubory se nepodařilo odstranit.", - "cleanupDestFilesNothing": "Na cílové destinaci není co uklízet", - "cleanupDestFilesError": "Vyčištění se nezdařilo", - "retryTransfer": "Zkusit znovu", - "retryTransferError": "Opakovaný pokus se nezdařil", - "transferFailedRetryHint": "V cíli byla uložena částečná data. Pokus bude obnoven po obnovení připojení." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Žádné SSH tunely", - "createFirstTunnelMessage": "Zatím jste nevytvořili žádné SSH tunely. Nakonfigurujte připojení tunelu v hostitelském manažeru, abyste mohli začít.", - "connected": "Připojeno", - "disconnected": "Odpojeno", - "connecting": "Připojování...", - "error": "Chyba", - "canceling": "Rušení...", - "connect": "Připojit", - "disconnect": "Odpojit", - "cancel": "Zrušit", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Místní port", - "remotePort": "Vzdálený port", - "currentHostPort": "Aktuální port hostitele", - "endpointPort": "Přístav koncového bodu", - "bindIp": "Místní IP", - "endpointSshConfig": "Konfigurace koncového bodu SSH", - "endpointSshHost": "Koncový bod SSH hostitel", - "endpointSshHostPlaceholder": "Vyberte nakonfigurovaného hostitele", - "endpointSshHostRequired": "Vyberte koncový SSH hostitele pro každý klientský tunel.", - "attempt": "Pokusit se {{current}} o {{max}}", - "nextRetryIn": "Další opakování za {{seconds}} sekund", - "clientTunnels": "Tunely klienta", - "clientTunnel": "Zákaznický tunel", - "addClientTunnel": "Přidat klientský tunel", - "noClientTunnels": "Na této ploše nejsou nakonfigurovány žádné tunely klienta.", - "tunnelName": "Název tunelu", - "remoteHost": "Vzdálený server", - "autoStart": "Automatický start", - "clientAutoStartDesc": "Začne, když se tento desktopový klient otevře a zůstane připojen.", - "clientManualStartDesc": "Používejte Start a Zastavit z tohoto řádku. Termix jej automaticky neotevře.", - "clientRemoteServerNote": "Vzdálené přesměrování může vyžadovat AllowTcpForwarding a GatewayPorts na serveru SSH serveru. Vzdálený port se uzavře, když se tato plocha odpojí.", - "clientTunnelStarted": "Klientský tunel spuštěn", - "clientTunnelStopped": "Klientský tunel zastaven", - "tunnelTestSucceeded": "Test tunelu byl úspěšný", - "tunnelTestFailed": "Test tunelu selhal", - "localSaved": "Klientské tunely uloženy", - "localSaveError": "Nepodařilo se uložit místní klientské tunely", - "invalidBindIp": "Místní IP adresa musí být platná IPv4 adresa.", - "invalidLocalTargetIp": "Místní cílová IP musí být platná IPv4 adresa.", - "invalidLocalPort": "Místní port musí být mezi 1 a 65535.", - "invalidRemotePort": "Vzdálený port musí být mezi 1 a 65535.", - "invalidLocalTargetPort": "Místní cílový port musí být mezi 1 a 65535.", - "invalidEndpointPort": "Přístav koncových bodů musí být mezi 1 a 65535.", - "duplicateAutoStartBind": "Pouze jeden klientský tunel s automatickým startem může používat {{bind}}.", - "manualControlError": "Aktualizace stavu tunelu se nezdařila.", - "active": "Aktivní", - "start": "Začít", - "stop": "Zastavit", - "test": "Zkouška", - "type": "Typ tunelu", - "typeLocal": "Místní (-L)", - "typeRemote": "Vzdálené (-R)", - "typeDynamic": "Dynamické (-D)", - "typeServerLocalDesc": "Aktuální hostitel ke koncovce.", - "typeServerRemoteDesc": "Koncový bod zpět do aktuálního hostitele.", - "typeClientLocalDesc": "Lokální počítač ke koncovce.", - "typeClientRemoteDesc": "Koncový bod zpět do lokálního počítače.", - "typeClientDynamicDesc": "SOCKS na místním počítači.", - "typeDynamicDesc": "Přesměrování SOCKS5 KONNECT provozu přes SSH", - "forwardDescriptionServerLocal": "Aktuální host {{sourcePort}} → koncový bod {{endpointPort}}.", - "forwardDescriptionServerRemote": "Koncový bod {{endpointPort}} → aktuální host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS na aktuálním hostiteli {{sourcePort}}.", - "forwardDescriptionClientLocal": "Místní {{sourcePort}} → vzdálené {{endpointPort}}.", - "forwardDescriptionClientRemote": "Vzdálená {{sourcePort}} → lokální {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS na místním portu {{sourcePort}}.", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS prostřednictvím {{endpoint}}", - "autoNameClientLocal": "Místní {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → místní {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} prostřednictvím {{endpoint}}", - "route": "Trasa:", - "lastStarted": "Naposledy spuštěno", - "lastTested": "Poslední test", - "lastError": "Poslední chyba", - "maxRetries": "Maximální počet opakování", - "maxRetriesDescription": "Maximální počet pokusů o opakování.", - "retryInterval": "Interval opakovat (sekundy)", - "retryIntervalDescription": "Čas čekat mezi pokusy opakovat.", - "local": "Místní", - "remote": "Vzdálené", - "destination": "Místo určení", - "host": "Hostitel", - "mode": "Režim", - "noHostSelected": "Nebyl vybrán žádný hostitel", - "working": "Zpracovávám..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Paměť", + "memory": "Memory", "disk": "Disk", - "network": "Síť", - "uptime": "Doba provozu", - "processes": "Proces", - "available": "Dostupné", - "free": "Zdarma", - "connecting": "Připojování...", - "connectionFailed": "Nepodařilo se připojit k serveru", - "naCpus": "Nepřichází v úvahu procesor", - "cpuCores_one": "Jádro {{count}}", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Využití CPU", - "memoryUsage": "Využití paměti", - "diskUsage": "Využití disku", - "failedToFetchHostConfig": "Nepodařilo se načíst konfiguraci hostitele", - "serverOffline": "Server je offline", - "cannotFetchMetrics": "Nelze načíst metriku z offline serveru", - "totpFailed": "Ověření TOTP se nezdařilo", - "noneAuthNotSupported": "Statistiky serveru nepodporují typ ověřování 'žádné'.", - "load": "Načíst", - "systemInfo": "Systémové informace", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Operační systém", - "kernel": "Jádro", - "seconds": "sekundy", - "networkInterfaces": "Síťová rozhraní", - "noInterfacesFound": "Nebyla nalezena žádná síťová rozhraní", - "noProcessesFound": "Nebyly nalezeny žádné procesy", - "loginStats": "SSH přihlašovací statistiky", - "noRecentLoginData": "Žádné nedávné přihlašovací údaje", - "executingQuickAction": "Provádění {{name}}...", - "quickActionSuccess": "{{name}} úspěšně dokončeno", - "quickActionFailed": "{{name}} selhal", - "quickActionError": "Provedení {{name}} se nezdařilo", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Naslouchající porty", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adresa", - "process": "Proces", - "noData": "Žádná data naslouchajících portů" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Neaktivní", - "policy": "Zásady", - "rules": "pravidla", - "noData": "Nejsou k dispozici žádná data firewallu", - "action": "Akce", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Zdroj", - "anywhere": "Kdekoliv" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Zatížení Prg", - "swap": "Prohodit", - "architecture": "Architektura", - "refresh": "Aktualizovat", - "retry": "Opakovat" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Samostatně hostované SSH a správa vzdálené plochy", - "loginTitle": "Přihlásit se do Termix", - "registerTitle": "Vytvořit účet", - "forgotPassword": "Zapomněli jste heslo?", - "rememberMe": "Zapamatovat zařízení na 30 dní (včetně TOTP)", - "noAccount": "Nemáte účet?", - "hasAccount": "Již máte účet?", - "twoFactorAuth": "Dvoufaktorové ověření", - "enterCode": "Zadejte ověřovací kód", - "backupCode": "Nebo použijte záložní kód", - "verifyCode": "Ověřit kód", - "redirectingToApp": "Přesměrování do aplikace...", - "sshAuthenticationRequired": "Vyžadováno ověření SSH", - "sshNoKeyboardInteractive": "Klávesnice - Interaktivní ověření není k dispozici", - "sshAuthenticationFailed": "Ověření se nezdařilo", - "sshAuthenticationTimeout": "Časový limit ověření", - "sshNoKeyboardInteractiveDescription": "Server nepodporuje interaktivní ověřování klávesnice. Zadejte prosím své heslo nebo SSH klíč.", - "sshAuthFailedDescription": "Zadané přihlašovací údaje jsou nesprávné. Zkuste to prosím znovu s platnými pověřeními.", - "sshTimeoutDescription": "Vypršel časový limit pokusu o ověření. Zkuste to znovu.", - "sshProvideCredentialsDescription": "Zadejte prosím vaše SSH přihlašovací údaje pro připojení k tomuto serveru.", - "sshPasswordDescription": "Zadejte heslo pro toto SSH připojení.", - "sshKeyPasswordDescription": "Pokud je váš SSH klíč šifrován, zadejte heslo zde.", - "passphraseRequired": "Vyžadována přístupová fráze", - "passphraseRequiredDescription": "SSH klíč je šifrován. Zadejte prosím heslo pro odemknutí.", - "back": "Zpět", - "firstUser": "První uživatel", - "firstUserMessage": "Jste první uživatel a bude vytvořen správce. Nastavení admin můžete zobrazit v rozevíracím menu postranního panelu. Pokud si myslíte, že je to chyba, zkontrolujte logy doku, nebo vytvořte GitHub.", - "external": "Externí", - "loginWithExternal": "Přihlásit se s externím poskytovatelem", - "loginWithExternalDesc": "Přihlášení pomocí vašeho nakonfigurovaného externího poskytovatele identity", - "externalNotSupportedInElectron": "Externí ověřování zatím není v aplikaci Electron podporováno. Použijte prosím webovou verzi pro OIDC přihlášení.", - "resetPasswordButton": "Obnovit heslo", - "sendResetCode": "Odeslat kód pro obnovení", - "resetCodeDesc": "Zadejte své uživatelské jméno pro získání kódu pro obnovení hesla. Kód bude přihlášen do logů v dokovacím kontejneru.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Ověřit kód", - "enterResetCode": "Zadejte šestimístný kód z logů kontejneru dockeru pro uživatele:", - "newPassword": "Nové heslo", - "confirmNewPassword": "Potvrzení hesla", - "enterNewPassword": "Zadejte své nové heslo pro uživatele:", - "signUp": "Zaregistrovat se", - "desktopApp": "Stolní aplikace", - "loggingInToDesktopApp": "Přihlášení do desktopové aplikace", - "loadingServer": "Načítání serveru...", - "dataLossWarning": "Obnovením hesla smažete všechny uložené SSH hostitele, přihlašovací údaje a další šifrovaná data. Tuto akci nelze vrátit zpět. Použijte pouze v případě, že jste zapomněli své heslo a nejste přihlášeni.", - "authenticationDisabled": "Autentizace zakázána", - "authenticationDisabledDesc": "Všechny metody ověřování jsou momentálně zakázány. Obraťte se na správce.", - "attemptsRemaining": "Zbývá {{count}} pokusů" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Ověřit klíč SSH hostitele", - "keyChangedWarning": "SSH klíč hostitele změněn", - "firstConnectionTitle": "První připojení k tomuto hostiteli", - "firstConnectionDescription": "Nelze určit pravost tohoto hostitele. Ověřte shodu s tím, co očekáváte.", - "keyChangedDescription": "Hostitelský klíč pro tento server se změnil od vašeho posledního připojení. To by mohlo znamenat bezpečnostní problém.", - "previousKey": "Předchozí klíč", - "newFingerprint": "Nový otisk prstu", - "fingerprint": "Otisk prstu", - "verifyInstructions": "Pokud tomuto hostiteli důvěřujete, klepněte na tlačítko Přijmout pro pokračování a uložení tohoto otisku pro budoucí připojení.", - "securityWarning": "Bezpečnostní varování", - "acceptAndContinue": "Přijmout a pokračovat", - "acceptNewKey": "Přijmout nový klíč a pokračovat" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Nelze se připojit k databázi", - "unknownError": "Neznámá chyba", - "loginFailed": "Přihlášení se nezdařilo", - "failedPasswordReset": "Nepodařilo se spustit obnovení hesla", - "failedVerifyCode": "Nepodařilo se ověřit kód pro obnovení", - "failedCompleteReset": "Obnovení hesla se nezdařilo", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Nepodařilo se spustit OIDC přihlášení", - "silentSigninOidcUnavailable": "Bylo požadováno tiché přihlášení, ale OIDC přihlášení není k dispozici.", - "failedUserInfo": "Nepodařilo se získat informace o uživateli po přihlášení", - "oidcAuthFailed": "OIDC ověření se nezdařilo", - "invalidAuthUrl": "Z backendu obdržena neplatná autorizační URL", - "requiredField": "Toto pole je povinné", - "minLength": "Minimální délka je {{min}}", - "passwordMismatch": "Hesla se neshodují", - "passwordLoginDisabled": "Uživatelské jméno/heslo přihlášení je momentálně zakázáno", - "sessionExpired": "Relace vypršela - přihlaste se prosím znovu", - "totpRateLimited": "Omezená hodnota: Příliš mnoho pokusů o ověření TOTP. Opakujte akci později.", - "totpRateLimitedWithTime": "Omezená hodnota: Příliš mnoho pokusů o ověření TOTP. Počkejte prosím {{time}} sekund před dalším pokusem.", - "resetCodeRateLimited": "Omezená hodnota: Příliš mnoho pokusů o ověření. Opakujte akci později.", - "resetCodeRateLimitedWithTime": "Omezená hodnota: Příliš mnoho pokusů o ověření. Počkejte prosím {{time}} sekund před dalším pokusem.", - "authTokenSaveFailed": "Nepodařilo se uložit ověřovací token", - "failedToLoadServer": "Nepodařilo se načíst server" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Registrace nového účtu je v současné době zakázána administrátorem. Přihlaste se nebo kontaktujte správce.", - "userNotAllowed": "Váš účet nemá oprávnění k registraci. Obraťte se na správce.", - "databaseConnectionFailed": "Nepodařilo se připojit k databázovému serveru", - "resetCodeSent": "Resetovat kód odeslaný do Docker logů", - "codeVerified": "Kód byl úspěšně ověřen", - "passwordResetSuccess": "Obnovení hesla bylo úspěšné", - "loginSuccess": "Přihlášení bylo úspěšné", - "registrationSuccess": "Registrace úspěšná" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Místní stolní tunely zaměřující nastavené SSH hostitele.", - "c2sTunnelPresets": "Předvolby klientského tunelu", - "c2sTunnelPresetsDesc": "Uložte místní seznam tunelů tohoto desktopového klienta jako pojmenovanou předvolbu serveru, nebo nahrajte předvolbu zpět do tohoto klienta.", - "c2sTunnelPresetsUnavailable": "Předvolby klientského tunelu jsou k dispozici pouze v desktopovém klientovi.", - "c2sPresetName": "Název předvolby", - "c2sPresetNamePlaceholder": "Název předvolby klienta", - "c2sPresetToLoad": "Předvolba k načtení", - "c2sNoPresetSelected": "Nebyla vybrána žádná předvolba", - "c2sNoPresets": "Žádná přednastavení nebyla uložena", - "c2sLoadPreset": "Načíst", - "c2sCurrentLocalConfig": "{{count}} lokální klientské tunely nakonfigurovány na tomto počítači.", - "c2sPresetSyncNote": "Předvolby jsou explicitní snímky; načítání jednoho nahradí lokální klientský seznam klientských tunelů.", - "c2sPresetSaved": "Předvolba klientského tunelu byla uložena", - "c2sPresetLoaded": "Předvolba klientského tunelu byla načtena lokálně", - "c2sPresetRenamed": "Předvolba klientského tunelu přejmenována", - "c2sPresetDeleted": "Předvolba klientského tunelu byla smazána", - "c2sPresetLoadError": "Nepodařilo se načíst přednastavení klientského tunelu" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Jazyk", - "keyPassword": "heslo klíče", - "pastePrivateKey": "Sem vložte svůj soukromý klíč...", - "localListenerHost": "127.0.0.1 (poslouchejte lokálně)", - "localTargetHost": "127.0.0.1 (cíl na tomto počítači)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Zadejte své heslo", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Nástěnka", - "loading": "Načítání řídicího panelu...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Podpora", + "support": "Support", "discord": "Discord", - "serverOverview": "Přehled serveru", - "version": "Verze", - "upToDate": "Aktuální", - "updateAvailable": "Dostupná aktualizace", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Doba provozu", - "database": "Databáze", - "healthy": "Zdravé", - "error": "Chyba", - "totalHosts": "Celkem hostitelů", - "totalTunnels": "Celkem tunelů", - "totalCredentials": "Pověření celkem", - "recentActivity": "Nedávná aktivita", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Načítání nedávné aktivity...", - "noRecentActivity": "Žádná poslední aktivita", - "quickActions": "Rychlé akce", - "addHost": "Přidat hostitele", - "addCredential": "Přidat pověření", - "adminSettings": "Nastavení správce", - "userProfile": "Profil uživatele", - "serverStats": "Statistiky serveru", - "loadingServerStats": "Načítání statistik serveru...", - "noServerData": "Nejsou k dispozici žádná data serveru", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Přizpůsobit nástěnku", - "dashboardSettings": "Nastavení hlavního panelu", - "enableDisableCards": "Povolit/zakázat karty", - "resetLayout": "Obnovit výchozí", - "serverOverviewCard": "Přehled serveru", - "recentActivityCard": "Nedávná aktivita", - "networkGraphCard": "Graf sítě", - "networkGraph": "Graf sítě", - "quickActionsCard": "Rychlé akce", - "serverStatsCard": "Statistiky serveru", - "panelMain": "Hlavní", - "panelSide": "Boční", - "justNow": "právě teď" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABILNÍ", - "hostsOnline": "Hosté online", - "activeTunnels": "Aktivní tunely", - "registerNewServer": "Registrovat nový server", - "storeSshKeysOrPasswords": "Uložit SSH klíče nebo hesla", - "manageUsersAndRoles": "Správa uživatelů a rolí", - "manageYourAccount": "Spravovat svůj účet", - "hostStatus": "Stav hostitele", - "noHostsConfigured": "Nejsou nakonfigurováni žádní hostitelé", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Přidat:", - "commandPalette": "Paleta příkazu", - "done": "Hotovo", - "editModeInstructions": "Přetažením karet změníte pořadí · Dělič sloupců pro změnu velikosti sloupců · Přetažením dolního okraje karty změníte jeho výšku · Koš pro odstranění", - "empty": "Prázdné", - "clear": "Vyčistit" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Přidat hostitele", - "addGroup": "Přidat skupinu", - "addLink": "Přidat odkaz", - "zoomIn": "Přiblížit", - "zoomOut": "Oddálit", - "resetView": "Obnovit zobrazení", - "selectHost": "Vybrat hostitele", - "chooseHost": "Vyberte hostitele...", - "parentGroup": "Nadřazená skupina", - "noGroup": "Žádná skupina", - "groupName": "Název skupiny", - "color": "Barva", - "source": "Zdroj", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Přesunout do skupiny", - "selectGroup": "Vybrat skupinu...", - "addConnection": "Přidat připojení", - "hostDetails": "Podrobnosti hostitele", - "removeFromGroup": "Odstranit ze skupiny", - "addHostHere": "Přidat hostitele", - "editGroup": "Upravit skupinu", - "delete": "Vymazat", - "add": "Přidat", - "create": "Vytvořit", - "move": "Přesunout", - "connect": "Připojit", - "createGroup": "Vytvořit skupinu", - "selectSourcePlaceholder": "Vyberte zdroj...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Neplatný soubor", - "hostAlreadyExists": "Hostitel je již v topologii", - "connectionExists": "Připojení již existuje", - "unknown": "Neznámý", - "name": "Název", - "ip": "IP adresa", - "status": "Stav", - "failedToAddNode": "Přidání uzlu se nezdařilo", - "sourceDifferentFromTarget": "Zdroj a cíl musí být jiný", - "exportJSON": "Exportovat JSON", - "importJSON": "Importovat JSON", - "terminal": "Terminál", - "fileManager": "Správce souborů", - "tunnel": "Tunel", - "docker": "Dokovací modul", - "serverStats": "Statistiky serveru", - "noNodes": "Zatím žádné uzly" + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker není pro tohoto hostitele povolen", - "validating": "Ověřování Dockeru...", - "connecting": "Připojování...", - "error": "Chyba", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Nepodařilo se připojit k Dockeru", - "containerStarted": "Kontejner {{name}} spuštěn", - "failedToStartContainer": "Nepodařilo se spustit kontejner {{name}}", - "containerStopped": "Kontejner {{name}} zastaven", - "failedToStopContainer": "Nepodařilo se zastavit kontejner {{name}}", - "containerRestarted": "Kontejner {{name}} restartován", - "failedToRestartContainer": "Restartování kontejneru {{name}} se nezdařilo", - "containerPaused": "Kontejner {{name}} pozastaven", - "containerUnpaused": "Kontejner {{name}} nepozastaven", - "failedToTogglePauseContainer": "Nepodařilo se přepnout stav pozastavení pro kontejner {{name}}", - "containerRemoved": "Kontejner {{name}} byl odstraněn", - "failedToRemoveContainer": "Odstranění kontejneru {{name}} se nezdařilo", - "image": "Obrázek", - "ports": "Porty", - "noPorts": "Žádné porty", - "start": "Začít", - "confirmRemoveContainer": "Opravdu chcete odstranit kontejner '{{name}}'? Tuto akci nelze vrátit zpět.", - "runningContainerWarning": "Varování: Tento kontejner je v současné době spuštěn. Odstranění jej nejdříve zastaví.", - "loadingContainers": "Načítání kontejnerů...", - "manager": "Správce Dockerů", - "autoRefresh": "Automatické obnovení", - "timestamps": "Časová razítka", - "lines": "Řádky", - "filterLogs": "Filtrovat logy...", - "refresh": "Aktualizovat", - "download": "Stáhnout", - "clear": "Vyčistit", - "logsDownloaded": "Protokoly byly úspěšně staženy", - "last50": "Posledních 50", - "last100": "Posledních 100", - "last500": "Posledních 500", - "last1000": "Posledních 1000", - "allLogs": "Všechny logy", - "noLogsMatching": "Žádné logy odpovídající \"{{query}}\"", - "noLogsAvailable": "Žádné logy nejsou k dispozici", - "noContainersFound": "Nebyly nalezeny žádné kontejnery", - "noContainersFoundHint": "Na tomto hostiteli nejsou k dispozici žádné kontejnery v Dockeru", - "searchPlaceholder": "Hledat kontejnery...", - "allStatuses": "Všechny stavy", - "stateRunning": "Běh", - "statePaused": "Pozastaveno", - "stateExited": "Ukončeno", - "stateRestarting": "Restartování", - "noContainersMatchFilters": "Žádné kontejnery neodpovídají vašim filtrům", - "noContainersMatchFiltersHint": "Zkuste upravit vyhledávací kritéria nebo kritéria filtru", - "failedToFetchStats": "Nepodařilo se načíst statistiky kontejneru", - "containerNotRunning": "Kontejner není spuštěn", - "startContainerToViewStats": "Spustit kontejner pro zobrazení statistik", - "loadingStats": "Načítání statistik...", - "errorLoadingStats": "Chyba při načítání statistik", - "noStatsAvailable": "Žádné statistiky nejsou k dispozici", - "cpuUsage": "Využití CPU", - "current": "Aktuální", - "memoryUsage": "Využití paměti", - "networkIo": "Síť I/O", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Výstup", - "blockIo": "Blok I/O", - "read": "Číst", - "write": "Napsat", - "pids": "PID", - "containerInformation": "Informace o kontejneru", - "name": "Název", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Stát", - "containerMustBeRunning": "Kontejner musí být spuštěn pro přístup do konzole", - "verificationCodePrompt": "Zadejte ověřovací kód", - "totpVerificationFailed": "Ověření TOTP se nezdařilo. Zkuste to prosím znovu.", - "warpgateVerificationFailed": "Ověření Warpgate se nezdařilo. Zkuste to prosím znovu.", - "connectedTo": "Připojeno k {{containerName}}", - "disconnected": "Odpojeno", - "consoleError": "Chyba konzole", - "errorMessage": "Chyba: {{message}}", - "failedToConnect": "Nepodařilo se připojit k kontejneru", - "console": "Konzole", - "selectShell": "Vybrat shell", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "Popel", - "connect": "Připojit", - "disconnect": "Odpojit", - "notConnected": "Nepřipojeno", - "clickToConnect": "Klepněte na tlačítko připojit pro spuštění relace", - "connectingTo": "Připojuji se k {{containerName}}...", - "containerNotFound": "Kontejner nebyl nalezen", - "backToList": "Zpět na seznam", - "logs": "Logy", - "stats": "Statistiky", - "consoleTab": "Konzole", - "startContainerToAccess": "Spusťte kontejner pro přístup do konzole" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Obecná ustanovení", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Uživatelé", - "sectionSessions": "Relace", - "sectionRoles": "Role", - "sectionDatabase": "Databáze", - "sectionApiKeys": "API klíče", - "allowRegistration": "Povolit registraci uživatele", - "allowRegistrationDesc": "Nechat nové uživatele se registrovat", - "allowPasswordLogin": "Povolit přihlášení k heslu", - "allowPasswordLoginDesc": "Uživatelské jméno/heslo přihlášení", - "oidcAutoProvision": "Automatické poskytování OIDC", - "oidcAutoProvisionDesc": "Automatické vytváření účtů pro OIDC uživatele i když je registrace zakázána", - "allowPasswordReset": "Povolit obnovení hesla", - "allowPasswordResetDesc": "Resetovat kód pomocí Docker logů", - "sessionTimeout": "Časový limit relace", - "hours": "hodiny", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Výchozí nastavení sledování", - "statusCheck": "Kontrola stavu", - "metrics": "Metrika", - "sec": "sekunda", - "logLevel": "Úroveň logu", - "enableGuacamole": "Povolit Guacamol", - "enableGuacamoleDesc": "RDP/VNC vzdálená plocha", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Konfigurace OpenID Connect pro SSO. Pole označená * jsou povinná.", - "oidcClientId": "ID klienta", - "oidcClientSecret": "Tajný klíč klienta", - "oidcAuthUrl": "Autorizační URL", - "oidcIssuerUrl": "URL vydavatele", - "oidcTokenUrl": "URL tokenu", - "oidcUserIdentifier": "Cesta identifikátoru uživatele", - "oidcDisplayName": "Zobrazit název cesty", - "oidcScopes": "Rozsah", - "oidcUserinfoUrl": "Přepsat URL uživatelských informací", - "oidcAllowedUsers": "Povolení uživatelé", - "oidcAllowedUsersDesc": "Jeden e-mail na řádek. Ponechte prázdné pro povolení všech.", - "removeOidc": "Odebrat", - "usersCount": "{{count}} uživatelé", - "createUser": "Vytvořit", - "newRole": "Nová role", - "roleName": "Název", - "roleDisplayName": "Zobrazované jméno", - "roleDescription": "L 343, 22.12.2009, s. 1).", - "rolesCount": "{{count}} role", - "createRole": "Vytvořit", - "creating": "Vytváření...", - "exportDatabase": "Exportovat databázi", - "exportDatabaseDesc": "Stáhnout zálohu všech hostitelů, přihlašovacích údajů a nastavení", - "export": "Exportovat", - "exporting": "Exportování...", - "importDatabase": "Importovat databázi", - "importDatabaseDesc": "Obnovit ze záložního souboru .sqlite", - "importDatabaseSelected": "Vybráno: {{name}}", - "selectFile": "Vybrat soubor", - "changeFile": "Změnit", - "import": "Importovat", - "importing": "Importování...", - "apiKeysCount": "Klávesy {{count}}", - "newApiKey": "Nový API klíč", - "apiKeyCreatedWarning": "Klíč byl vytvořen - zkopírujte jej, nebude znovu zobrazen.", - "apiKeyName": "Název", - "apiKeyUser": "Uživatel", - "apiKeySelectUser": "Vyberte uživatele...", - "apiKeyExpiresAt": "Vyprší v", - "createKey": "Vytvořit klíč", - "apiKeyNoExpiry": "Bez vypršení platnosti", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Duální ověření", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Místní", - "adminStatusAdministrator": "Administrátor", - "adminStatusRegularUser": "Běžný uživatel", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "VLASTNÍ", - "youBadge": "VÁŠ", - "sessionsActive": "{{count}} aktivní", - "sessionActive": "Aktivní: {{time}}", - "sessionExpires": "Rozbalit: {{time}}", - "revokeAll": "Vše", - "revokeAllSessionsSuccess": "Všechny relace pro uživatele odebrány", - "revokeAllSessionsFailed": "Nepodařilo se zrušit relace", - "revokeSessionFailed": "Nepodařilo se zrušit relaci", - "addRole": "Přidat roli", - "noCustomRoles": "Nebyly definovány žádné vlastní role", - "removeRoleFailed": "Nepodařilo se odstranit roli", - "assignRoleFailed": "Přiřazení role se nezdařilo", - "deleteRoleFailed": "Nepodařilo se odstranit roli", - "userAdminAccess": "Administrátor", - "userAdminAccessDesc": "Úplný přístup ke všem nastavením administrátora", - "userRoles": "Role", - "revokeAllUserSessions": "Zrušit všechny relace", - "revokeAllUserSessionsDesc": "Vynutit opětovné přihlášení na všech zařízeních", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Smazání tohoto uživatele je trvalé.", - "deleteUser": "Odstranit {{username}}", - "deleting": "Odstraňování...", - "deleteUserFailed": "Odstranění uživatele se nezdařilo", - "deleteUserSuccess": "Uživatel \"{{username}}\" byl smazán", - "deleteRoleSuccess": "Role \"{{name}}\" smazána", - "revokeKeySuccess": "Klíč \"{{name}}\" odvolán", - "revokeKeyFailed": "Nepodařilo se zrušit klíč", - "copiedToClipboard": "Zkopírováno do schránky", - "done": "Hotovo", - "createUserTitle": "Vytvořit uživatele", - "createUserDesc": "Vytvořit nový místní účet.", - "createUserUsername": "Uživatelské jméno", - "createUserPassword": "Heslo", - "createUserPasswordHint": "Minimálně 6 znaků.", - "createUserEnterUsername": "Zadejte uživatelské jméno", - "createUserEnterPassword": "Zadejte heslo", - "createUserSubmit": "Vytvořit uživatele", - "editUserTitle": "Spravovat uživatele: {{username}}", - "editUserDesc": "Upravit role, stav správce, relace a nastavení účtu.", - "editUserUsername": "Uživatelské jméno", - "editUserAuthType": "Typ ověření", - "editUserAdminStatus": "Stav administrace", - "editUserUserId": "ID uživatele", - "linkAccountTitle": "Propojit OIDC s účtem hesel", - "linkAccountDesc": "Sloučit OIDC účet {{username}} s existujícím lokálním účtem.", - "linkAccountWarningTitle": "To bude:", - "linkAccountEffect1": "Odstranit pouze OIDC účet", - "linkAccountEffect2": "Přidat OIDC přihlášení do cílového účtu", - "linkAccountEffect3": "Povolit přihlášení OIDC i heslem", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Zadejte místní uživatelské jméno pro propojení s", - "linkAccounts": "Propojit účty", - "linkAccountSuccess": "Účet OIDC propojený s „{{username}}“", - "linkAccountFailed": "Nepodařilo se propojit účet OIDC", - "linkAccountInProgress": "Propojování...", - "saving": "Ukládání...", - "updateRegistrationFailed": "Nepodařilo se aktualizovat nastavení registrace", - "updatePasswordLoginFailed": "Nepodařilo se aktualizovat nastavení přihlášení k heslu", - "updateOidcAutoProvisionFailed": "Nepodařilo se aktualizovat nastavení automatického poskytování OIDC", - "updatePasswordResetFailed": "Aktualizace nastavení obnovení hesla se nezdařila", - "sessionTimeoutRange2": "Časový limit relace musí být mezi 1 a 720 hodinami", - "sessionTimeoutSaved": "Časový limit relace uložen", - "sessionTimeoutSaveFailed": "Nepodařilo se uložit časový limit relace", - "monitoringIntervalInvalid": "Neplatné hodnoty intervalu", - "monitoringSaved": "Nastavení sledování uloženo", - "monitoringSaveFailed": "Nepodařilo se uložit nastavení monitorování", - "guacamoleSaved": "Nastavení Guacamole uloženo", - "guacamoleSaveFailed": "Nepodařilo se uložit nastavení Guacamole", - "guacamoleUpdateFailed": "Nepodařilo se aktualizovat nastavení Guacamole", - "logLevelUpdateFailed": "Aktualizace úrovně protokolu se nezdařila", - "oidcSaved": "OIDC konfigurace uložena", - "oidcSaveFailed": "Nepodařilo se uložit OIDC konfiguraci", - "oidcRemoved": "OIDC konfigurace odstraněna", - "oidcRemoveFailed": "Nepodařilo se odstranit OIDC konfiguraci", - "createUserRequired": "Uživatelské jméno a heslo jsou povinné", - "createUserPasswordTooShort": "Heslo musí mít alespoň 6 znaků", - "createUserSuccess": "Uživatel \"{{username}}\" byl vytvořen", - "createUserFailed": "Nepodařilo se vytvořit uživatele", - "updateAdminStatusFailed": "Aktualizace stavu administrátora se nezdařila", - "allSessionsRevoked": "Všechny relace byly zrušeny", - "revokeSessionsFailed": "Nepodařilo se zrušit relace", - "createRoleRequired": "Jméno a zobrazované jméno jsou povinné", - "createRoleSuccess": "Role \"{{name}}\" byla vytvořena", - "createRoleFailed": "Nepodařilo se vytvořit roli", - "apiKeyNameRequired": "Je vyžadován název klíče", - "apiKeyUserRequired": "Je vyžadováno ID uživatele", - "apiKeyCreatedSuccess": "API klíč \"{{name}}\" byl vytvořen", - "apiKeyCreateFailed": "Nepodařilo se vytvořit API klíč", - "exportSuccess": "Databáze úspěšně exportována", - "exportFailed": "Export databáze selhal", - "importSelectFile": "Nejprve prosím vyberte soubor", - "importCompleted": "Import dokončen: {{total}} položky importováno, {{skipped}} přeskočeno", - "importFailed": "Import se nezdařil: {{error}}", - "importError": "Import databáze se nezdařil" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Hostitel", - "hostPlaceholder": "192.168.1.1 nebo příklad.com", - "portLabel": "Přístav", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Uživatelské jméno", - "usernamePlaceholder": "uživatelské jméno", - "authLabel": "Ověření", - "passwordLabel": "Heslo", - "passwordPlaceholder": "heslo", - "privateKeyLabel": "Soukromý klíč", - "privateKeyPlaceholder": "Vložit soukromý klíč...", - "credentialLabel": "Pověření", - "credentialPlaceholder": "Vyberte uložené pověření", - "connectToTerminal": "Připojit k terminálu", - "connectToFiles": "Připojit k souborům" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nebyl vybrán žádný terminál", - "noTerminalSelectedHint": "Otevřete kartu SSH terminálu pro zobrazení jeho historie příkazů", - "searchPlaceholder": "Hledat historii...", - "clearAll": "Vymazat vše", - "noHistoryEntries": "Žádné záznamy historie", - "trackingDisabled": "Sledování historie je zakázáno", - "trackingDisabledHint": "Pro nahrávání příkazů povolte v nastavení profilu." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Záznam klíčů", - "recordToTerminals": "Zaznamenat do terminálů", - "selectAll": "Vše", - "selectNone": "Nic", - "noTerminalTabsOpen": "Nebyly otevřeny žádné karty terminálu", - "selectTerminalsAbove": "Vyberte terminály nad", - "broadcastInputPlaceholder": "Napište sem pro vysílání klíčové...", - "stopRecording": "Zastavit nahrávání", - "startRecording": "Začít nahrávat", - "settingsTitle": "Nastavení", - "enableRightClickCopyPaste": "Povolit kopírování/vkládání pravým tlačítkem myši" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Rozložení", - "selectLayoutAbove": "Vyberte rozložení výše", - "selectLayoutHint": "Vyberte počet panelů k zobrazení", - "panesTitle": "Panely", - "openTabsTitle": "Otevřené panely", - "dragTabsHint": "Přetáhněte záložky do panelů výše nebo použijte Rychlé přiřazení", - "dropHere": "Přetáhněte sem", - "emptyPane": "Prázdné", - "dashboard": "Nástěnka", - "clearSplitScreen": "Vymazat rozdělenou obrazovku", - "quickAssign": "Rychlé přiřazení", - "alreadyAssigned": "Podokno {{index}}", - "splitTab": "Rozdělit kartu", - "addToSplit": "Přidat do Splitu", - "removeFromSplit": "Odebrat ze Splitu", - "assignToPane": "Přiřadit k podoknu" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Vytvořit snippet", - "createSnippetDescription": "Vytvořit nový snippet příkazu pro rychlé spuštění", - "nameLabel": "Název", - "namePlaceholder": "např. Restartovat Nginx", - "descriptionLabel": "L 343, 22.12.2009, s. 1).", - "descriptionPlaceholder": "Volitelný popis", - "optional": "Nepovinné", - "folderLabel": "Složka", - "noFolder": "Žádná složka (nekategorizovaná)", - "commandLabel": "Příkaz", - "commandPlaceholder": "Např. sudo systemctl restart nginx", - "cancel": "Zrušit", - "createSnippetButton": "Vytvořit snippet", - "createFolderTitle": "Vytvořit složku", - "createFolderDescription": "Uspořádat úryvky do složek", - "folderNameLabel": "Název složky", - "folderNamePlaceholder": "Např. systémové příkazy, Docker skripty", - "folderColorLabel": "Barva složky", - "folderIconLabel": "Ikona složky", - "previewLabel": "Náhled", - "folderNameFallback": "Název složky", - "createFolderButton": "Vytvořit složku", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Vše", - "selectNone": "Nic", - "noTerminalTabsOpen": "Nebyly otevřeny žádné karty terminálu", - "searchPlaceholder": "Hledat snippety...", - "newSnippet": "Nový snippet", - "newFolder": "Nová složka", - "run": "Spustit", - "noSnippetsInFolder": "V této složce nejsou žádné textové bloky", - "uncategorized": "Nezařazené", - "editSnippetTitle": "Upravit snippet", - "editSnippetDescription": "Aktualizovat tento snippet příkazu", - "saveSnippetButton": "Uložit změny", - "createSuccess": "Výstřižek byl úspěšně vytvořen", - "createFailed": "Nepodařilo se vytvořit snippet", - "updateSuccess": "Úryvek byl úspěšně aktualizován", - "updateFailed": "Nepodařilo se aktualizovat snippet", - "deleteFailed": "Nepodařilo se odstranit snippet", - "folderCreateSuccess": "Složka byla úspěšně vytvořena", - "folderCreateFailed": "Nepodařilo se vytvořit složku", - "editFolderTitle": "Upravit složku", - "editFolderDescription": "Přejmenovat nebo změnit vzhled této složky", - "saveFolderButton": "Uložit změny", - "editFolder": "Upravit složku", - "deleteFolder": "Odstranit složku", - "folderDeleteSuccess": "Složka \"{{name}}\" byla smazána", - "folderDeleteFailed": "Nepodařilo se odstranit složku", - "folderEditSuccess": "Složka byla úspěšně aktualizována", - "folderEditFailed": "Nepodařilo se aktualizovat složku", - "confirmRunMessage": "Spustit „{{name}}“?", - "confirmRunButton": "Běh", - "runSuccess": "Ano \"{{name}}\" v {{count}} terminálech", - "copySuccess": "Zkopírováno \"{{name}}\" do schránky", - "shareTitle": "Sdílet snippet", - "shareUser": "Uživatel", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", "shareRole": "Role", - "selectUser": "Vyberte uživatele...", - "selectRole": "Vyberte roli...", - "shareSuccess": "Úryvek byl úspěšně sdílen", - "shareFailed": "Sdílení snippetu se nezdařilo", - "revokeSuccess": "Přístup zrušen", - "revokeFailed": "Nepodařilo se zrušit přístup", - "currentAccess": "Aktuální přístup", - "shareLoadError": "Nepodařilo se načíst sdílená data", - "loading": "Načítám...", - "close": "Zavřít", - "reorderFailed": "Nepodařilo se uložit pořadí úryvků" + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Účet", - "sectionAppearance": "Vzhled", - "sectionSecurity": "Zabezpečení", - "sectionApiKeys": "API klíče", - "sectionC2sTunnels": "C2S tunely", - "usernameLabel": "Uživatelské jméno", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", "roleLabel": "Role", - "roleAdministrator": "Administrátor", - "authMethodLabel": "Metoda ověření", - "authMethodLocal": "Místní", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Zapnuto", - "twoFaOff": "Vypnuto", - "versionLabel": "Verze", - "deleteAccount": "Odstranit účet", - "deleteAccountDescription": "Trvale smazat váš účet", - "deleteButton": "Vymazat", - "deleteAccountPermanent": "Tato akce je trvalá a nelze ji vrátit zpět.", - "deleteAccountWarning": "Všechny relace, hosting, přihlašovací údaje a nastavení budou trvale odstraněny.", - "confirmPasswordDeletePlaceholder": "Pro potvrzení zadejte své heslo", - "languageLabel": "Jazyk", - "themeLabel": "Téma", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Barva zvýraznění", - "settingsTerminal": "Terminál", - "commandAutocomplete": "Automatické dokončování příkazu", - "commandAutocompleteDesc": "Zobrazit automatické dokončování při psaní", - "historyTracking": "Sledování historie", - "historyTrackingDesc": "Sledovat příkazy terminálu", - "syntaxHighlighting": "Zvýraznění syntaxe", - "syntaxHighlightingDesc": "Zvýraznit výstup terminálu", - "commandPalette": "Paleta příkazu", - "commandPaletteDesc": "Povolit klávesovou zkratku", - "reopenTabsOnLogin": "Znovu otevřít karty po přihlášení", - "reopenTabsOnLoginDesc": "Obnovte otevřené karty po přihlášení nebo obnovení stránky, a to i z jiného zařízení", - "confirmTabClose": "Potvrdit zavření záložky", - "confirmTabCloseDesc": "Zeptat se před zavřením karet terminálu", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Zobrazit štítky hostitele", - "showHostTagsDesc": "Zobrazit tagy v seznamu hostitelů", - "hostTrayOnClick": "Kliknutím rozbalíte Akce hostitele", - "hostTrayOnClickDesc": "Vždy zobrazovat tlačítka pro připojení; kliknutím rozbalíte možnosti správy, místo abyste na něj nasadili myš.", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Udržovat levý postranní panel aplikace vždy rozbalený, místo aby se rozbaloval při najetí myší", - "settingsSnippets": "Výstřižky bloků", - "foldersCollapsed": "Sbalené složky", - "foldersCollapsedDesc": "Sbalit složky ve výchozím nastavení", - "confirmExecution": "Potvrdit provedení", - "confirmExecutionDesc": "Potvrdit před spuštěním textových bloků", - "settingsUpdates": "Aktualizace", - "disableUpdateChecks": "Zakázat kontroly aktualizací", - "disableUpdateChecksDesc": "Zastavit kontrolu aktualizací", - "totpAuthenticator": "TOTP autentifikátor", - "totpEnabled": "2FA je povoleno", - "totpDisabled": "Přidat zabezpečení přihlášení", - "disable": "Zakázat", - "enable": "Povolit", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Naskenujte QR kód nebo zadejte tajný klíč ve vaší ověřovací aplikaci, pak zadejte 6místný kód", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Ověřit", - "changePassword": "Změnit heslo", - "currentPasswordLabel": "Aktuální heslo", - "currentPasswordPlaceholder": "Aktuální heslo", - "newPasswordLabel": "Nové heslo", - "newPasswordPlaceholder": "Nové heslo", - "confirmPasswordLabel": "Potvrdit nové heslo", - "confirmPasswordPlaceholder": "Potvrdit nové heslo", - "updatePassword": "Aktualizovat heslo", - "createApiKeyTitle": "Vytvořit klíč API", - "createApiKeyDescription": "Generovat nový API klíč pro programový přístup.", - "apiKeyNameLabel": "Název", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "volitelné", - "cancel": "Zrušit", - "createKey": "Vytvořit klíč", - "apiKeyCount": "Klávesy {{count}}", - "newKey": "Nový klíč", - "noApiKeys": "Zatím žádné klíče API.", - "apiKeyActive": "Aktivní", - "apiKeyUsageHint": "Zahrnout váš klíč do", - "apiKeyUsageHintHeader": "záhlaví.", - "apiKeyPermissionsHint": "Klíče dědí oprávnění vytvářejícího uživatele.", - "roleUser": "Uživatel", - "authMethodDual": "Duální ověření", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Nepodařilo se spustit nastavení TOTP", - "totpEnter6Digits": "Zadejte 6místný kód", - "totpEnabledSuccess": "Dvoufaktorové ověřování povoleno", - "totpInvalidCode": "Neplatný kód, zkuste to prosím znovu", - "totpDisableInputRequired": "Zadejte váš TOTP kód nebo heslo", - "totpDisabledSuccess": "Dvoufaktorové ověření zakázáno", - "totpDisableFailed": "Nepodařilo se zakázat 2FA", - "totpDisableTitle": "Zakázat 2FA", - "totpDisablePlaceholder": "Zadejte TOTP kód nebo heslo", - "totpDisableConfirm": "Zakázat 2FA", - "totpContinueVerify": "Pokračovat k ověření", - "totpVerifyTitle": "Ověřit kód", - "totpBackupTitle": "Záložní kódy", - "totpDownloadBackup": "Stáhnout záložní kódy", - "done": "Hotovo", - "secretCopied": "Tajný klíč zkopírován do schránky", - "apiKeyNameRequired": "Je vyžadován název klíče", - "apiKeyCreated": "API klíč \"{{name}}\" byl vytvořen", - "apiKeyCreateFailed": "Nepodařilo se vytvořit API klíč", - "apiKeyUser": "Uživatel", - "apiKeyExpires": "Vyprší", - "apiKeyRevoked": "API klíč \"{{name}}\" odvolán", - "apiKeyRevokeFailed": "Nepodařilo se zrušit klíč API", - "passwordFieldsRequired": "Aktuální a nová hesla jsou vyžadována", - "passwordMismatch": "Hesla se neshodují", - "passwordTooShort": "Heslo musí mít alespoň 6 znaků", - "passwordUpdated": "Heslo úspěšně aktualizováno", - "passwordUpdateFailed": "Aktualizace hesla se nezdařila", - "deletePasswordRequired": "Pro smazání účtu je vyžadováno heslo", - "deleteFailed": "Nepodařilo se odstranit účet", - "deleting": "Odstraňování...", - "colorPickerTooltip": "Otevřít výběr barev", - "themeSystem": "Systém", - "themeLight": "Světlý", - "themeDark": "Tmavý", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizovaný", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Tmavý", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/da_DK.json b/src/ui/locales/translated/da_DK.json index f39a46d3..9bc5af32 100644 --- a/src/ui/locales/translated/da_DK.json +++ b/src/ui/locales/translated/da_DK.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Mapper", - "folder": "Mappe", - "password": "Adgangskode", - "key": "Nøgle", - "sshPrivateKey": "SSH Privat Nøgle", + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", "upload": "Upload", - "keyPassword": "Adgangskode Til Nøgle", - "sshKey": "SSH Nøgle", - "uploadPrivateKeyFile": "Upload Privat Nøglefil", - "searchCredentials": "Søg efter legitimationsoplysninger...", - "addCredential": "Tilføj Credential", - "caCertificate": "CA-certifikat (-cert.pub)", - "caCertificateDescription": "Valgfri: Upload eller indsæt CA-signeret certifikatfil (f.eks. id_ed25519-cert.pub). Påkrævet når din SSH-server bruger certifikatbaseret godkendelse.", - "uploadCertFile": "Upload -cert.pub fil", - "clearCert": "Ryd", - "certLoaded": "Certifikat indlæst", - "certPublicKeyLabel": "CA- Certifikat", - "certTypeLabel": "Certifikatets type", - "pasteOrUploadCert": "Indsæt eller upload et -cert.pub certifikat...", - "hasCaCert": "Har CA-certifikat", - "noCaCert": "Intet CA-certifikat" + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Kunne ikke indlæse advarsler", - "failedToDismissAlert": "Mislykkedes at afvise advarsel" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Server Konfiguration", - "description": "Konfigurer Termix-serverens URL til at oprette forbindelse til dine backend-tjenester", + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", "serverUrl": "Server URL", - "enterServerUrl": "Indtast venligst en server URL", - "saveFailed": "Konfigurationen kunne ikke gemmes", - "saveError": "Fejl under lagring af konfiguration", - "saving": "Gemmer...", - "saveConfig": "Gem Konfiguration", - "helpText": "Angiv URL'en hvor din Termix-server kører (f.eks. http://localhost:30001 eller https://your-server.com)", - "changeServer": "Skift Server", - "mustIncludeProtocol": "Server URL skal starte med http:// eller https://", - "allowInvalidCertificate": "Tillad ugyldigt certifikat", - "allowInvalidCertificateDesc": "Brug kun til betroede, selvhostede servere med selvsignerede certifikater eller IP-adressecertifikater.", - "useEmbedded": "Brug Lokal Server", - "embeddedDesc": "Kør Termix med den indbyggede lokale server (ingen fjernserver er nødvendig)", - "embeddedConnecting": "Forbinder til lokal server...", - "embeddedNotReady": "Lokal server er ikke klar endnu. Vent et øjeblik og prøv igen.", - "localServer": "Lokal Server" + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Fejl Ved Versionskontrol", - "checkFailed": "Mislykkedes at tjekke for opdateringer", - "upToDate": "App er opdateret", - "currentVersion": "Du kører version {{version}}", - "updateAvailable": "Opdatering Tilgængelig", - "newVersionAvailable": "En ny version er tilgængelig! Du kører {{current}}, men {{latest}} er tilgængelig.", + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", "betaVersion": "Beta Version", - "betaVersionDesc": "Du kører {{current}}, som er nyere end den seneste stabile udgave {{latest}}.", - "releasedOn": "Udgivet til {{date}}", - "downloadUpdate": "Download Opdatering", - "checking": "Søger efter opdateringer...", - "checkUpdates": "Søg efter opdateringer", - "checkingUpdates": "Søger efter opdateringer...", - "updateRequired": "Opdatering Påkrævet" + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Luk", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Fortsæt", - "maintenance": "Vedligeholdelse", - "degraded": "Fortyndet", - "error": "Fejl", - "warning": "Advarsel", - "unsavedChanges": "Ikke-gemte ændringer", - "dismiss": "Afvis", - "loading": "Indlæser...", - "optional": "Valgfri", - "connect": "Forbind", - "copied": "Kopieret", - "connecting": "Forbinder...", - "updateAvailable": "Opdatering Tilgængelig", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Åbn i nyt faneblad", - "noReleases": "Ingen Udgivelser", - "updatesAndReleases": "Opdaterer & Udgivelser", - "newVersionAvailable": "En ny version ({{version}}) er tilgængelig.", - "failedToFetchUpdateInfo": "Mislykkedes at hente opdateringsinformation", - "preRelease": "Forudløsning", - "noReleasesFound": "Ingen udgivelser fundet.", - "cancel": "Annuller", - "username": "Brugernavn", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", "login": "Login", - "register": "Registrer", - "password": "Adgangskode", - "confirmPassword": "Bekræft Adgangskode", - "back": "Tilbage", - "save": "Gem", - "saving": "Gemmer...", - "delete": "Slet", - "rename": "Omdøb", - "edit": "Rediger", - "add": "Tilføj", - "confirm": "Bekræft", - "no": "Nej", - "or": "ELLER", - "next": "Næste", - "previous": "Forrige", - "refresh": "Opdater", - "language": "Sprog", - "checking": "Kontrollerer...", - "checkingDatabase": "Kontrollerer databaseforbindelse...", - "checkingAuthentication": "Kontrollerer godkendelse...", - "backendReconnected": "Server forbindelse gendannet", - "connectionDegraded": "Serverforbindelse tabt, genoprette…", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Fjern", - "create": "Opret", - "update": "Opdater", - "copy": "Kopiér", - "copyFailed": "Kunne ikke kopiere til udklipsholder", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Gendan", - "of": "af" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Hjem", + "home": "Home", "terminal": "Terminal", "docker": "Docker", - "tunnels": "Tunneler", - "fileManager": "Filhåndtering", - "serverStats": "Server Statistik", - "admin": "Administrator", - "userProfile": "Bruger Profil", - "splitScreen": "Opdel Skærm", - "confirmClose": "Luk denne aktive session?", - "close": "Luk", - "cancel": "Annuller", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", "sshManager": "SSH Manager", - "cannotSplitTab": "Kan ikke dele dette faneblad", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopier Adgangskode", - "copySudoPassword": "Kopier Sudo Adgangskode", - "passwordCopied": "Adgangskode kopieret til udklipsholder", - "noPasswordAvailable": "Ingen adgangskode tilgængelig", - "failedToCopyPassword": "Kunne ikke kopiere adgangskode", - "refreshTab": "Opdater forbindelse", - "openFileManager": "Åbn Filhåndtering", - "dashboard": "Instrumentbræt", - "networkGraph": "Netværk Graf", - "quickConnect": "Hurtig Forbindelse", - "sshTools": "SSH Værktøjer", - "history": "Historik", - "hosts": "Værter", - "snippets": "Stumper", - "hostManager": "Vært Manager", - "credentials": "Legitimation", - "connections": "Forbindelser", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", "roleAdministrator": "Administrator", - "roleUser": "Bruger" + "roleUser": "User" }, "hosts": { - "hosts": "Værter", - "noHosts": "Ingen SSH værter", - "retry": "Forsøg igen", - "refresh": "Opdater", - "optional": "Valgfri", - "downloadSample": "Hent Eksempel", - "failedToDeleteHost": "Kunne ikke slette {{name}}", - "importSkipExisting": "Import (spring over eksisterende)", - "connectionDetails": "Forbindelse Detaljer", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Fjernskrivebord", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Brugernavn", - "folder": "Mappe", - "tags": "Mærker", - "pin": "Fastgør", - "addHost": "Tilføj Vært", - "editHost": "Rediger Vært", - "cloneHost": "Klon Vært", - "enableTerminal": "Aktiver Terminal", - "enableTunnel": "Aktiver Tunnel", - "enableFileManager": "Aktiver Filhåndtering", - "enableDocker": "Aktiver Docker", - "defaultPath": "Standard Sti", - "connection": "Forbindelse", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", "upload": "Upload", - "authentication": "Godkendelse", - "password": "Adgangskode", - "key": "Nøgle", + "authentication": "Authentication", + "password": "Password", + "key": "Key", "credential": "Credential", - "none": "Ingen", - "sshPrivateKey": "SSH Privat Nøgle", - "keyType": "Nøgle Type", - "uploadFile": "Upload Fil", - "tabGeneral": "Generelt", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunneler", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Filer", - "tabStats": "Statistik", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Deling", - "tabAuthentication": "Godkendelse", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Filhåndtering", - "serverStats": "Server Statistik", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Mappe \"{{oldName}}\" omdøbt til \"{{newName}}\" lykkedes", - "failedToRenameFolder": "Kunne ikke omdøbe mappe", - "movedToFolder": "Flyttet til \"{{folder}}\"", - "editHostTooltip": "Rediger vært", - "statusChecks": "Status Kontrol", - "metricsCollection": "Metrik Samling", - "metricsInterval": "Interval For Metrikelsamling", - "metricsIntervalDesc": "Hvor ofte at indsamle server statistik (5s - 1h)", - "behavior": "Opførsel", - "themePreview": "Tema Forhåndsvisning", - "theme": "Tema", - "fontFamily": "Skrifttype Familie", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Bogstav Afstand", - "lineHeight": "Linje Højde", - "cursorStyle": "Markør Stil", - "cursorBlink": "Markør Blink", - "scrollbackBuffer": "Rullback Buffer", - "bellStyle": "Klokke Stil", - "rightClickSelectsWord": "Højreklik Vælger Ord", - "fastScrollModifier": "Hurtig Scroll Modifikator", - "fastScrollSensitivity": "Hurtig Rul Følsomhed", - "sshAgentForwarding": "SSH Agent Videresend", - "backspaceMode": "Backspace Tilstand", - "startupSnippet": "Opstart Snippet", - "selectSnippet": "Vælg snippet", - "forceKeyboardInteractive": "Tving Tastatur-Interaktiv", - "overrideCredentialUsername": "Tilsidesæt Credential Brugernavn", - "overrideCredentialUsernameDesc": "Brug brugernavnet angivet ovenfor i stedet for legitimationsoplysningens brugernavn", - "jumpHostChain": "Hop Host Kæde", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Tilføj Port", - "addProxyNode": "Tilføj Node", + "addKnock": "Add Port", + "addProxyNode": "Add Node", "proxyNode": "Proxy Node", - "proxyType": "Proxy- Type", - "quickActions": "Hurtige Handlinger", - "sudoPasswordAutoFill": "Sudo Password AutoFyld", - "sudoPassword": "Sudo Adgangskode", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", "keepaliveInterval": "Keepalive Interval (ms)", - "moshCommand": "MOSH Kommando", - "environmentVariables": "Miljø Variabler", - "addVariable": "Tilføj Variabel", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Kopier Terminal- URL", - "copyFileManagerUrl": "Kopiér Filhåndterings URL", - "copyRemoteDesktopUrl": "Kopiér Fjernskrivebords URL", - "failedToConnect": "Kunne ikke forbinde til konsollen", - "connect": "Forbind", - "disconnect": "Afbryd", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", "start": "Start", - "enableStatusCheck": "Aktiver Status Tjek", - "enableMetrics": "Aktiver Metrics", - "bulkUpdateFailed": "Masse opdatering mislykkedes", - "selectAll": "Vælg Alle", - "deselectAll": "Fravælg Alle", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Sikker Skal", - "virtualNetwork": "Virtuelt Netværk", - "unencryptedShell": "Ukrypteret skal", - "addressIp": "Adresse / IP", - "friendlyName": "Venligt Navn", - "folderAndAdvanced": "Mappe & Avanceret", - "privateNotes": "Private Noter", - "privateNotesPlaceholder": "Detaljer om denne server...", - "pinToTop": "Fastgør til toppen", - "pinToTopDesc": "Vis altid denne vært øverst på listen", - "portKnockingSequence": "Port Knocking Sekvens", - "addKnockBtn": "Tilføj Knock", - "noPortKnocking": "Ingen port knocking konfigureret.", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Forsinkelse Efter (ms)", - "useSocks5Proxy": "Brug SOCKS5 Proxy", - "useSocks5ProxyDesc": "Rute forbindelse via en proxyserver", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", "proxyHost": "Proxy Host", "proxyPort": "Proxy Port", - "proxyUsername": "Proxy Brugernavn", - "proxyPassword": "Proxy Adgangskode", - "proxySingleMode": "Enkelt Proxy", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", "proxyChainMode": "Proxy Chain", - "you": "Dig", - "jumpHostChainLabel": "Hop Host Kæde", - "addJumpBtn": "Tilføj Hop", - "noJumpHosts": "Ingen hop værter konfigureret.", - "selectAServer": "Vælg en server...", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", "sshPort": "SSH Port", - "authMethod": "Auth Metode", - "storedCredential": "Gemte Credential", - "selectACredential": "Vælg en legitimationsoplysning...", - "keyTypeLabel": "Nøgle Type", - "keyTypeAuto": "Automatisk Detektering", - "keyPasteTab": "Indsæt", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", "keyUploadTab": "Upload", - "keyFileLoaded": "Nøgle fil indlæst", - "keyUploadClick": "Klik for at uploade .pem / .key / .ppk", - "clearKey": "Ryd nøgle", - "keySaved": "SSH-nøgle gemt", - "keyReplaceNotice": "indsæt en ny nøgle nedenfor for at erstatte den", - "keyPassphraseSaved": "Adgangssætning gemt, type for at ændre", - "replaceKey": "Erstat nøgle", - "forceKeyboardInteractiveLabel": "Tving Tastatur Interaktiv", - "forceKeyboardInteractiveShortDesc": "Gennemtving manuel adgangskodeindtastning, selvom nøgler er til stede", - "terminalAppearance": "Terminal Udseende", - "colorTheme": "Farve Tema", - "fontFamilyLabel": "Skrifttype Familie", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Markør Stil", - "letterSpacingPx": "Bogstavafstand (px)", - "lineHeightLabel": "Linje Højde", - "bellStyleLabel": "Klokke Stil", - "backspaceModeLabel": "Backspace Tilstand", - "cursorBlinking": "Markør Blinker", - "cursorBlinkingDesc": "Aktivér blinkende animation for terminalmarkøren", - "rightClickSelectsWordLabel": "Højreklik Vælger Ord", - "rightClickSelectsWordShortDesc": "Vælg ordet under markøren ved højreklik", - "behaviorAndAdvanced": "Opførsel Og Avanceret", - "scrollbackBufferLabel": "Rullback Buffer", - "scrollbackMaxLines": "Maksimalt antal linjer i historikken", - "sshAgentForwardingLabel": "SSH Agent Videresend", - "sshAgentForwardingShortDesc": "Kassér dine lokale SSH-nøgler til denne vært", - "enableAutoMosh": "Aktivér Auto-Mosh", - "enableAutoMoshDesc": "Foretræk Mosh frem for SSH hvis tilgængelig", - "enableAutoTmux": "Aktivér Auto-Tmux", - "enableAutoTmuxDesc": "Start eller vedhæft automatisk til tmux session", - "sudoPasswordAutoFillLabel": "Sudo Adgangskode Auto-Udfyld", - "sudoPasswordAutoFillShortDesc": "Angiv automatisk sudo adgangskode, når du bliver spurgt", - "sudoPasswordLabel": "Sudo Adgangskode", - "environmentVariablesLabel": "Miljø Variabler", - "addVariableBtn": "Tilføj Variabel", - "noEnvVars": "Ingen miljøvariabler konfigureret.", - "fastScrollModifierLabel": "Hurtig Scroll Modifikator", - "fastScrollSensitivityLabel": "Hurtig Rul Følsomhed", - "moshCommandLabel": "Mosh Kommando", - "startupSnippetLabel": "Opstart Snippet", - "keepaliveIntervalLabel": "Keepalive-interval (sekunder)", - "maxKeepaliveMisses": "Maks. Keepalive Mangler", - "tunnelSettings": "Tunnel Indstillinger", - "enableTunneling": "Aktiver Tunneling", - "enableTunnelingDesc": "Aktiver SSH-tunnelfunktionalitet for denne vært", - "serverTunnelsSection": "Servertunneler", - "addTunnelBtn": "Tilføj Tunnel", - "noTunnelsConfigured": "Ingen tunneler konfigureret.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", "tunnelType": "Tunnel Type", - "tunnelModeLocalDesc": "Videresend en lokal port til en port på den eksterne server (eller en vært, der kan nås fra den).", - "tunnelModeRemoteDesc": "Videresend en port på den eksterne server tilbage til en lokal port på din maskine.", - "tunnelModeDynamicDesc": "Opret en SOCKS5 proxy på en lokal port til dynamisk port viderestilling.", - "sameHost": "Denne vært (direkte tunnel)", - "endpointHost": "Endepunkt Vært", - "endpointPort": "Endepunkt Port", - "bindHost": "Bind Vært", - "sourcePort": "Kilde Port", - "maxRetries": "Maks. Forsøg", - "retryIntervalS": "Forsøg Interval (s)", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", "autoStartLabel": "Auto-start", - "autoStartDesc": "Tilslut automatisk denne tunnel når værten indlæses", - "tunnelConnecting": "Tunnel forbinder...", - "tunnelDisconnected": "Tunnel afbrudt", - "failedToConnectTunnel": "Kunne ikke forbinde", - "failedToDisconnectTunnel": "Afbrydelse af forbindelse mislykkedes", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", "dockerIntegration": "Docker Integration", - "enableDockerMonitor": "Aktiver Docker", - "enableDockerMonitorDesc": "Overvåg og administrere containere på denne vært via Docker", - "enableFileManagerMonitor": "Aktiver Filhåndtering", - "enableFileManagerMonitorDesc": "Gennemse og administrere filer på denne vært over SFTP", - "defaultPathLabel": "Standard Sti", - "fileManagerPathHint": "Mappen der skal åbnes når filhåndteringen starter for denne vært.", - "statusChecksLabel": "Status Kontrol", - "enableStatusChecks": "Aktiver Statuskontrol", - "enableStatusChecksDesc": "Periodisk ping af denne vært for at bekræfte tilgængelighed", - "useGlobalInterval": "Brug Globalt Interval", - "useGlobalIntervalDesc": "Tilsidesæt med intervallet for statuscheck for hele serveren", - "checkIntervalS": "Kontroller Interval (s)", - "checkIntervalDesc": "Sekunder mellem hver tilslutning ping", - "metricsCollectionLabel": "Metrik Samling", - "enableMetricsLabel": "Aktiver Metrics", - "enableMetricsDesc": "Indsaml CPU, RAM, disk, og netværksbrug fra denne vært", - "useGlobalMetrics": "Brug Globalt Interval", - "useGlobalMetricsDesc": "Tilsidesæt med serverens måleinterval", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", "metricsIntervalS": "Metrics Interval (s)", - "metricsIntervalDesc2": "Sekunder mellem metriske snapshots", - "visibleWidgets": "Synlige Widgets", - "cpuUsageLabel": "CPU Forbrug", - "cpuUsageDesc": "CPU procent, load gennemsnit, sparkline graf", - "memoryLabel": "Hukommelsesforbrug", - "memoryDesc": "RAM-forbrug, swap, cachet", - "storageLabel": "Diskforbrug", - "storageDesc": "Diskforbrug pr. monteringspunkt", - "networkLabel": "Netværksgrænseflader", - "networkDesc": "Grænseflade liste og båndbredde", - "uptimeLabel": "Oppetid", - "uptimeDesc": "Systemets oppetid og starttid", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", "systemInfoLabel": "System Info", - "systemInfoDesc": "OS, kerne, værtsnavn, arkitektur", - "recentLoginsLabel": "Seneste Logins", - "recentLoginsDesc": "Succesfulde og mislykkede login begivenheder", - "topProcessesLabel": "Top Processer", - "topProcessesDesc": "PID, CPU%, MEM%, kommando", - "listeningPortsLabel": "Lytter Havne", - "listeningPortsDesc": "Åbne porte med proces og tilstand", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, SELinux-status", - "quickActionsLabel": "Hurtige Handlinger", - "quickActionsToolbar": "Hurtige handlinger vises som knapper i serveren statistik værktøjslinjen for et enkelt klik kommando udførelse.", - "noQuickActions": "Ingen hurtige handlinger endnu.", - "buttonLabel": "Knap etiket", - "selectSnippetPlaceholder": "Vælg snippet...", - "addActionBtn": "Tilføj Handling", - "hostSharedSuccessfully": "Vært delt med succes", - "failedToShareHost": "Kunne ikke dele vært", - "accessRevoked": "Adgang tilbagekaldt", - "failedToRevokeAccess": "Mislykkedes at tilbagekalde adgang", - "cancelBtn": "Annuller", - "savingBtn": "Gemmer...", - "addHostBtn": "Tilføj Vært", - "hostUpdated": "Vært opdateret", - "hostCreated": "Vært oprettet", - "failedToSave": "Kunne ikke gemme vært", - "credentialUpdated": "Credential opdateret", - "credentialCreated": "Credential oprettet", - "failedToSaveCredential": "Kunne ikke gemme legitimationsoplysninger", - "backToHosts": "Tilbage til værter", - "backToCredentials": "Tilbage til legitimationsoplysninger", - "pinned": "Fastgjort", - "noHostsFound": "Ingen værter fundet", - "tryDifferentTerm": "Prøv et andet udtryk", - "addFirstHost": "Tilføj din første vært for at komme i gang", - "noCredentialsFound": "Ingen legitimationsoplysninger fundet", - "addCredentialBtn": "Tilføj Credential", - "updateCredentialBtn": "Opdater Credential", - "features": "Funktioner", - "noFolder": "(Ingen mappe)", - "deleteSelected": "Slet", - "exitSelection": "Afslut markering", - "importSkip": "Import (spring over eksisterende)", - "importOverwrite": "Import (overskrivning)", - "collapseBtn": "Sammenfold", - "importExportBtn": "Import / Eksport", - "hostStatusesRefreshed": "Værtsstatusser opdateret", - "failedToRefreshHosts": "Kunne ikke genopfriske værter", - "movedHostTo": "Flyttet {{host}} til \"{{folder}}\"", - "failedToMoveHost": "Kunne ikke flytte vært", - "folderRenamedTo": "Mappe omdøbt til \"{{name}}\"", - "deletedFolder": "Slettede mappe \"{{name}}\"", - "failedToDeleteFolder": "Kunne ikke slette mappe", - "deleteAllInFolder": "Slet alle værter i \"{{name}}\"? Dette kan ikke fortrydes.", - "deletedHost": "Slettede {{name}}", - "copiedToClipboard": "Kopieret til udklipsholder", - "terminalUrlCopied": "Terminal URL kopieret", - "fileManagerUrlCopied": "Filhåndterings URL kopieret", - "tunnelUrlCopied": "Tunnel URL kopieret", - "dockerUrlCopied": "Docker URL kopieret", - "serverStatsUrlCopied": "Server statistik URL kopieret", - "rdpUrlCopied": "RDP- URL kopieret", - "vncUrlCopied": "VNC URL kopieret", - "telnetUrlCopied": "Telnet URL kopieret", - "remoteDesktopUrlCopied": "Fjernskrivebords URL kopieret", - "expandActions": "Udvid handlinger", - "collapseActions": "Skjul handlinger", - "wakeOnLanAction": "Vågn på LAN", - "wakeOnLanSuccess": "Magisk pakke sendt til {{name}}", - "wakeOnLanError": "Kunne ikke sende magisk pakke", - "cloneHostAction": "Klon Vært", - "copyAddress": "Kopiér Adresse", - "copyLink": "Kopier Link", - "copyTerminalUrlAction": "Kopier Terminal- URL", - "copyFileManagerUrlAction": "Kopiér Filhåndterings URL", - "copyTunnelUrlAction": "Kopier Tunnel URL", - "copyDockerUrlAction": "Kopier Docker-URL", - "copyServerStatsUrlAction": "Kopier Server Statistik URL", - "copyRdpUrlAction": "Kopier RDP- URL", - "copyVncUrlAction": "Kopier VNC URL", - "copyTelnetUrlAction": "Kopier Telnet Url", - "copyRemoteDesktopUrlAction": "Kopiér Fjernskrivebords URL", - "deleteCredentialConfirm": "Slet legitimationsoplysninger \"{{name}}\"?", - "deletedCredential": "Slettede {{name}}", - "deploySSHKeyTitle": "Deploy SSH-nøgle", - "deployingBtn": "Implementerer...", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", "deployBtn": "Deploy", - "failedToDeployKey": "Kunne ikke implementere nøgle", - "deleteHostsConfirm": "Slet {{count}} vært{{plural}}? Dette kan ikke fortrydes.", - "movedToRoot": "Flyttet til rod", - "failedToMoveHosts": "Flytning af værter mislykkedes", - "enableTerminalFeature": "Aktiver Terminal", - "disableTerminalFeature": "Deaktivér Terminal", - "enableFilesFeature": "Aktiver Filer", - "disableFilesFeature": "Deaktivér Filer", - "enableTunnelsFeature": "Aktiver Tunneler", - "disableTunnelsFeature": "Deaktivér Tunneler", - "enableDockerFeature": "Aktiver Docker", - "disableDockerFeature": "Deaktivér Docker", - "addTagsPlaceholder": "Tilføj tags...", - "authDetails": "Autentificeringsoplysninger", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", "credType": "Type", - "generateKeyPairDesc": "Generer et nyt nøglepar, vil både private og offentlige nøgler blive udfyldt automatisk.", - "generatingKey": "Genererer...", - "generateLabel": "Generer {{label}}", - "uploadFileBtn": "Upload fil", - "keyPassphraseOptional": "Nøglekodeord (valgfri)", - "sshPublicKeyOptional": "Offentlig SSH Nøgle (Valgfri)", - "publicKeyGenerated": "Offentlig nøgle genereret", - "failedToGeneratePublicKey": "Kunne ikke udlede offentlig nøgle", - "publicKeyCopied": "Offentlig nøgle kopieret", - "keyPairGenerated": "{{label}} nøglepar genereret", - "failedToGenerateKeyPair": "Kunne ikke generere nøglepar", - "searchHostsPlaceholder": "Søg efter værter, adresser, tags…", - "searchCredentialsPlaceholder": "Søg efter legitimationsoplysninger…", - "refreshBtn": "Opdater", - "addTag": "Tilføj tags...", - "deleteConfirmBtn": "Slet", - "tunnelRequirementsText": "SSH-serveren skal have GatewayPorts ja, AllowTcpForwarding yes, og PermitRootLogin ja sat i /etc/ssh/sshd_config.", - "deleteHostConfirm": "Slet \"{{name}}\"?", - "enableAtLeastOneProtocol": "Aktivér mindst én protokol ovenfor for at konfigurere godkendelse og forbindelsesindstillinger.", - "keyPassphrase": "Nøglekodeord", - "connectBtn": "Forbind", - "disconnectBtn": "Afbryd", - "basicInformation": "Grundlæggende Oplysninger", - "authDetailsSection": "Autentificeringsoplysninger", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", "credTypeLabel": "Type", - "hostsTab": "Værter", - "credentialsTab": "Legitimation", - "selectMultiple": "Vælg flere", - "selectHosts": "Vælg værter", - "connectionLabel": "Forbindelse", - "authenticationLabel": "Godkendelse", - "generateKeyPairTitle": "Generer Nøglepar", - "generateKeyPairDescription": "Generer et nyt nøglepar, vil både private og offentlige nøgler blive udfyldt automatisk.", - "generateFromPrivateKey": "Generer fra privat nøgle", - "refreshBtn2": "Opdater", - "exitSelectionTitle": "Afslut markering", - "exportAll": "Eksporter Alle", - "addHostBtn2": "Tilføj Vært", - "addCredentialBtn2": "Tilføj Credential", - "checkingHostStatuses": "Kontrollerer værtsstatusser...", - "pinnedSection": "Fastgjort", - "hostsExported": "Værter eksporteret succesfuldt", - "exportFailed": "Kunne ikke eksportere værter", - "sampleDownloaded": "Eksempel fil downloadet", - "failedToDeleteCredential2": "Kunne ikke slette legitimationsoplysninger", - "noFolderOption": "(Ingen mappe)", - "nSelected": "{{count}} valgt", - "featuresMenu": "Funktioner", - "moveMenu": "Flyt", - "cancelSelection": "Annuller", - "deployDialogDesc": "Deploy {{name}} til en værts autoriserede_keys.", - "targetHostLabel": "Mål Vært", - "selectHostOption": "Vælg en vært...", - "keyDeployedSuccess": "Nøgle implementeret med succes", - "failedToDeployKey2": "Kunne ikke implementere nøgle", - "deletedCount": "Slettede {{count}} værter", - "failedToDeleteCount": "Kunne ikke slette {{count}} værter", - "duplicatedHost": "Duplikeret \"{{name}}\"", - "failedToDuplicateHost": "Kunne ikke duplikere vært", - "updatedCount": "Opdateret {{count}} værter", - "friendlyNameLabel": "Venligt Navn", - "descriptionLabel": "Varebeskrivelse", - "loadingHost": "Indlæser vært...", - "loadingHosts": "Indlæser værter...", - "loadingCredentials": "Indlæser legitimationsoplysninger...", - "noHostsYet": "Ingen værter endnu", - "noHostsMatchSearch": "Ingen værter matcher din søgning", - "hostNotFound": "Vært ikke fundet", - "searchHosts": "Søg efter værter...", - "sortHosts": "Sortér værter", - "sortDefault": "Standardordre", - "sortNameAsc": "Navn (A → Å)", - "sortNameDesc": "Navn (Z → A)", - "sortIpAsc": "IP-adresse (stigende)", - "sortIpDesc": "IP-adresse (beskrivelse)", - "sortOnlineFirst": "Online først", - "sortOfflineFirst": "Offline først", - "sortPinnedFirst": "Fastgjort først", - "filterHosts": "Filtrer værter", - "filterClearAll": "Ryd filtre", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fastgjort", - "filterAuthGroup": "Godkendelsestype", - "filterAuthPassword": "Adgangskode", - "filterAuthKey": "SSH-nøgle", - "filterAuthCredential": "Legitimationsoplysninger", - "filterAuthNone": "Ingen", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokol", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "Landdistriktsudviklingsplan", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Funktioner", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Filhåndtering", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Tags", - "shareHost": "Del Vært", - "shareHostTitle": "Del: {{name}}", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Denne vært skal bruge legitimationsoplysninger for at aktivere deling. Redigér værten og tildel et legitimationsoplysninger først." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Forbindelse", - "authentication": "Godkendelse", - "connectionSettings": "Forbindelsesindstillinger", - "displaySettings": "Vis Indstillinger", - "audioSettings": "Indstillinger For Lyd", - "rdpPerformance": "RDP- Ydelse", - "deviceRedirection": "Enhed Omdirigering", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", "session": "Session", - "gateway": "Port", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Udklipsholder", - "sessionRecording": "Session Optagelse", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Vnc Indstillinger", - "terminalSettings": "Terminalindstillinger", - "rdpPort": "Rdp Port", - "username": "Brugernavn", - "password": "Adgangskode", - "domain": "Domæne", - "securityMode": "Sikkerheds Tilstand", - "colorDepth": "Farve Dybde", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Højde", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Ændr Størrelsesmetode", - "clientName": "Klient Navn", - "initialProgram": "Oprindeligt Program", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Port Til Port", - "gatewayUsername": "Gateway Brugernavn", - "gatewayPassword": "Gateway Adgangskode", - "gatewayDomain": "Gateway Domæne", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", "remoteAppProgram": "RemoteApp Program", - "workingDirectory": "Arbejdsmappe", - "arguments": "Argumenter", - "normalizeLineEndings": "Normalisér Linjeafslutninger", - "recordingPath": "Optagelsessti", - "recordingName": "Optagelses Navn", - "macAddress": "MAC- Adresse", - "broadcastAddress": "Broadcast- Adresse", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Vent Tid (r)", - "driveName": "Drev Navn", - "drivePath": "Kør Sti", - "ignoreCertificate": "Ignorer Certifikat", - "ignoreCertificateDesc": "Tillad forbindelser til værter med selvsignerede certifikater", - "forceLossless": "Gennemtving Tabsfri", - "forceLosslessDesc": "Gennemtving tabsfri billedkodning (højere kvalitet, mere båndbredde)", - "disableAudio": "Deaktivér Lyd", - "disableAudioDesc": "Lydløs al lyd fra den eksterne session", - "enableAudioInput": "Aktiver Audio Input (Mikrofon)", - "enableAudioInputDesc": "Videresend lokal mikrofon til den eksterne session", - "wallpaper": "Baggrund", - "wallpaperDesc": "Vis skrivebordsbaggrund (deaktivering forbedrer ydeevnen)", - "theming": "Temaer", - "themingDesc": "Aktiver visuelle temaer og stilarter", - "fontSmoothing": "Skrifttype Udjævning", - "fontSmoothingDesc": "Aktiver ClearType skrifttype rendering", - "fullWindowDrag": "Fuldt Vindue Træk", - "fullWindowDragDesc": "Vis vindueindhold under trækning", - "desktopComposition": "Skrivebords Sammensætning", - "desktopCompositionDesc": "Aktiver Aero glas effekter", - "menuAnimations": "Menu Animationer", - "menuAnimationsDesc": "Aktiver menu fade og dias-animationer", - "disableBitmapCaching": "Deaktivér Bitmap- Caching", - "disableBitmapCachingDesc": "Slå bitmap-cache fra (kan hjælpe med glitches)", - "disableOffscreenCaching": "Deaktivér Offscreen Caching", - "disableOffscreenCachingDesc": "Slå offscreen cache fra", - "disableGlyphCaching": "Deaktivér Symbol Caching", - "disableGlyphCachingDesc": "Slå glyph cache fra", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Brug RemoteFX grafik pipeline", - "enablePrinting": "Aktiver Udskrivning", - "enablePrintingDesc": "Omdiriger lokale printere til fjernsessionen", - "enableDriveRedirection": "Aktiver Drev Omdirigering", - "enableDriveRedirectionDesc": "Kortlæg en lokal mappe som et drev i den eksterne session", - "createDrivePath": "Opret Kørselssti", - "createDrivePathDesc": "Opret automatisk mappen, hvis den ikke findes", - "disableDownload": "Deaktivér Download", - "disableDownloadDesc": "Undgå at downloade filer fra den eksterne session", - "disableUpload": "Deaktivér Upload", - "disableUploadDesc": "Forhindr upload af filer til den eksterne session", - "enableTouch": "Aktivér Touch", - "enableTouchDesc": "Aktiver berørings-input-videresendelse", - "consoleSession": "Konsol Session", - "consoleSessionDesc": "Opret forbindelse til konsollen (session 0) i stedet for en ny session", - "sendWolPacket": "Send WOL Pakke", - "sendWolPacketDesc": "Send en magisk pakke til at vække denne vært før tilslutning", - "disableCopy": "Deaktivér Kopiering", - "disableCopyDesc": "Forhindr kopiering af tekst fra den eksterne session", - "disablePaste": "Deaktivér Indsæt", - "disablePasteDesc": "Forhindre indsættelse af tekst i den eksterne session", - "createPathIfMissing": "Opret sti, hvis der mangler", - "createPathIfMissingDesc": "Opret automatisk optagelsesmappen", - "excludeOutput": "Udeluk Output", - "excludeOutputDesc": "Optag ikke skærmoutput (kun metadata)", - "excludeMouse": "Udeluk Mus", - "excludeMouseDesc": "Optag ikke musebevægelser", - "includeKeystrokes": "Inkludér Nøglestreger", - "includeKeystrokesDesc": "Optag rå tastetryk ud over skærmoutput", - "vncPort": "Vnc Port", - "vncPassword": "VNC Adgangskode", - "vncUsernameOptional": "Brugernavn (valgfrit)", - "vncLeaveBlank": "Efterlad blank, hvis ikke påkrævet", - "cursorMode": "Markør Tilstand", - "swapRedBlue": "Byt Rød/Blå", - "swapRedBlueDesc": "Byt de røde og blå farvekanaler (løser nogle farveproblemer)", - "readOnly": "Skrivebeskyttet", - "readOnlyDesc": "Se fjernskærmen uden at sende input", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", "telnetPort": "Telnet Port", - "terminalType": "Terminaltype", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Farvesammensætning", - "backspaceKey": "Backspace Nøgle", - "saveHostFirst": "Gem værten først.", - "sharingOptionsAfterSave": "Delingsindstillinger er tilgængelige, efter at værten er blevet gemt.", - "sharingLoadError": "Kunne ikke indlæse delingsdata. Tjek din forbindelse og prøv igen.", - "shareHostSection": "Del Vært", - "shareWithUser": "Del med bruger", - "shareWithRole": "Del med Rolle", - "selectUser": "Vælg Bruger", - "selectRole": "Vælg Rolle", - "selectUserOption": "Vælg en bruger...", - "selectRoleOption": "Vælg en rolle...", - "permissionLevel": "Tilladelsesniveau", - "expiresInHours": "Udløber om (timer)", - "noExpiryPlaceholder": "Lad være tom for ingen udløbsdato", - "shareBtn": "Del", - "currentAccess": "Nuværende Adgang", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Tilladelse", - "grantedByHeader": "Tildelt Af", - "expiresHeader": "Udløber", - "noAccessEntries": "Ingen adgangsposter endnu.", - "expiredLabel": "Udløbet", - "neverLabel": "Aldrig", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Annuller", - "savingBtn": "Gemmer...", - "updateHostBtn": "Opdater Vært", - "addHostBtn": "Tilføj Vært" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Søg efter værter, kommandoer eller indstillinger...", - "quickActions": "Hurtige Handlinger", - "hostManager": "Vært Manager", - "hostManagerDesc": "Håndtere, tilføje eller redigere værter", - "addNewHost": "Tilføj Ny Vært", - "addNewHostDesc": "Registrer en ny vært", - "adminSettings": "Admin Indstillinger", - "adminSettingsDesc": "Konfigurere systempræferencer og brugere", - "userProfile": "Bruger Profil", - "userProfileDesc": "Administrer din konto og præferencer", - "addCredential": "Tilføj Credential", - "addCredentialDesc": "Gem SSH-nøgler eller adgangskoder", - "recentActivity": "Seneste Aktivitet", - "serversAndHosts": "Servere Og Værter", - "noHostsFound": "Ingen værter fundet matchende \"{{search}}\"", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", "links": "Links", "navigate": "Navigate", - "select": "Vælg", - "toggleWith": "Skift med" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Ingen fane tildelt", - "focusedPane": "Aktiv rude" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Ingen forbindelser", - "noConnectionsDesc": "Åbn en terminal, filhåndtering eller fjernskrivebord for at se forbindelser her", - "connectedFor": "Forbundet for {{duration}}", - "connected": "Forbundet", - "disconnected": "Afbrudt", - "closeTab": "Luk faneblad", - "closeConnection": "Luk forbindelse", - "forgetTab": "Glemme", - "removeBackground": "Fjerne", - "reconnect": "Genopret forbindelse", - "reopenTab": "Genåbn", - "sectionOpen": "Åben", - "sectionBackground": "Baggrund", - "backgroundDesc": "Sessioner fortsætter i 30 minutter efter afbrydelse og kan genoprettes.", - "persisted": "Fortsatte i baggrunden", - "expiresIn": "Udløber om {{duration}}", - "search": "Søg efter forbindelser...", - "noSearchResults": "Ingen forbindelser matcher din søgning" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Forbinder til {{type}} session...", - "connectionError": "Forbindelsesfejl", - "connectionFailed": "Forbindelse mislykkedes", - "failedToConnect": "Kunne ikke hente forbindelsestoken", - "hostNotFound": "Vært ikke fundet", - "noHostSelected": "Ingen vært valgt", - "reconnect": "Genopret", - "retry": "Forsøg igen", - "guacdUnavailable": "Fjernskrivebordstjeneste (guacd) er ikke tilgængelig. Sørg for, at guacd kører og er tilgængelig og konfigureret korrekt i administratorindstillinger.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Låseskærm)", - "winKey": "Windows Nøgle", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Skift", - "win": "Vind", - "stickyActive": "{{key}} (låset - klik for at slippe)", - "stickyInactive": "{{key}} (klik for at låse)", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", "esc": "Escape", "tab": "Tab", - "home": "Hjem", - "end": "Slut", - "pageUp": "Side Op", - "pageDown": "Side Ned", - "arrowUp": "Pil Op", - "arrowDown": "Pil Ned", - "arrowLeft": "Pil Til Venstre", - "arrowRight": "Pil Til Højre", - "fnToggle": "Funktionsnøgler", - "reconnect": "Genforbind Session", - "collapse": "Skjul værktøjslinje", - "expand": "Udvid værktøjslinje", - "dragHandle": "Træk for at flytte" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Opret forbindelse til vært", - "clear": "Ryd", - "paste": "Indsæt", - "reconnect": "Genopret", - "connectionLost": "Forbindelse tabt", - "connected": "Forbundet", - "clipboardWriteFailed": "Kunne ikke kopiere til udklipsholderen. Sørg for, at siden er serveret over HTTPS eller localhost.", - "clipboardReadFailed": "Kunne ikke læse fra udklipsholderen. Sørg for, at udklipsholdertilladelser er tildelt.", - "clipboardHttpWarning": "Indsæt kræver HTTPS. Brug Ctrl + Shift + V eller servere Termix over HTTPS.", - "unknownError": "Ukendt fejl opstod", - "websocketError": "WebSocket forbindelsesfejl", - "connecting": "Forbinder...", - "noHostSelected": "Ingen vært valgt", - "reconnecting": "Genforbindelse... ({{attempt}}/{{max}})", - "reconnected": "Genoprettet succesfuldt", - "tmuxSessionCreated": "tmux session oprettet: {{name}}", - "tmuxSessionAttached": "tmux session vedhæftet: {{name}}", - "tmuxUnavailable": "tmux er ikke installeret på den eksterne vært, falder tilbage til standard shell", - "tmuxSessionPickerTitle": "tmux sessioner", - "tmuxSessionPickerDesc": "Eksisterende tmux sessioner fundet på denne vært. Vælg en for at vedhæfte eller oprette en ny session.", - "tmuxWindows": "Vinduer", - "tmuxWindowCount": "{{count}} vindue", - "tmuxAttached": "Vedhæftede klienter", - "tmuxAttachedCount": "{{count}} vedhæftet", - "tmuxLastActivity": "Seneste aktivitet", - "tmuxTimeJustNow": "lige nu", - "tmuxTimeMinutes": "{{count}}m siden", - "tmuxTimeHours": "{{count}}h siden", - "tmuxTimeDays": "{{count}}d siden", - "tmuxCreateNew": "Start ny session", - "tmuxCopyHint": "Justér markering og tryk på Enter for at kopiere til udklipsholder", - "tmuxDetach": "Frigør fra tmux session", - "tmuxDetached": "Detached fra tmux session", - "maxReconnectAttemptsReached": "Maksimal genforbindelsesforsøg nået", - "closeTab": "Luk", - "connectionTimeout": "Forbindelse timeout", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Kører {{command}} - {{host}}", - "totpRequired": "To-Faktor Godkendelse Kræves", - "totpCodeLabel": "Bekræftelseskode", - "totpVerify": "Verificér", - "warpgateAuthRequired": "Warpgate Authentication Påkrævet", - "warpgateSecurityKey": "Sikkerhed Nøgle", - "warpgateAuthUrl": "Godkendelses-URL", - "warpgateOpenBrowser": "Åbn i browser", - "warpgateContinue": "Jeg Er Afsluttet Godkendelse", - "opksshAuthRequired": "OPKSSH Godkendelse Kræves", - "opksshAuthDescription": "Komplet godkendelse i din browser for at fortsætte. Denne session vil forblive gyldig i 24 timer.", - "opksshOpenBrowser": "Åbn browser for at godkende", - "opksshWaitingForAuth": "Venter på godkendelse i browser...", - "opksshAuthenticating": "Behandler godkendelse...", - "opksshTimeout": "Godkendelse fik timeout. Prøv venligst igen.", - "opksshAuthFailed": "Godkendelse mislykkedes. Kontroller dine legitimationsoplysninger og prøv igen.", - "opksshSignInWith": "Log ind med {{provider}}", - "sudoPasswordPopupTitle": "Indsæt Adgangskode?", - "websocketAbnormalClose": "Forbindelsen lukkes uventet. Dette kan skyldes et problem med omvendt proxy eller SSL-konfiguration. Kontroller serverlogs.", - "connectionLogTitle": "Forbindelses Log", - "connectionLogCopy": "Kopier logs til udklipsholder", - "connectionLogEmpty": "Ingen forbindelses logs endnu", - "connectionLogWaiting": "Venter på forbindelseslogger...", - "connectionLogCopied": "Forbindelseslogs kopieret til udklipsholder", - "connectionLogCopyFailed": "Kunne ikke kopiere logfiler til udklipsholder", - "connectionRejected": "Forbindelse afvist af serveren. Tjek venligst din godkendelse og netværkskonfiguration.", - "hostKeyRejected": "SSH-værtnøglebekræftelse afvist. Forbindelse annulleret.", - "sessionTakenOver": "Session blev åbnet i et andet faneblad. Genjusterer..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Ingen vært valgt", - "initializingEditor": "Initialiserer editor...", - "file": "Fil", - "folder": "Mappe", - "uploadFile": "Upload Fil", - "downloadFile": "Hent", - "extractArchive": "Udpak Arkiv", - "extractingArchive": "Udtrækker {{name}}...", - "archiveExtractedSuccessfully": "{{name}} udtrukket succesfuldt", - "extractFailed": "Udpak mislykkedes", - "compressFile": "Komprimer Fil", - "compressFiles": "Komprimer Filer", - "compressFilesDesc": "Komprimer {{count}} elementer i et arkiv", - "archiveName": "Arkivets Navn", - "enterArchiveName": "Indtast arkivnavn...", - "compressionFormat": "Komprimeringsformat", - "selectedFiles": "Valgte filer", - "andMoreFiles": "og {{count}} mere...", - "compress": "Komprimér", - "compressingFiles": "Komprimering af {{count}} elementer til {{name}}...", - "filesCompressedSuccessfully": "{{name}} oprettet", - "compressFailed": "Komprimering mislykkedes", - "edit": "Rediger", - "preview": "Eksempelvisning", - "previous": "Forrige", - "next": "Næste", - "pageXOfY": "Side {{current}} af {{total}}", - "zoomOut": "Zoom Ud", - "zoomIn": "Zoom Ind", - "newFile": "Ny Fil", - "newFolder": "Ny Mappe", - "rename": "Omdøb", - "uploading": "Uploader...", - "uploadingFile": "Uploader {{name}}...", - "fileName": "Fil Navn", - "folderName": "Mappe Navn", - "fileUploadedSuccessfully": "Filen \"{{name}}\" blev uploadet", - "failedToUploadFile": "Upload af fil mislykkedes", - "fileDownloadedSuccessfully": "Fil \"{{name}}\" downloadet med succes", - "failedToDownloadFile": "Download af fil mislykkedes", - "fileCreatedSuccessfully": "Fil \"{{name}}\" oprettet", - "folderCreatedSuccessfully": "Mappe \"{{name}}\" oprettet", - "failedToCreateItem": "Kunne ikke oprette element", - "operationFailed": "{{operation}} operation mislykkedes for {{name}}: {{error}}", - "failedToResolveSymlink": "Kunne ikke løse symlink", - "itemsDeletedSuccessfully": "{{count}} elementer slettet", - "failedToDeleteItems": "Kunne ikke slette elementer", - "sudoPasswordRequired": "Administrator Adgangskode Kræves", - "enterSudoPassword": "Indtast sudo adgangskode for at fortsætte denne handling", - "sudoPassword": "Sudo adgangskode", - "sudoOperationFailed": "Sudo operation mislykkedes", - "sudoAuthFailed": "Sudo godkendelse mislykkedes", - "dragFilesToUpload": "Slip filer her for at uploade", - "emptyFolder": "Denne mappe er tom", - "searchFiles": "Søg i filer...", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", "upload": "Upload", - "selectHostToStart": "Vælg en vært for at starte filhåndtering", - "sshRequiredForFileManager": "Filhåndtering kræver SSH. Denne vært har ikke SSH aktiveret.", - "failedToConnect": "Kunne ikke forbinde til SSH", - "failedToLoadDirectory": "Kunne ikke indlæse mappe", - "noSSHConnection": "Ingen SSH-forbindelse tilgængelig", - "copy": "Kopiér", - "cut": "Klip", - "paste": "Indsæt", - "copyPath": "Kopiér Sti", - "copyPaths": "Kopiér Stier", - "delete": "Slet", - "properties": "Egenskaber", - "refresh": "Opdater", - "downloadFiles": "Download {{count}} filer til Browser", - "copyFiles": "Kopier {{count}} elementer", - "cutFiles": "Klip {{count}} elementer", - "deleteFiles": "Slet {{count}} elementer", - "filesCopiedToClipboard": "{{count}} elementer kopieret til udklipsholder", - "filesCutToClipboard": "{{count}} elementer klippet til udklipsholder", - "pathCopiedToClipboard": "Sti kopieret til udklipsholder", - "pathsCopiedToClipboard": "{{count}} stier kopieret til udklipsholder", - "failedToCopyPath": "Kunne ikke kopiere sti til udklipsholder", - "movedItems": "Flyttede {{count}} elementer", - "failedToDeleteItem": "Kunne ikke slette element", - "itemRenamedSuccessfully": "{{type}} omdøbt med succes", - "failedToRenameItem": "Kunne ikke omdøbe element", - "download": "Hent", - "permissions": "Rettigheder", - "size": "Størrelse", - "modified": "Ændret", - "path": "Sti", - "confirmDelete": "Er du sikker på, at du vil slette {{name}}?", - "permissionDenied": "Tilladelse nægtet", - "serverError": "Serverfejl", - "fileSavedSuccessfully": "Filen er gemt", - "failedToSaveFile": "Kunne ikke gemme filen", - "confirmDeleteSingleItem": "Er du sikker på, at du vil slette \"{{name}}\"?", - "confirmDeleteMultipleItems": "Er du sikker på du vil slette {{count}} elementer permanent?", - "confirmDeleteMultipleItemsWithFolders": "Er du sikker på, at du vil slette {{count}} elementer? Dette inkluderer mapper og deres indhold.", - "confirmDeleteFolder": "Er du sikker på, at du vil slette mappen \"{{name}}\" og alt dens indhold?", - "permanentDeleteWarning": "Denne handling kan ikke fortrydes. Elementerne slettes permanent fra serveren.", - "recent": "Seneste", - "pinned": "Fastgjort", - "folderShortcuts": "Mappe Genveje", - "failedToReconnectSSH": "Kunne ikke genforbinde SSH session", - "openTerminalHere": "Åbn Terminal Her", - "run": "Kør", - "openTerminalInFolder": "Åbn terminal i denne mappe", - "openTerminalInFileLocation": "Åbn terminal på filplacering", - "runningFile": "Kører - {{file}}", - "onlyRunExecutableFiles": "Kan kun køre eksekverbare filer", - "directories": "Mapper", - "removedFromRecentFiles": "Fjernet \"{{name}}\" fra de seneste filer", - "removeFailed": "Fjernelse mislykkedes", - "unpinnedSuccessfully": "Ufastgjort \"{{name}}\" lykkedes", - "unpinFailed": "Frigørelse mislykkedes", - "removedShortcut": "Fjernet genvej \"{{name}}\"", - "removeShortcutFailed": "Fjernelse af genvej mislykkedes", - "clearedAllRecentFiles": "Ryddede alle seneste filer", - "clearFailed": "Ryd mislykkedes", - "removeFromRecentFiles": "Fjern fra seneste filer", - "clearAllRecentFiles": "Ryd alle seneste filer", - "unpinFile": "Frigør fil", - "removeShortcut": "Fjern genvej", - "pinFile": "Fastgør fil", - "addToShortcuts": "Føj til genveje", - "pasteFailed": "Indsæt mislykkedes", - "noUndoableActions": "Ingen fortrydelige handlinger", - "undoCopySuccess": "Ugjort kopiering: Slettede {{count}} kopierede filer", - "undoCopyFailedDelete": "Fortryd mislykkedes: Kunne ikke slette kopierede filer", - "undoCopyFailedNoInfo": "Fortryd mislykkedes: Kunne ikke finde kopieret filinformation", - "undoMoveSuccess": "Undid flytning operation: Flyttede {{count}} filer tilbage til den oprindelige placering", - "undoMoveFailedMove": "Fortryd mislykkedes: Kunne ikke flytte nogen filer tilbage", - "undoMoveFailedNoInfo": "Fortryd mislykkedes: Kunne ikke finde flyttede filoplysninger", - "undoDeleteNotSupported": "Sletning kan ikke fortrydes: Filer er blevet slettet permanent fra serveren", - "undoTypeNotSupported": "Ikke-understøttet fortryd operationstype", - "undoOperationFailed": "Fortryd handling mislykkedes", - "unknownError": "Ukendt fejl", - "confirm": "Bekræft", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", "find": "Find...", - "replace": "Erstat", - "downloadInstead": "Download I stedet", - "keyboardShortcuts": "Tastaturgenveje", - "searchAndReplace": "Søg & Erstat", - "editing": "Redigering", - "search": "Søg", - "findNext": "Find Næste", - "findPrevious": "Find Forrige", - "save": "Gem", - "selectAll": "Vælg Alle", - "undo": "Fortryd", - "redo": "Gendan", - "moveLineUp": "Flyt Linje Op", - "moveLineDown": "Flyt Linje Ned", - "toggleComment": "Skift Kommentar", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Kunne ikke indlæse billede", - "startTyping": "Begynd at skrive...", - "unknownSize": "Ukendt størrelse", - "fileIsEmpty": "Filen er tom", - "largeFileWarning": "Advarsel Om Stor Fil", - "largeFileWarningDesc": "Denne fil er {{size}} i størrelse, hvilket kan forårsage problemer med ydeevnen når den åbnes som tekst.", - "fileNotFoundAndRemoved": "Fil \"{{name}}\" ikke fundet og er blevet fjernet fra de seneste / fastgjorte filer", - "failedToLoadFile": "Kunne ikke indlæse fil: {{error}}", - "serverErrorOccurred": "Serverfejl opstod. Prøv igen senere.", - "autoSaveFailed": "Auto-lagring mislykkedes", - "fileAutoSaved": "Fil auto-gemt", - "moveFileFailed": "Kunne ikke flytte {{name}}", - "moveOperationFailed": "Flytning mislykkedes", - "canOnlyCompareFiles": "Kan kun sammenligne to filer", - "comparingFiles": "Sammenligner filer: {{file1}} og {{file2}}", - "dragFailed": "Træk handling mislykkedes", - "filePinnedSuccessfully": "Filen \"{{name}}\" fastgjort", - "pinFileFailed": "Kunne ikke fastgøre fil", - "fileUnpinnedSuccessfully": "Filen \"{{name}}\" blev fjernet", - "unpinFileFailed": "Kunne ikke frigøre filen", - "shortcutAddedSuccessfully": "Mappegenvej \"{{name}}\" blev tilføjet", - "addShortcutFailed": "Mislykkedes at tilføje genvej", - "operationCompletedSuccessfully": "{{operation}} {{count}} elementer lykkedes", - "operationCompleted": "{{operation}} {{count}} varer", - "downloadFileSuccess": "Fil {{name}} downloadet med succes", - "downloadFileFailed": "Download mislykkedes", - "moveTo": "Flyt til {{name}}", - "diffCompareWith": "Sammenlign sammenligninger med {{name}}", - "dragOutsideToDownload": "Træk udenfor vindue for at downloade ({{count}} filer)", - "newFolderDefault": "Nyhedsmappe", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Flyttede {{count}} elementer til {{target}}", - "move": "Flyt", - "searchInFile": "Søg i fil (Ctrl+F)", - "showKeyboardShortcuts": "Vis tastaturgenveje", - "startWritingMarkdown": "Begynd at skrive dit markdown indhold...", - "loadingFileComparison": "Indlæser filsammenligning...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Sammenlign", - "sideBySide": "Side om side", - "inline": "Indlejret", - "fileComparison": "Fil sammenligning: {{file1}} vs {{file2}}", - "fileTooLarge": "Filen er for stor: {{error}}", - "sshConnectionFailed": "SSH-forbindelsen mislykkedes. Tjek venligst din forbindelse til {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Kunne ikke indlæse fil: {{error}}", - "connecting": "Forbinder...", - "connectedSuccessfully": "Forbundet med succes", - "totpVerificationFailed": "TOTP verifikation mislykkedes", - "warpgateVerificationFailed": "Godkendelse af Warpgate mislykkedes", - "authenticationFailed": "Godkendelse mislykkedes", - "verificationCodePrompt": "Bekræftelseskode:", - "changePermissions": "Skift Tilladelser", - "currentPermissions": "Nuværende Tilladelser", - "owner": "Ejer", - "group": "Gruppe", - "others": "Andre", - "read": "Læs", - "write": "Skriv", - "execute": "Udfør", - "permissionsChangedSuccessfully": "Tilladelser ændret", - "failedToChangePermissions": "Mislykkedes at ændre tilladelser", - "name": "Navn", - "sortByName": "Navn", - "sortByDate": "Ændret Dato", - "sortBySize": "Størrelse", - "ascending": "Stigende", - "descending": "Faldende", - "root": "Rod", - "new": "Ny", - "sortBy": "Sortér Efter", - "items": "Varer", - "selected": "Valgt", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", - "octal": "Oktal", - "storage": "Lagerplads", + "octal": "Octal", + "storage": "Storage", "disk": "Disk", - "used": "Brugt", - "of": "af", - "toggleSidebar": "Slå Sidebjælke Til/Fra", - "cannotLoadPdf": "Kan ikke indlæse PDF", - "pdfLoadError": "Der opstod en fejl under indlæsning af denne PDF-fil.", - "loadingPdf": "Indlæser PDF...", - "loadingPage": "Indlæser side..." + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopiér til vært…", - "moveToHost": "Flyt til vært…", - "copyItemsToHost": "Kopiér {{count}} elementer til at være vært for…", - "moveItemsToHost": "Flyt {{count}} elementer til at være vært for…", - "noHostsConnected": "Ingen andre filhåndteringsværter tilgængelige.", - "noHostsConnectedHint": "Tilføj en anden SSH-vært med Filhåndtering aktiveret i Værtshåndtering.", - "selectDestinationHost": "Vælg destinationsvært", - "destinationPath": "Destinationssti", - "recentDestinations": "Seneste destinationer", - "collapseRecentDestinations": "Skjul de seneste destinationer", - "expandRecentDestinations": "Udvid de seneste destinationer", - "browseFolders": "Gennemse destinationsmapper", - "browseDestination": "Gennemse eller indtast sti", - "confirmCopy": "Kopi", - "confirmMove": "Flytte", - "transferring": "Overfører…", - "compressing": "Komprimering…", - "extracting": "Udtrækker…", - "transferringItems": "Overførsel af {{current}} af {{total}} elementer…", - "transferSuccess": "Overførsel fuldført", - "transferError": "Overførslen mislykkedes", - "transferPartial": "Overførsel fuldført med {{count}} fejl", - "transferPartialHint": "Kunne ikke overføre: {{paths}}", - "itemsSummary": "{{count}} elementer", - "destMustBeDirectory": "Destinationen skal være en mappe for overførsler af flere elementer", - "selectThisFolder": "Vælg denne mappe", - "browsePathWillBeCreated": "Denne mappe findes ikke endnu. Den vil blive oprettet, når overførslen starter.", - "browsePathError": "Kunne ikke åbne denne sti på destinationsværten.", - "goUp": "Gå op", - "copyFolderToHost": "Kopiér mappe til vært…", - "moveFolderToHost": "Flyt mappe til vært…", - "hostReady": "Parat", - "hostConnecting": "Forbinder…", - "hostDisconnected": "Ikke forbundet", - "hostAuthRequired": "Godkendelse kræves — åbn først Filhåndtering på denne vært", - "hostConnectionFailed": "Forbindelsen mislykkedes", - "metricsTitle": "Overførselstider", - "metricsPrepare": "Forbered destination: {{duration}}", - "metricsCompress": "Komprimer på kilde: {{duration}}", - "metricsHopSourceRead": "Kilde → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → destination (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → destination (lokal): {{throughput}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", - "metricsExtract": "Udtræk på destination: {{duration}}", - "metricsSourceDelete": "Fjern fra kilde: {{duration}}", - "metricsTotal": "I alt: {{duration}}", - "progressCompressing": "Komprimering på kildevært…", - "progressExtracting": "Udpakning på destination…", - "progressTransferring": "Overførsel af data…", - "progressReconnecting": "Genopretter forbindelse…", - "parallelSegmentsLabel": "Parallelle overførselsbaner", - "parallelSegmentsOption": "{{count}} baner", - "parallelSegmentsHint": "Store filer opdeles i bidder på 256 MB. Flere baner bruger separate forbindelser (f.eks. at starte flere overførsler) for at opnå en højere samlet gennemløbshastighed.", - "progressTotalSpeed": "{{speed}} i alt ({{lanes}} baner)", - "progressTransferringItems": "Overførsel af filer ({{current}} af {{total}})…", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} filer", - "sourceNotDeletedPartial": "Kildefiler gemt (delvis overførsel)", - "jumpHostLimitation": "Begge værter skal kunne nås fra Termix-serveren. Direkte vært-til-vært-routing understøttes ikke.", - "cancel": "Ophæve", - "methodLabel": "Overførselsmetode", - "methodAuto": "Bil", - "methodTar": "Tar-arkiv", - "methodItemSftp": "SFTP pr. fil", - "methodAutoHint": "Vælger tar eller SFTP pr. fil baseret på antal filer, størrelse og komprimerbarhed. Enkeltfiler bruger altid streaming SFTP.", - "methodTarHint": "Komprimer ved kildekode, overfør ét arkiv, udpak ved destination. Kræver tar på begge Unix-værter.", - "methodItemSftpHint": "Overfør hver fil individuelt via SFTP. Fungerer på alle værter, inklusive Windows.", - "methodPreviewLoading": "Beregning af overførselsmetode…", - "methodPreviewError": "Kunne ikke forhåndsvise overførselsmetoden. Serveren vil stadig vælge en metode, når du starter.", - "methodPreviewWillUseTar": "Vil bruge: Tar-arkiv", - "methodPreviewWillUseItemSftp": "Vil bruge: SFTP pr. fil", - "methodPreviewScanSummary": "{{fileCount}} filer, {{totalSize}} i alt (scannet på kildevært).", - "methodItemSftpLimitation": "Hver fil bruger den samme SFTP-strøm som en enkelt filkopi, én efter én. Status kombineres på tværs af alle filer, så bjælken bevæger sig langsomt under store filer.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Du valgte SFTP pr. fil.", - "user_tar": "Du valgte tar-arkivet.", - "tar_unavailable": "Tar er ikke tilgængelig på en eller begge værter — SFTP pr. fil vil blive brugt i stedet.", - "windows_host": "En Windows-vært er involveret — tar bruges ikke.", - "auto_multi_large": "Auto: flere filer inklusive en stor fil ({{largestSize}}) med komprimerbare data — tar samler filer i én overførsel.", - "auto_single_large_in_archive": "Auto: én stor fil ({{largestSize}}) i dette sæt — SFTP pr. fil.", - "auto_many_incompressible": "Auto: for det meste inkomprimerbare data — SFTP pr. fil.", - "auto_many_files": "Auto: mange filer ({{fileCount}}) — tar reducerer overhead pr. fil.", - "auto_default": "Auto: SFTP pr. fil for dette sæt." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Ophæve", - "progressCancelling": "Annullerer…", - "progressStalled": "Standset", - "resumedHint": "Genoprettet forbindelse til en aktiv overførsel, der er startet i et andet vindue.", - "transferCancelled": "Overførsel annulleret", - "transferCancelledCopyHint": "Delvise filer kan forblive på destinationen.", - "transferCancelledMoveHint": "Delvise filer kan forblive på destinationen. Kildefilerne blev ikke fjernet.", - "cleanupDestFiles": "Oprydningsdestination", - "cleanupDestFilesSuccess": "Fjernede delvise filer fra destinationen", - "cleanupDestFilesPartial": "Nogle delvise filer kunne ikke fjernes", - "cleanupDestFilesNothing": "Intet at rydde op på destinationen", - "cleanupDestFilesError": "Oprydning mislykkedes", - "retryTransfer": "Prøv igen", - "retryTransferError": "Genforsøg mislykkedes", - "transferFailedRetryHint": "Delvise data blev gemt på destinationen. Forsøget genoptages, når forbindelsen er tilbage." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Ingen SSH-tunneler", - "createFirstTunnelMessage": "Du har endnu ikke oprettet nogen SSH-tunneler. Konfigurer tunnelforbindelser i værtshåndteringen for at komme i gang.", - "connected": "Forbundet", - "disconnected": "Afbrudt", - "connecting": "Forbinder...", - "error": "Fejl", - "canceling": "Annullerer...", - "connect": "Forbind", - "disconnect": "Afbryd", - "cancel": "Annuller", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Lokal Port", - "remotePort": "Ekstern Port", - "currentHostPort": "Nuværende Værts Port", - "endpointPort": "Endepunkt Port", - "bindIp": "Lokal IP", - "endpointSshConfig": "Indstilling Af Endepunkt SSH", - "endpointSshHost": "Endepunkt SSH Host", - "endpointSshHostPlaceholder": "Vælg en konfigureret vært", - "endpointSshHostRequired": "Vælg en SSH-vært for hver klienttunnel.", - "attempt": "Forsøg {{current}} af {{max}}", - "nextRetryIn": "Næste forsøg igen i {{seconds}} sekunder", - "clientTunnels": "Klient Tunneler", - "clientTunnel": "Klient Tunnel", - "addClientTunnel": "Tilføj Klienttunnel", - "noClientTunnels": "Ingen klienttunneler konfigureret på dette skrivebord.", - "tunnelName": "Tunnel Navn", - "remoteHost": "Ekstern Vært", - "autoStart": "Automatisk Start", - "clientAutoStartDesc": "Starter når denne desktop klient åbner og forbliver tilsluttet.", - "clientManualStartDesc": "Brug Start og Stop fra denne række. Termix vil ikke åbne den automatisk.", - "clientRemoteServerNote": "Fjernvideresendelse kan kræve AllowTcpForwarding og GatewayPorts på SSH-slutserveren. Fjernporten lukker, når skrivebordet afbryder.", - "clientTunnelStarted": "Klienttunnel startet", - "clientTunnelStopped": "Klient tunnel stoppet", - "tunnelTestSucceeded": "Tunneltesten lykkedes", - "tunnelTestFailed": "Tunneltest mislykkedes", - "localSaved": "Klienttunneler gemt", - "localSaveError": "Kunne ikke gemme lokale klienttunneler", - "invalidBindIp": "Lokal IP skal være en gyldig IPv4-adresse.", - "invalidLocalTargetIp": "Lokalt mål IP skal være en gyldig IPv4-adresse.", - "invalidLocalPort": "Lokal port skal være mellem 1 og 65535.", - "invalidRemotePort": "Fjernport skal være mellem 1 og 65535.", - "invalidLocalTargetPort": "Lokal målport skal være mellem 1 og 65535.", - "invalidEndpointPort": "Endepunkthavn skal være mellem 1 og 65535.", - "duplicateAutoStartBind": "Kun én auto-start klienttunnel kan bruge {{bind}}.", - "manualControlError": "Mislykkedes at opdatere tunneltilstand.", - "active": "Aktiv", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", "start": "Start", "stop": "Stop", "test": "Test", "type": "Tunnel Type", - "typeLocal": "Lokal (-L)", - "typeRemote": "Fjernbetjening (-R)", - "typeDynamic": "Dynamisk (-D)", - "typeServerLocalDesc": "Nuværende vært for slutpunktet.", - "typeServerRemoteDesc": "Endepunkt tilbage til nuværende vært.", - "typeClientLocalDesc": "Lokal computer til slutpunkt.", - "typeClientRemoteDesc": "Slutpunkt tilbage til lokal computer.", - "typeClientDynamicDesc": "SOCKS på lokal computer.", - "typeDynamicDesc": "Videresend SOCKS5 CONNECT trafik gennem SSH", - "forwardDescriptionServerLocal": "Nuværende vært {{sourcePort}} → endpoint {{endpointPort}}.", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS på nuværende vært {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokal {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS på lokal port {{sourcePort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "Lokal {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → Lokal {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", - "route": "Rute:", - "lastStarted": "Sidst startet", - "lastTested": "Sidst testet", - "lastError": "Seneste fejl", - "maxRetries": "Maks. Forsøg", - "maxRetriesDescription": "Maksimal mængde af forsøg igen.", - "retryInterval": "Forsøg Interval (sekunder)", - "retryIntervalDescription": "Tid til at vente mellem forsøg igen.", - "local": "Lokal", - "remote": "Fjernbetjening", - "destination": "Bestemmelse", - "host": "Vært", - "mode": "Tilstand", - "noHostSelected": "Ingen vært valgt", - "working": "Arbejder..." + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Hukommelse", + "memory": "Memory", "disk": "Disk", - "network": "Netværk", - "uptime": "Oppetid", - "processes": "Processer", - "available": "Tilgængelig", - "free": "Gratis", - "connecting": "Forbinder...", - "connectionFailed": "Kunne ikke forbinde til serveren", - "naCpus": "N/A CPU(er)", - "cpuCores_one": "{{count}} Kerne", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU Forbrug", - "memoryUsage": "Hukommelsesforbrug", - "diskUsage": "Diskforbrug", - "failedToFetchHostConfig": "Kunne ikke hente værtskonfiguration", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", "serverOffline": "Server Offline", - "cannotFetchMetrics": "Kan ikke hente målinger fra offline server", - "totpFailed": "TOTP verifikation mislykkedes", - "noneAuthNotSupported": "Serverstatistik understøtter ikke 'intet' godkendelsestype.", - "load": "Indlæs", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Operativsystem", - "kernel": "Kerne", - "seconds": "sekunder", - "networkInterfaces": "Netværksgrænseflader", - "noInterfacesFound": "Ingen netværksgrænseflader fundet", - "noProcessesFound": "Ingen processer fundet", - "loginStats": "SSH Login Statistik", - "noRecentLoginData": "Ingen seneste login-data", - "executingQuickAction": "Udfører {{name}}...", - "quickActionSuccess": "{{name}} fuldført", - "quickActionFailed": "{{name}} mislykkedes", - "quickActionError": "Kunne ikke udføre {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Lytter Havne", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adresse", - "process": "Proces", - "noData": "Ingen lytter porte data" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inaktiv", - "policy": "Politik", - "rules": "regler", - "noData": "Ingen firewall-data tilgængelige", - "action": "Handling", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Kilde", - "anywhere": "Overalt" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Indlæs Gns.", - "swap": "Byt", - "architecture": "Arkitektur", - "refresh": "Opdater", - "retry": "Forsøg igen" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Selvhostet SSH og fjernskrivebordsadministration", - "loginTitle": "Log ind på Termix", - "registerTitle": "Opret Konto", - "forgotPassword": "Glemt Adgangskode?", - "rememberMe": "Husk enheden for 30 dage (inkluderer TOTP)", - "noAccount": "Har du ikke en konto?", - "hasAccount": "Har du allerede en konto?", - "twoFactorAuth": "To-Faktor Godkendelse", - "enterCode": "Indtast bekræftelseskode", - "backupCode": "Eller brug sikkerhedskode", - "verifyCode": "Verificér Kode", - "redirectingToApp": "Omdirigerer til app...", - "sshAuthenticationRequired": "SSH Godkendelse Kræves", - "sshNoKeyboardInteractive": "Tastatur-Interaktiv Godkendelse Ikke Tilgængelig", - "sshAuthenticationFailed": "Godkendelse Mislykkedes", - "sshAuthenticationTimeout": "Godkendelse Timeout", - "sshNoKeyboardInteractiveDescription": "Serveren understøtter ikke tastaturinteraktiv godkendelse. Angiv venligst din adgangskode eller SSH-nøgle.", - "sshAuthFailedDescription": "De angivne legitimationsoplysninger var forkert. Prøv igen med gyldige legitimationsoplysninger.", - "sshTimeoutDescription": "Godkendelsesforsøget fik timeout. Prøv venligst igen.", - "sshProvideCredentialsDescription": "Angiv venligst dine SSH-legitimationsoplysninger for at oprette forbindelse til denne server.", - "sshPasswordDescription": "Indtast adgangskoden til denne SSH-forbindelse.", - "sshKeyPasswordDescription": "Hvis din SSH-nøgle er krypteret, så skriv adgangskoden her.", - "passphraseRequired": "Kodeord Kræves", - "passphraseRequiredDescription": "SSH-tasten er krypteret. Indtast kodeord for at låse den op.", - "back": "Tilbage", - "firstUser": "Første Bruger", - "firstUserMessage": "Du er den første bruger og vil blive lavet en admin. Du kan se admin indstillinger i sidebar bruger dropdown. Hvis du mener, at dette er en fejl, tjek docker logs, eller opret et GitHub problem.", - "external": "Ekstern", - "loginWithExternal": "Log ind med ekstern udbyder", - "loginWithExternalDesc": "Login ved hjælp af din konfigurerede eksterne identitetsudbyder", - "externalNotSupportedInElectron": "Ekstern godkendelse er endnu ikke understøttet i Electron appen. Brug venligst webversionen til OIDC login.", - "resetPasswordButton": "Nulstil Adgangskode", - "sendResetCode": "Send Nulstillingskode", - "resetCodeDesc": "Indtast dit brugernavn for at modtage en adgangskode nulstillingskode. Koden vil blive logget i docker container logs.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verificér Kode", - "enterResetCode": "Indtast den 6-cifrede kode fra docker container logger for brugeren:", - "newPassword": "Ny Adgangskode", - "confirmNewPassword": "Bekræft Adgangskode", - "enterNewPassword": "Indtast din nye adgangskode for brugeren:", - "signUp": "Tilmeld Dig", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", "desktopApp": "Desktop App", - "loggingInToDesktopApp": "Logger ind på skrivebords-appen", - "loadingServer": "Indlæser server...", - "dataLossWarning": "Nulstilling af din adgangskode på denne måde vil slette alle dine gemte SSH værter, legitimationsoplysninger og andre krypterede data. Denne handling kan ikke fortrydes. Brug kun dette, hvis du har glemt din adgangskode og ikke er logget ind.", - "authenticationDisabled": "Godkendelse Deaktiveret", - "authenticationDisabledDesc": "Alle godkendelsesmetoder er i øjeblikket deaktiveret. Kontakt venligst din administrator.", - "attemptsRemaining": "{{count}} forsøg tilbage" + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verificér SSH-værtnøgle", - "keyChangedWarning": "SSH Værtsnøgle Ændret", - "firstConnectionTitle": "Første gang der oprettes forbindelse til værten", - "firstConnectionDescription": "Denne værts ægthed kan ikke fastslås. Kontrollér, at fingeraftrykket matcher det, du forventer.", - "keyChangedDescription": "Værtsnøglen for denne server er ændret siden din sidste forbindelse. Dette kan indikere et sikkerhedsproblem.", - "previousKey": "Forrige Nøgle", - "newFingerprint": "Nyt Fingeraftryk", - "fingerprint": "Fingeraftryk", - "verifyInstructions": "Hvis du stoler på denne vært, skal du klikke på Accepter for at fortsætte og gemme dette fingeraftryk til fremtidige forbindelser.", - "securityWarning": "Advarsel Om Sikkerhed", - "acceptAndContinue": "Accepter & Fortsæt", - "acceptNewKey": "Accepter Ny Nøgle Og Fortsæt" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Kunne ikke forbinde til databasen", - "unknownError": "Ukendt fejl", - "loginFailed": "Login mislykkedes", - "failedPasswordReset": "Kunne ikke starte nulstilling af adgangskode", - "failedVerifyCode": "Kunne ikke bekræfte nulstillingskode", - "failedCompleteReset": "Kunne ikke fuldføre nulstilling af adgangskode", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Mislykkedes at starte OIDC login", - "silentSigninOidcUnavailable": "Der blev anmodet om lydløs login, men OIDC-login er ikke tilgængeligt.", - "failedUserInfo": "Kunne ikke hente brugerinfo efter login", - "oidcAuthFailed": "OIDC godkendelse mislykkedes", - "invalidAuthUrl": "Ugyldig autorisation URL modtaget fra backend", - "requiredField": "Dette felt er påkrævet", - "minLength": "Minimum længde er {{min}}", - "passwordMismatch": "Adgangskoder stemmer ikke overens", - "passwordLoginDisabled": "Brugernavn/adgangskode login er i øjeblikket deaktiveret", - "sessionExpired": "Session udløbet - log venligst ind igen", - "totpRateLimited": "Begrænset hastighed: For mange TOTP-bekræftelsesforsøg. Prøv igen senere.", - "totpRateLimitedWithTime": "Begrænset hastighed: For mange TOTP-bekræftelsesforsøg. Vent venligst {{time}} sekunder, før du prøver igen.", - "resetCodeRateLimited": "Begrænset sats: For mange bekræftelsesforsøg. Prøv igen senere.", - "resetCodeRateLimitedWithTime": "Begrænset sats: For mange bekræftelsesforsøg. Vent venligst {{time}} sekunder, før du prøver igen.", - "authTokenSaveFailed": "Kunne ikke gemme godkendelsestoken", - "failedToLoadServer": "Kunne ikke indlæse server" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Ny konto registrering er i øjeblikket deaktiveret af en administrator. Log ind eller kontakt en administrator.", - "userNotAllowed": "Din konto er ikke autoriseret til at registrere. Kontakt venligst en administrator.", - "databaseConnectionFailed": "Kunne ikke forbinde til databaseserveren", - "resetCodeSent": "Nulstil kode sendt til Docker logs", - "codeVerified": "Kode bekræftet", - "passwordResetSuccess": "Adgangskode nulstillet", - "loginSuccess": "Login lykkedes", - "registrationSuccess": "Registrering gennemført" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokale stationære tunneler rettet mod konfigurerede SSH-værter.", - "c2sTunnelPresets": "Klient Tunnel Forudindstillinger", - "c2sTunnelPresetsDesc": "Gem denne desktop klients lokale tunnelliste som en navngiven server forudindstilling, eller indlæs en forudindstillet tilbage i denne klient.", - "c2sTunnelPresetsUnavailable": "Klienttunnelforudindstillinger er kun tilgængelige i skrivebordsklienten.", - "c2sPresetName": "Forudindstillet Navn", - "c2sPresetNamePlaceholder": "Klient forudindstillede navn", - "c2sPresetToLoad": "Forudindstilling For At Indlæse", - "c2sNoPresetSelected": "Ingen forudindstilling valgt", - "c2sNoPresets": "Ingen forudindstillinger gemt", - "c2sLoadPreset": "Indlæs", - "c2sCurrentLocalConfig": "{{count}} lokal klienttunnel(er) konfigureret på dette skrivebord.", - "c2sPresetSyncNote": "Forudindstillinger er eksplicitte snapshots; indlæsning af en erstatter denne desktop klients lokale klienttunnelliste.", - "c2sPresetSaved": "Klient tunnel forudindstilling gemt", - "c2sPresetLoaded": "Forudindstillede klienttunnel indlæst lokalt", - "c2sPresetRenamed": "Klient tunnel forudindstillet omdøbt", - "c2sPresetDeleted": "Klient tunnel forudindstilling slettet", - "c2sPresetLoadError": "Kunne ikke indlæse klienttunnelforudindstillinger" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Sprog", - "keyPassword": "nøgleadgangskode", - "pastePrivateKey": "Indsæt din private nøgle her...", - "localListenerHost": "127.0.0.1 (lyt lokalt)", - "localTargetHost": "127.0.0.1 (mål på denne computer)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Indtast din adgangskode", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Instrumentbræt", - "loading": "Indlæser dashboard...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", "support": "Support", - "discord": "Uenighed", - "serverOverview": "Server Oversigt", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", "version": "Version", - "upToDate": "Op til dato", - "updateAvailable": "Opdatering Tilgængelig", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Oppetid", + "uptime": "Uptime", "database": "Database", - "healthy": "Sunde", - "error": "Fejl", - "totalHosts": "Total Værter", - "totalTunnels": "Tunneler I Alt", - "totalCredentials": "Total Legitimationsoplysninger", - "recentActivity": "Seneste Aktivitet", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Indlæser seneste aktivitet...", - "noRecentActivity": "Ingen seneste aktivitet", - "quickActions": "Hurtige Handlinger", - "addHost": "Tilføj Vært", - "addCredential": "Tilføj Credential", - "adminSettings": "Admin Indstillinger", - "userProfile": "Bruger Profil", - "serverStats": "Server Statistik", - "loadingServerStats": "Indlæser serverstatistik...", - "noServerData": "Ingen tilgængelige serverdata", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Tilpas Dashboard", - "dashboardSettings": "Indstillinger For Kontrolpanel", - "enableDisableCards": "Aktiver/Deaktiver Kort", - "resetLayout": "Nulstil til standard", - "serverOverviewCard": "Server Oversigt", - "recentActivityCard": "Seneste Aktivitet", - "networkGraphCard": "Netværk Graf", - "networkGraph": "Netværk Graf", - "quickActionsCard": "Hurtige Handlinger", - "serverStatsCard": "Server Statistik", - "panelMain": "Hoved", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", "panelSide": "Side", - "justNow": "lige nu" + "justNow": "just now" }, "dashboardTab": { - "stable": "STABEL", - "hostsOnline": "Værter Online", - "activeTunnels": "Aktive Tunneler", - "registerNewServer": "Registrer en ny server", - "storeSshKeysOrPasswords": "Gem SSH-nøgler eller adgangskoder", - "manageUsersAndRoles": "Administrer brugere og roller", - "manageYourAccount": "Administrer din konto", - "hostStatus": "Værts Status", - "noHostsConfigured": "Ingen værter konfigureret", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Tilføj:", - "commandPalette": "Kommando Palet", - "done": "Udført", - "editModeInstructions": "Træk kort for at omarrangere · Træk kolonneskillefeltet for at ændre størrelse kolonner · Træk den nederste kant af et kort for at ændre størrelsen på dets højde · Papirkurv for at fjerne", - "empty": "Tom", - "clear": "Ryd" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Tilføj Vært", - "addGroup": "Tilføj Gruppe", - "addLink": "Tilføj Link", - "zoomIn": "Zoom Ind", - "zoomOut": "Zoom Ud", - "resetView": "Nulstil Visning", - "selectHost": "Vælg Vært", - "chooseHost": "Vælg en vært...", - "parentGroup": "Overordnet Gruppe", - "noGroup": "Ingen Gruppe", - "groupName": "Gruppens Navn", - "color": "Farve", - "source": "Kilde", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Flyt til gruppe", - "selectGroup": "Vælg gruppe...", - "addConnection": "Tilføj Forbindelse", - "hostDetails": "Vært Detaljer", - "removeFromGroup": "Fjern fra gruppe", - "addHostHere": "Tilføj Vært Her", - "editGroup": "Rediger Gruppe", - "delete": "Slet", - "add": "Tilføj", - "create": "Opret", - "move": "Flyt", - "connect": "Forbind", - "createGroup": "Opret Gruppe", - "selectSourcePlaceholder": "Vælg Kilde...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Ugyldig Fil", - "hostAlreadyExists": "Værten er allerede i topologien", - "connectionExists": "Forbindelsen findes allerede", - "unknown": "Ukendt", - "name": "Navn", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", "status": "Status", - "failedToAddNode": "Kunne ikke tilføje indholdselement", - "sourceDifferentFromTarget": "Kilde og mål skal være forskellige", - "exportJSON": "Eksporter JSON", - "importJSON": "Importér JSON", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Filhåndtering", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Server Statistik", - "noNodes": "Ingen indholdselementer endnu" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker er ikke aktiveret for denne vært", - "validating": "Validerer Docker...", - "connecting": "Forbinder...", - "error": "Fejl", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Kunne ikke forbinde til Docker", - "containerStarted": "Container {{name}} startet", - "failedToStartContainer": "Kunne ikke starte container {{name}}", - "containerStopped": "Container {{name}} stoppet", - "failedToStopContainer": "Kunne ikke stoppe containeren {{name}}", - "containerRestarted": "Container {{name}} genstartet", - "failedToRestartContainer": "Kunne ikke genstarte container {{name}}", - "containerPaused": "Container {{name}} sat på pause", - "containerUnpaused": "Container {{name}} ikke sat på pause", - "failedToTogglePauseContainer": "Kunne ikke skifte pausetilstand for beholder {{name}}", - "containerRemoved": "Container {{name}} fjernet", - "failedToRemoveContainer": "Kunne ikke fjerne container {{name}}", - "image": "Billede", - "ports": "Havne", - "noPorts": "Ingen porte", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", "start": "Start", - "confirmRemoveContainer": "Er du sikker på, at du vil fjerne beholderen '{{name}}'? Denne handling kan ikke fortrydes.", - "runningContainerWarning": "Advarsel: Denne beholder kører i øjeblikket. Fjernelse af den vil først stoppe beholderen.", - "loadingContainers": "Indlæser containere...", - "manager": "Håndtering Af Docker", - "autoRefresh": "Opdater Automatisk", - "timestamps": "Tidsstempler", - "lines": "Linjer", - "filterLogs": "Filtrer logger...", - "refresh": "Opdater", - "download": "Hent", - "clear": "Ryd", - "logsDownloaded": "Logs downloadet", - "last50": "Sidste 50", - "last100": "Sidste 100", - "last500": "Sidste 500", - "last1000": "Sidste 1000", - "allLogs": "Alle Logs", - "noLogsMatching": "Ingen logs der matcher \"{{query}}\"", - "noLogsAvailable": "Ingen logs tilgængelige", - "noContainersFound": "Ingen containere fundet", - "noContainersFoundHint": "Ingen Docker-containere er tilgængelige på denne vært", - "searchPlaceholder": "Søg containere...", - "allStatuses": "Alle Status", - "stateRunning": "Kører", - "statePaused": "Pauset", - "stateExited": "Afsluttet", - "stateRestarting": "Genstarter", - "noContainersMatchFilters": "Ingen beholdere matcher dine filtre", - "noContainersMatchFiltersHint": "Prøv at justere dine søgnings- eller filterkriterier", - "failedToFetchStats": "Kunne ikke hente containerstatistik", - "containerNotRunning": "Container kører ikke", - "startContainerToViewStats": "Start beholderen for at se statistik", - "loadingStats": "Indlæser statistikker...", - "errorLoadingStats": "Fejl under indlæsning af statistik", - "noStatsAvailable": "Ingen statistik tilgængelig", - "cpuUsage": "CPU Forbrug", - "current": "Aktuel", - "memoryUsage": "Hukommelsesforbrug", - "networkIo": "Netværk I/O", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", "output": "Output", - "blockIo": "I/O-blok", - "read": "Læs", - "write": "Skriv", - "pids": "PID'er", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", "containerInformation": "Container Information", - "name": "Navn", + "name": "Name", "id": "ID", - "state": "Stat", - "containerMustBeRunning": "Container skal køre for at få adgang til konsollen", - "verificationCodePrompt": "Indtast bekræftelseskode", - "totpVerificationFailed": "TOTP verifikation mislykkedes. Prøv venligst igen.", - "warpgateVerificationFailed": "Godkendelse af Warpgate mislykkedes. Prøv venligst igen.", - "connectedTo": "Forbundet til {{containerName}}", - "disconnected": "Afbrudt", - "consoleError": "Konsol fejl", - "errorMessage": "Fejl: {{message}}", - "failedToConnect": "Kunne ikke forbinde til container", - "console": "Konsol", - "selectShell": "Vælg skal", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "aske", - "connect": "Forbind", - "disconnect": "Afbryd", - "notConnected": "Ikke forbundet", - "clickToConnect": "Klik på Opret forbindelse for at starte en skalsession", - "connectingTo": "Forbinder til {{containerName}}...", - "containerNotFound": "Container ikke fundet", - "backToList": "Tilbage til liste", - "logs": "Logfiler", - "stats": "Statistik", - "consoleTab": "Konsol", - "startContainerToAccess": "Start beholderen for at få adgang til konsollen" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Generelt", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Brugere", - "sectionSessions": "Sessioner", - "sectionRoles": "Roller", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", "sectionDatabase": "Database", - "sectionApiKeys": "Api Nøgler", - "allowRegistration": "Tillad Brugerregistrering", - "allowRegistrationDesc": "Lad nye brugere registrere sig selv", - "allowPasswordLogin": "Tillad Adgangskode Login", - "allowPasswordLoginDesc": "Brugernavn/adgangskode login", - "oidcAutoProvision": "OIDC Auto-Tilsyn", - "oidcAutoProvisionDesc": "Auto-opret konti for OIDC-brugere, selv når registrering er deaktiveret", - "allowPasswordReset": "Tillad Nulstilling Af Adgangskode", - "allowPasswordResetDesc": "Nulstil kode via Docker-logs", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", "sessionTimeout": "Session Timeout", - "hours": "timer", - "sessionTimeoutRange": "Minimum 1t · Max 720h", - "monitoringDefaults": "Overvågning Af Standarder", - "statusCheck": "Status Tjek", - "metrics": "Metrik", - "sec": "sek", - "logLevel": "Log Niveau", - "enableGuacamole": "Aktiver Guacamole", - "enableGuacamoleDesc": "RDP/VNC fjernskrivebord", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Indstil OpenID Connect for SSO. Felter markeret * er obligatoriske.", - "oidcClientId": "Klient ID", - "oidcClientSecret": "Klient Hemmelighed", - "oidcAuthUrl": "Godkendelses URL", - "oidcIssuerUrl": "Udsteders URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "Token URL", - "oidcUserIdentifier": "Bruger Id Sti", - "oidcDisplayName": "Vis Navn Sti", - "oidcScopes": "Anvendelsesområde", - "oidcUserinfoUrl": "Tilsidesæt Brugerinfo URL", - "oidcAllowedUsers": "Tilladt Brugere", - "oidcAllowedUsersDesc": "En e-mail pr. linje. Efterlad blank for at tillade alle.", - "removeOidc": "Fjern", - "usersCount": "{{count}} brugere", - "createUser": "Opret", - "newRole": "Ny Rolle", - "roleName": "Navn", - "roleDisplayName": "Vis Navn", - "roleDescription": "Varebeskrivelse", - "rolesCount": "{{count}} roller", - "createRole": "Opret", - "creating": "Opretter...", - "exportDatabase": "Eksporter Database", - "exportDatabaseDesc": "Download en sikkerhedskopi af alle værter, legitimationsoplysninger og indstillinger", - "export": "Eksporter", - "exporting": "Eksporterer...", - "importDatabase": "Importér Database", - "importDatabaseDesc": "Gendan fra en .sqlite backup-fil", - "importDatabaseSelected": "Valgt: {{name}}", - "selectFile": "Vælg Fil", - "changeFile": "Skift", - "import": "Importér", - "importing": "Importerer...", - "apiKeysCount": "{{count}} nøgler", - "newApiKey": "Ny API-nøgle", - "apiKeyCreatedWarning": "Nøgle oprettet - kopiere det nu, vil det ikke blive vist igen.", - "apiKeyName": "Navn", - "apiKeyUser": "Bruger", - "apiKeySelectUser": "Vælg en bruger...", - "apiKeyExpiresAt": "Udløber Ved", - "createKey": "Opret Nøgle", - "apiKeyNoExpiry": "Ingen udløbsdato", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Dobbelt Auth", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokal", + "authTypeLocal": "Local", "adminStatusAdministrator": "Administrator", - "adminStatusRegularUser": "Almindelig Bruger", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "TOLD", - "youBadge": "DU", - "sessionsActive": "{{count}} aktiv", - "sessionActive": "Aktiv: {{time}}", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Alle", - "revokeAllSessionsSuccess": "Alle sessioner for brugeren tilbagekaldt", - "revokeAllSessionsFailed": "Mislykkedes at tilbagekalde sessioner", - "revokeSessionFailed": "Mislykkedes at tilbagekalde session", - "addRole": "Tilføj rolle", - "noCustomRoles": "Ingen brugerdefinerede roller defineret", - "removeRoleFailed": "Kunne ikke fjerne rollen", - "assignRoleFailed": "Tildeling af rolle mislykkedes", - "deleteRoleFailed": "Kunne ikke slette rollen", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Fuld adgang til alle administratorindstillinger", - "userRoles": "Roller", - "revokeAllUserSessions": "Tilbagekald Alle Sessioner", - "revokeAllUserSessionsDesc": "Tving gen-login på alle enheder", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Sletning af denne bruger er permanent.", - "deleteUser": "Slet {{username}}", - "deleting": "Sletter...", - "deleteUserFailed": "Kunne ikke slette bruger", - "deleteUserSuccess": "Bruger \"{{username}}\" slettet", - "deleteRoleSuccess": "Rolle \"{{name}}\" slettet", - "revokeKeySuccess": "Nøgle \"{{name}}\" tilbagekaldt", - "revokeKeyFailed": "Kunne ikke tilbagekalde nøgle", - "copiedToClipboard": "Kopieret til udklipsholder", - "done": "Udført", - "createUserTitle": "Opret Bruger", - "createUserDesc": "Opret en ny lokal konto.", - "createUserUsername": "Brugernavn", - "createUserPassword": "Adgangskode", - "createUserPasswordHint": "Minimum 6 tegn.", - "createUserEnterUsername": "Indtast brugernavn", - "createUserEnterPassword": "Indtast adgangskode", - "createUserSubmit": "Opret Bruger", - "editUserTitle": "Administrer Bruger: {{username}}", - "editUserDesc": "Rediger roller, admin status, sessioner og kontoindstillinger.", - "editUserUsername": "Brugernavn", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", "editUserAuthType": "Auth Type", - "editUserAdminStatus": "Administrator Status", - "editUserUserId": "Bruger ID", - "linkAccountTitle": "Link OIDC til adgangskodekonto", - "linkAccountDesc": "Flet OIDC-kontoen {{username}} med en eksisterende lokal konto.", - "linkAccountWarningTitle": "Dette vil:", - "linkAccountEffect1": "Slet den eneste OIDC-konto", - "linkAccountEffect2": "Tilføj OIDC login til målkontoen", - "linkAccountEffect3": "Tillad både OIDC og adgangskode login", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Indtast det lokale brugernavn til at linke til", - "linkAccounts": "Link Konti", - "linkAccountSuccess": "OIDC-konto linket til \"{{username}}\"", - "linkAccountFailed": "Kunne ikke linke OIDC-konto", - "linkAccountInProgress": "Linker...", - "saving": "Gemmer...", - "updateRegistrationFailed": "Kunne ikke opdatere registreringsopsætning", - "updatePasswordLoginFailed": "Kunne ikke opdatere adgangskode login indstilling", - "updateOidcAutoProvisionFailed": "Kunne ikke opdatere OIDC auto-bestemmelses indstilling", - "updatePasswordResetFailed": "Kunne ikke opdatere indstillingen for nulstilling af adgangskode", - "sessionTimeoutRange2": "Session timeout skal være mellem 1 og 720 timer", - "sessionTimeoutSaved": "Session timeout gemt", - "sessionTimeoutSaveFailed": "Kunne ikke gemme sessions timeout", - "monitoringIntervalInvalid": "Ugyldige intervalværdier", - "monitoringSaved": "Overvågningsindstillinger gemt", - "monitoringSaveFailed": "Kunne ikke gemme overvågningsindstillinger", - "guacamoleSaved": "Guacamole indstillinger gemt", - "guacamoleSaveFailed": "Kunne ikke gemme Guacamole indstillinger", - "guacamoleUpdateFailed": "Kunne ikke opdatere Guacamole indstilling", - "logLevelUpdateFailed": "Kunne ikke opdatere logniveau", - "oidcSaved": "OIDC konfiguration gemt", - "oidcSaveFailed": "Kunne ikke gemme OIDC konfiguration", - "oidcRemoved": "OIDC konfiguration fjernet", - "oidcRemoveFailed": "Kunne ikke fjerne OIDC konfiguration", - "createUserRequired": "Brugernavn og adgangskode er påkrævet", - "createUserPasswordTooShort": "Adgangskoden skal være på mindst 6 tegn", - "createUserSuccess": "Bruger \"{{username}}\" oprettet", - "createUserFailed": "Kunne ikke oprette bruger", - "updateAdminStatusFailed": "Mislykkedes at opdatere admin status", - "allSessionsRevoked": "Alle sessioner tilbagekaldt", - "revokeSessionsFailed": "Mislykkedes at tilbagekalde sessioner", - "createRoleRequired": "Navn og visningsnavn er påkrævet", - "createRoleSuccess": "Rolle \"{{name}}\" oprettet", - "createRoleFailed": "Kunne ikke oprette rolle", - "apiKeyNameRequired": "Nøgle navn er påkrævet", - "apiKeyUserRequired": "Bruger ID er påkrævet", - "apiKeyCreatedSuccess": "API-nøgle \"{{name}}\" oprettet", - "apiKeyCreateFailed": "Kunne ikke oprette API-nøgle", - "exportSuccess": "Database eksporteret succesfuldt", - "exportFailed": "Database eksport mislykkedes", - "importSelectFile": "Vælg først en fil", - "importCompleted": "Import fuldført: {{total}} elementer importeret, {{skipped}} sprunget over", - "importFailed": "Import mislykkedes: {{error}}", - "importError": "Database import mislykkedes" + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Vært", - "hostPlaceholder": "192.168.1.1 eller eksempel.com", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Brugernavn", - "usernamePlaceholder": "brugernavn", + "usernameLabel": "Username", + "usernamePlaceholder": "username", "authLabel": "Auth", - "passwordLabel": "Adgangskode", - "passwordPlaceholder": "adgangskode", - "privateKeyLabel": "Privat Nøgle", - "privateKeyPlaceholder": "Indsæt privat nøgle...", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", "credentialLabel": "Credential", - "credentialPlaceholder": "Vælg gemte legitimationsoplysninger", - "connectToTerminal": "Opret forbindelse til Terminal", - "connectToFiles": "Opret forbindelse til filer" + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Ingen terminal valgt", - "noTerminalSelectedHint": "Åbn et SSH-terminalfaneblad for at se dets kommandohistorik", - "searchPlaceholder": "Søg historik...", - "clearAll": "Ryd Alle", - "noHistoryEntries": "Ingen historik poster", - "trackingDisabled": "Tracking af historik er deaktiveret", - "trackingDisabledHint": "Aktivér det i dine profilindstillinger for at optage kommandoer." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Optagelse Af Nøgler", - "recordToTerminals": "Optag til terminaler", - "selectAll": "Alle", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Ingen terminalfaneblade åbne", - "selectTerminalsAbove": "Vælg terminaler ovenfor", - "broadcastInputPlaceholder": "Skriv her for at sende tastetryk...", - "stopRecording": "Stop Optagelse", - "startRecording": "Start Optagelse", - "settingsTitle": "Indstillinger", - "enableRightClickCopyPaste": "Aktivér højreklik kopiér/indsæt" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { "layoutTitle": "Layout", - "selectLayoutAbove": "Vælg et layout ovenfor", - "selectLayoutHint": "Vælg hvor mange ruder der skal vises", - "panesTitle": "Paner", - "openTabsTitle": "Åbn Faneblade", - "dragTabsHint": "Træk faner ind i ruderne ovenfor, eller brug Hurtig tildeling", - "dropHere": "Slip her", - "emptyPane": "Tom", - "dashboard": "Instrumentbræt", - "clearSplitScreen": "Ryd Opdelt Skærm", - "quickAssign": "Hurtig tildeling", - "alreadyAssigned": "Ruden {{index}}", - "splitTab": "Opdel faneblad", - "addToSplit": "Tilføj til Opdeling", - "removeFromSplit": "Fjern fra Split", - "assignToPane": "Tildel til rude" + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Opret Snippet", - "createSnippetDescription": "Opret et nyt kommandouddrag til hurtig udførelse", - "nameLabel": "Navn", - "namePlaceholder": "f.eks. Genstart Nginx", - "descriptionLabel": "Varebeskrivelse", - "descriptionPlaceholder": "Valgfri beskrivelse", - "optional": "Valgfri", - "folderLabel": "Mappe", - "noFolder": "Ingen mappe (Ikke kategoriseret)", - "commandLabel": "Kommando", - "commandPlaceholder": "f.eks. sudo systemctl genstart nginx", - "cancel": "Annuller", - "createSnippetButton": "Opret Snippet", - "createFolderTitle": "Opret Mappe", - "createFolderDescription": "Organiser dine snippets i mapper", - "folderNameLabel": "Mappe Navn", - "folderNamePlaceholder": "f.eks. Systemkommandoer, Docker Scripts", - "folderColorLabel": "Mappe Farve", - "folderIconLabel": "Mappeikon", - "previewLabel": "Eksempelvisning", - "folderNameFallback": "Mappe Navn", - "createFolderButton": "Opret Mappe", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Alle", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Ingen terminalfaneblade åbne", - "searchPlaceholder": "Søg i snippets...", - "newSnippet": "Ny Snippet", - "newFolder": "Ny Mappe", - "run": "Kør", - "noSnippetsInFolder": "Ingen snippets i denne mappe", - "uncategorized": "Ikke Kategoriseret", - "editSnippetTitle": "Rediger Snippet", - "editSnippetDescription": "Opdater dette kommandouddrag", - "saveSnippetButton": "Gem Ændringer", - "createSuccess": "Snippet oprettet", - "createFailed": "Kunne ikke oprette snippet", - "updateSuccess": "Snippet opdateret", - "updateFailed": "Mislykkedes at opdatere snippet", - "deleteFailed": "Kunne ikke slette snippet", - "folderCreateSuccess": "Mappe oprettet", - "folderCreateFailed": "Kunne ikke oprette mappe", - "editFolderTitle": "Rediger Mappe", - "editFolderDescription": "Omdøb eller ændr udseendet af denne mappe", - "saveFolderButton": "Gem Ændringer", - "editFolder": "Rediger mappe", - "deleteFolder": "Slet mappe", - "folderDeleteSuccess": "Mappe \"{{name}}\" slettet", - "folderDeleteFailed": "Kunne ikke slette mappe", - "folderEditSuccess": "Mappen er opdateret", - "folderEditFailed": "Mislykkedes at opdatere mappe", - "confirmRunMessage": "Kør \"{{name}}\"?", - "confirmRunButton": "Løbe", - "runSuccess": "Ran \"{{name}}\" i {{count}} terminal(er)", - "copySuccess": "Kopieret \"{{name}}\" til udklipsholder", - "shareTitle": "Del Snippet", - "shareUser": "Bruger", - "shareRole": "Rolle", - "selectUser": "Vælg en bruger...", - "selectRole": "Vælg en rolle...", - "shareSuccess": "Snippet delt med succes", - "shareFailed": "Kunne ikke dele snippet", - "revokeSuccess": "Adgang tilbagekaldt", - "revokeFailed": "Mislykkedes at tilbagekalde adgang", - "currentAccess": "Nuværende Adgang", - "shareLoadError": "Kunne ikke indlæse delingsdata", - "loading": "Indlæser...", - "close": "Luk", - "reorderFailed": "Kunne ikke gemme rækkefølgen af uddrag" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Konto", - "sectionAppearance": "Udseende", - "sectionSecurity": "Sikkerhed", - "sectionApiKeys": "Api Nøgler", - "sectionC2sTunnels": "Tunneler C2S", - "usernameLabel": "Brugernavn", - "roleLabel": "Rolle", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", "roleAdministrator": "Administrator", - "authMethodLabel": "Auth Metode", - "authMethodLocal": "Lokal", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Til", - "twoFaOff": "Fra", + "twoFaOn": "On", + "twoFaOff": "Off", "versionLabel": "Version", - "deleteAccount": "Slet Konto", - "deleteAccountDescription": "Slet din konto permanent", - "deleteButton": "Slet", - "deleteAccountPermanent": "Denne handling er permanent og kan ikke fortrydes.", - "deleteAccountWarning": "Alle sessioner, værter, legitimationsoplysninger og indstillinger vil blive slettet permanent.", - "confirmPasswordDeletePlaceholder": "Indtast din adgangskode for at bekræfte", - "languageLabel": "Sprog", - "themeLabel": "Tema", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Accent Farve", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Kommando Autofuldfør", - "commandAutocompleteDesc": "Vis autofuldførelse under indtastning", - "historyTracking": "Historik Sporing", - "historyTrackingDesc": "Spor terminalkommandoer", - "syntaxHighlighting": "Syntaksfremhævning", - "syntaxHighlightingDesc": "Fremhæv terminal output", - "commandPalette": "Kommando Palet", - "commandPaletteDesc": "Aktivér tastaturgenvej", - "reopenTabsOnLogin": "Genåbn faner ved login", - "reopenTabsOnLoginDesc": "Gendan dine åbne faner, når du logger ind eller opdaterer siden, selv fra en anden enhed", - "confirmTabClose": "Bekræft Luk Af Faneblad", - "confirmTabCloseDesc": "Spørg før lukning af terminalfaner", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Vis Vært Tags", - "showHostTagsDesc": "Vis tags i værtslisten", - "hostTrayOnClick": "Klik for at udvide værtshandlinger", - "hostTrayOnClickDesc": "Vis altid forbindelsesknapper; klik for at udvide administrationsmuligheder i stedet for at holde musen over", - "pinAppRail": "Fastgør app-skinne", - "pinAppRailDesc": "Hold appskinnen i venstre sidebar altid udvidet i stedet for at udvide den, når du holder musen over den", - "settingsSnippets": "Stumper", - "foldersCollapsed": "Mapper Sammenbrudt", - "foldersCollapsedDesc": "Fold som standard mapper sammen", - "confirmExecution": "Bekræft Kørsel", - "confirmExecutionDesc": "Bekræft før kørende snippets", - "settingsUpdates": "Opdateringer", - "disableUpdateChecks": "Deaktivér Opdateringstjek", - "disableUpdateChecksDesc": "Stop søgning efter opdateringer", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", "totpAuthenticator": "TOTP Authenticator", - "totpEnabled": "2FA er aktiveret", - "totpDisabled": "Tilføj ekstra login sikkerhed", - "disable": "Deaktivér", - "enable": "Aktiver", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scan QR-koden eller angiv hemmeligheden i din autentificerings-app, og angiv derefter den 6-cifrede kode", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verificér", - "changePassword": "Skift Adgangskode", - "currentPasswordLabel": "Nuværende Adgangskode", - "currentPasswordPlaceholder": "Nuværende adgangskode", - "newPasswordLabel": "Ny Adgangskode", - "newPasswordPlaceholder": "Ny adgangskode", - "confirmPasswordLabel": "Bekræft Ny Adgangskode", - "confirmPasswordPlaceholder": "Bekræft ny adgangskode", - "updatePassword": "Opdater Adgangskode", - "createApiKeyTitle": "Opret API-nøgle", - "createApiKeyDescription": "Generer en ny API-nøgle til programmatisk adgang.", - "apiKeyNameLabel": "Navn", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "valgfri", - "cancel": "Annuller", - "createKey": "Opret Nøgle", - "apiKeyCount": "{{count}} nøgler", - "newKey": "Ny Nøgle", - "noApiKeys": "Ingen API-nøgler endnu.", - "apiKeyActive": "Aktiv", - "apiKeyUsageHint": "Inkluder din nøgle i", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", "apiKeyUsageHintHeader": "header.", - "apiKeyPermissionsHint": "Nøgler arver tilladelserne til den oprettede bruger.", - "roleUser": "Bruger", - "authMethodDual": "Dobbelt Auth", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Kunne ikke starte opsætning af TOTP", - "totpEnter6Digits": "Indtast en 6-cifret kode", - "totpEnabledSuccess": "To-faktor autentificering aktiveret", - "totpInvalidCode": "Ugyldig kode, prøv igen", - "totpDisableInputRequired": "Indtast din TOTP kode eller adgangskode", - "totpDisabledSuccess": "To-faktor autentificering deaktiveret", - "totpDisableFailed": "Mislykkedes at deaktivere 2FA", - "totpDisableTitle": "Deaktivér 2FA", - "totpDisablePlaceholder": "Indtast TOTP kode eller adgangskode", - "totpDisableConfirm": "Deaktivér 2FA", - "totpContinueVerify": "Fortsæt med at bekræfte", - "totpVerifyTitle": "Verificér Kode", - "totpBackupTitle": "Backup Koder", - "totpDownloadBackup": "Download Sikkerhedskoder", - "done": "Udført", - "secretCopied": "Hemmelig kopieret til udklipsholder", - "apiKeyNameRequired": "Nøgle navn er påkrævet", - "apiKeyCreated": "API-nøgle \"{{name}}\" oprettet", - "apiKeyCreateFailed": "Kunne ikke oprette API-nøgle", - "apiKeyUser": "Bruger", - "apiKeyExpires": "Udløber", - "apiKeyRevoked": "API-nøgle \"{{name}}\" tilbagekaldt", - "apiKeyRevokeFailed": "Mislykkedes at tilbagekalde API-nøgle", - "passwordFieldsRequired": "Nuværende og nye adgangskoder er påkrævet", - "passwordMismatch": "Adgangskoder stemmer ikke overens", - "passwordTooShort": "Adgangskoden skal være på mindst 6 tegn", - "passwordUpdated": "Adgangskode opdateret", - "passwordUpdateFailed": "Kunne ikke opdatere adgangskode", - "deletePasswordRequired": "Adgangskode er påkrævet for at slette din konto", - "deleteFailed": "Kunne ikke slette konto", - "deleting": "Sletter...", - "colorPickerTooltip": "Åbn farvevælger", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", "themeSystem": "System", - "themeLight": "Lys", - "themeDark": "Mørk", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solariseret", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "En Mørk", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/de_DE.json b/src/ui/locales/translated/de_DE.json index 8245e406..9bc5af32 100644 --- a/src/ui/locales/translated/de_DE.json +++ b/src/ui/locales/translated/de_DE.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Ordner", - "folder": "Ordner", - "password": "Passwort", - "key": "Schlüssel", - "sshPrivateKey": "SSH Privatschlüssel", - "upload": "Hochladen", - "keyPassword": "Schlüsselpasswort", - "sshKey": "SSH-Schlüssel", - "uploadPrivateKeyFile": "Private Schlüsseldatei hochladen", - "searchCredentials": "Anmeldeinformationen suchen...", - "addCredential": "Zugangsdaten hinzufügen", - "caCertificate": "CA-Zertifikat (-cert.pub)", - "caCertificateDescription": "Optional: Laden Sie die CA-signierte Zertifikatsdatei hoch oder fügen Sie sie ein (z.B. id_ed25519-cert.pub). Wird benötigt, wenn Ihr SSH-Server Zertifikatsbasierte Autorisierung verwendet.", - "uploadCertFile": "Datei -cert.pub hochladen", - "clearCert": "Leeren", - "certLoaded": "Zertifikat geladen", - "certPublicKeyLabel": "CA-Zertifikat", - "certTypeLabel": "Zertifikatstyp", - "pasteOrUploadCert": "Einfügen oder Hochladen eines -cert.pub Zertifikats...", - "hasCaCert": "Hat CA-Zertifikat", - "noCaCert": "Kein CA-Zertifikat" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Fehler beim Laden der Warnungen", - "failedToDismissAlert": "Fehler beim Ausblenden der Warnung" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Serverkonfiguration", - "description": "Konfigurieren Sie die Termix-Server-URL für eine Verbindung zu Ihren Backend-Diensten", - "serverUrl": "Server-URL", - "enterServerUrl": "Bitte geben Sie eine Server-URL ein", - "saveFailed": "Fehler beim Speichern der Konfiguration", - "saveError": "Fehler beim Speichern der Konfiguration", - "saving": "Speichern...", - "saveConfig": "Konfiguration speichern", - "helpText": "Geben Sie die URL ein, auf der Ihr Termix Server läuft (z.B. http://localhost:30001 oder https://your-server.com)", - "changeServer": "Server ändern", - "mustIncludeProtocol": "Server-URL muss mit http:// oder https:// beginnen", - "allowInvalidCertificate": "Ungültiges Zertifikat zulassen", - "allowInvalidCertificateDesc": "Nur für vertrauenswürdige, selbstgehostete Server mit selbstsignierten oder IP-Adresszertifikaten verwenden.", - "useEmbedded": "Lokalen Server verwenden", - "embeddedDesc": "Starte Termix mit dem eingebauten lokalen Server (kein entfernter Server benötigt)", - "embeddedConnecting": "Verbindung zum lokalen Server...", - "embeddedNotReady": "Lokaler Server ist noch nicht bereit. Bitte warten Sie einen Moment und versuchen Sie es erneut.", - "localServer": "Lokaler Server" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Versionsüberprüfungsfehler", - "checkFailed": "Fehler bei der Suche nach Updates", - "upToDate": "App ist aktuell", - "currentVersion": "Sie verwenden Version {{version}}", - "updateAvailable": "Update verfügbar", - "newVersionAvailable": "Eine neue Version ist verfügbar! Sie verwenden {{current}}, aber {{latest}} ist verfügbar.", - "betaVersion": "Beta-Version", - "betaVersionDesc": "Sie verwenden {{current}}, was neuer ist als die neueste stabile Version {{latest}}.", - "releasedOn": "Veröffentlicht am {{date}}", - "downloadUpdate": "Update herunterladen", - "checking": "Suche nach Updates...", - "checkUpdates": "Nach Updates suchen", - "checkingUpdates": "Suche nach Updates...", - "updateRequired": "Update erforderlich" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Schließen", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Weiter", - "maintenance": "Wartung", - "degraded": "Degradiert", - "error": "Fehler", - "warning": "Warnung", - "unsavedChanges": "Ungespeicherte Änderungen", - "dismiss": "Verwerfen", - "loading": "Wird geladen...", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", "optional": "Optional", - "connect": "Verbinden", - "copied": "Kopiert", - "connecting": "Verbinden...", - "updateAvailable": "Update verfügbar", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "In neuem Tab öffnen", - "noReleases": "Keine Releases", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", "updatesAndReleases": "Updates & Releases", - "newVersionAvailable": "Eine neue Version ({{version}}) ist verfügbar.", - "failedToFetchUpdateInfo": "Update-Informationen konnten nicht abgerufen werden", - "preRelease": "Pre-Release", - "noReleasesFound": "Keine Releases gefunden.", - "cancel": "Abbrechen", - "username": "Benutzername", - "login": "Anmelden", - "register": "Registrieren", - "password": "Passwort", - "confirmPassword": "Passwort bestätigen", - "back": "Zurück", - "save": "Speichern", - "saving": "Speichern...", - "delete": "Löschen", - "rename": "Umbenennen", - "edit": "Bearbeiten", - "add": "Neu", - "confirm": "Bestätigen", - "no": "Nein", - "or": "ODER", - "next": "Nächste", - "previous": "Vorherige", - "refresh": "Aktualisieren", - "language": "Sprache", - "checking": "Überprüfen...", - "checkingDatabase": "Überprüfe Datenbankverbindung...", - "checkingAuthentication": "Authentifizierung wird überprüft...", - "backendReconnected": "Serververbindung wiederhergestellt", - "connectionDegraded": "Serververbindung verloren, Wiederherstellen…", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Entfernen", - "create": "Anlegen", - "update": "Aktualisieren", - "copy": "Kopieren", - "copyFailed": "Fehler beim Kopieren in die Zwischenablage", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Wiederherstellen", - "of": "von" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Zuhause", + "home": "Home", "terminal": "Terminal", "docker": "Docker", - "tunnels": "Tunnel", - "fileManager": "Datei-Manager", - "serverStats": "Serverstatistik", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", - "userProfile": "Benutzerprofil", - "splitScreen": "Bildschirm teilen", - "confirmClose": "Diese aktive Sitzung schließen?", - "close": "Schließen", - "cancel": "Abbrechen", - "sshManager": "SSH-Manager", - "cannotSplitTab": "Kann diesen Tab nicht teilen", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Passwort kopieren", - "copySudoPassword": "Sudo-Passwort kopieren", - "passwordCopied": "Passwort in Zwischenablage kopiert", - "noPasswordAvailable": "Kein Passwort verfügbar", - "failedToCopyPassword": "Fehler beim Kopieren des Passworts", - "refreshTab": "Verbindung aktualisieren", - "openFileManager": "Dateimanager öffnen", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", "dashboard": "Dashboard", - "networkGraph": "Netzwerkgrafik", - "quickConnect": "Schnellverbindung", - "sshTools": "SSH-Tools", - "history": "Verlauf", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", "hosts": "Hosts", - "snippets": "Schnipsel", - "hostManager": "Host-Manager", - "credentials": "Anmeldedaten", - "connections": "Verbindungen", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", "roleAdministrator": "Administrator", - "roleUser": "Benutzer" + "roleUser": "User" }, "hosts": { "hosts": "Hosts", - "noHosts": "Keine SSH-Hosts", - "retry": "Wiederholen", - "refresh": "Aktualisieren", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", "optional": "Optional", - "downloadSample": "Beispiel herunterladen", - "failedToDeleteHost": "Fehler beim Löschen von {{name}}", - "importSkipExisting": "Import (Bestehende überspringen)", - "connectionDetails": "Verbindungsdetails", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Remote-Desktop", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Benutzername", - "folder": "Ordner", + "username": "Username", + "folder": "Folder", "tags": "Tags", "pin": "Pin", - "addHost": "Host hinzufügen", - "editHost": "Host bearbeiten", - "cloneHost": "Host klonen", - "enableTerminal": "Terminal aktivieren", - "enableTunnel": "Tunnel aktivieren", - "enableFileManager": "Dateimanager aktivieren", - "enableDocker": "Docker aktivieren", - "defaultPath": "Standardpfad", - "connection": "Verbindung", - "upload": "Hochladen", - "authentication": "Authentifizierung", - "password": "Passwort", - "key": "Schlüssel", - "credential": "Anmeldedaten", - "none": "Keine", - "sshPrivateKey": "SSH Privatschlüssel", - "keyType": "Schlüsseltyp", - "uploadFile": "Datei hochladen", - "tabGeneral": "Allgemein", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunnel", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Dateien", - "tabStats": "Statistik", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Teilen", - "tabAuthentication": "Authentifizierung", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Datei-Manager", - "serverStats": "Serverstatistik", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Ordner \"{{oldName}}\" wurde erfolgreich in \"{{newName}}\" umbenannt", - "failedToRenameFolder": "Ordner konnte nicht umbenannt werden", - "movedToFolder": "Verschoben nach \"{{folder}}\"", - "editHostTooltip": "Host bearbeiten", - "statusChecks": "Statusprüfungen", - "metricsCollection": "Metrik-Sammlung", - "metricsInterval": "Metrik-Sammlungsintervall", - "metricsIntervalDesc": "Wie oft Serverstatistiken gesammelt werden (5s - 1h)", - "behavior": "Verhalten", - "themePreview": "Theme-Vorschau", - "theme": "Thema", - "fontFamily": "Schriftfamilie", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Buchstabenabstand", - "lineHeight": "Zeilenhöhe", - "cursorStyle": "Cursor-Stil", - "cursorBlink": "Cursor-Blinke", - "scrollbackBuffer": "Scrollbackpuffer", - "bellStyle": "Glockenstil", - "rightClickSelectsWord": "Rechtsklick markiert Wort", - "fastScrollModifier": "Schneller Scroll-Modifikator", - "fastScrollSensitivity": "Schnelle Scroll Empfindlichkeit", - "sshAgentForwarding": "SSH-Agent-Weiterleitung", - "backspaceMode": "Backspace-Modus", - "startupSnippet": "Startschlitz", - "selectSnippet": "Snippet auswählen", - "forceKeyboardInteractive": "Tastatur-Interaktiv erzwingen", - "overrideCredentialUsername": "Benutzername überschreiben", - "overrideCredentialUsernameDesc": "Verwenden Sie den oben angegebenen Benutzernamen anstelle des Benutzernamens", - "jumpHostChain": "Host-Kette springen", - "portKnocking": "Port Klopfen", - "addKnock": "Port hinzufügen", - "addProxyNode": "Knoten hinzufügen", - "proxyNode": "Proxyknoten", - "proxyType": "Proxy-Typ", - "quickActions": "Schnelle Aktionen", - "sudoPasswordAutoFill": "Sudo Passwort automatisch eingeben", - "sudoPassword": "Sudo Passwort", - "keepaliveInterval": "Keepalive-Intervall (ms)", - "moshCommand": "MOSH Befehl", - "environmentVariables": "Umgebungsvariablen", - "addVariable": "Variable hinzufügen", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "URL des Terminals kopieren", - "copyFileManagerUrl": "Datei Manager URL kopieren", - "copyRemoteDesktopUrl": "Remote-Desktop-URL kopieren", - "failedToConnect": "Verbindung zur Konsole fehlgeschlagen", - "connect": "Verbinden", - "disconnect": "Verbindung trennen", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", "start": "Start", - "enableStatusCheck": "Statusprüfung aktivieren", - "enableMetrics": "Metriken aktivieren", - "bulkUpdateFailed": "Massenaktualisierung fehlgeschlagen", - "selectAll": "Alle auswählen", - "deselectAll": "Alle abwählen", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Sichere Shell", - "virtualNetwork": "Virtuelles Netzwerk", - "unencryptedShell": "Unverschlüsselte Shell", - "addressIp": "Adresse / IP", - "friendlyName": "Freundlicher Name", - "folderAndAdvanced": "Ordner & Erweitert", - "privateNotes": "Private Notizen", - "privateNotesPlaceholder": "Details zu diesem Server...", - "pinToTop": "Nach oben anheften", - "pinToTopDesc": "Diesen Host immer am Anfang der Liste anzeigen", - "portKnockingSequence": "Port Knocking Sequenz", - "addKnockBtn": "Knock hinzufügen", - "noPortKnocking": "Kein Port knocking konfiguriert.", - "knockPort": "Knock-Port", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Verzögerung nach (ms)", - "useSocks5Proxy": "SOCKS5-Proxy verwenden", - "useSocks5ProxyDesc": "Verbindung über einen Proxy-Server leiten", - "proxyHost": "Proxy-Host", - "proxyPort": "Proxy-Port", - "proxyUsername": "Proxy Benutzername", - "proxyPassword": "Proxy-Passwort", - "proxySingleMode": "Einzelner Proxy", - "proxyChainMode": "Proxy-Kette", - "you": "Du", - "jumpHostChainLabel": "Host-Kette springen", - "addJumpBtn": "Neuer Sprung", - "noJumpHosts": "Keine Spring-Hosts konfiguriert.", - "selectAServer": "Wählen Sie einen Server...", - "sshPort": "SSH-Port", - "authMethod": "Auth Methode", - "storedCredential": "Anmeldeinformationen gespeichert", - "selectACredential": "Anmeldedaten auswählen...", - "keyTypeLabel": "Schlüsseltyp", - "keyTypeAuto": "Auto-Erkennung", - "keyPasteTab": "Einfügen", - "keyUploadTab": "Hochladen", - "keyFileLoaded": "Schlüsseldatei geladen", - "keyUploadClick": "Klicken, um .pem / .key / .ppk hochzuladen", - "clearKey": "Lösche Taste", - "keySaved": "SSH-Schlüssel gespeichert", - "keyReplaceNotice": "füge einen neuen Schlüssel ein, um ihn zu ersetzen", - "keyPassphraseSaved": "Passwort gespeichert, zum Ändern eingeben", - "replaceKey": "Schlüssel ersetzen", - "forceKeyboardInteractiveLabel": "Tastatur-Interaktiv erzwingen", - "forceKeyboardInteractiveShortDesc": "Manuelle Passworteingabe erzwingen, auch wenn Schlüssel vorhanden sind", - "terminalAppearance": "Terminalansicht", - "colorTheme": "Farbschema", - "fontFamilyLabel": "Schriftfamilie", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Cursor-Stil", - "letterSpacingPx": "Buchstabenabstand (px)", - "lineHeightLabel": "Zeilenhöhe", - "bellStyleLabel": "Glockenstil", - "backspaceModeLabel": "Backspace-Modus", - "cursorBlinking": "Cursor-Blinken", - "cursorBlinkingDesc": "Blinkende Animation für den Cursor des Terminals aktivieren", - "rightClickSelectsWordLabel": "Rechtsklick Wort auswählen", - "rightClickSelectsWordShortDesc": "Wählen Sie das Wort unter dem Cursor mit der rechten Maustaste", - "behaviorAndAdvanced": "Verhalten & Fortgeschrittene", - "scrollbackBufferLabel": "Scrollbackpuffer", - "scrollbackMaxLines": "Maximale Anzahl von Zeilen im Verlauf", - "sshAgentForwardingLabel": "SSH-Agent-Weiterleitung", - "sshAgentForwardingShortDesc": "Ihre lokalen SSH-Schlüssel an diesen Host übergeben", - "enableAutoMosh": "Auto-Mosh aktivieren", - "enableAutoMoshDesc": "Mosh als SSH bevorzugen, falls verfügbar", - "enableAutoTmux": "Auto-Tmux aktivieren", - "enableAutoTmuxDesc": "Automatisches Starten oder Anhängen an tmux Sitzung", - "sudoPasswordAutoFillLabel": "Sudo Passwort automatisch ausfüllen", - "sudoPasswordAutoFillShortDesc": "Sudo-Passwort bei Aufforderung automatisch angeben", - "sudoPasswordLabel": "Sudo Passwort", - "environmentVariablesLabel": "Umgebungsvariablen", - "addVariableBtn": "Variable hinzufügen", - "noEnvVars": "Keine Umgebungsvariablen konfiguriert.", - "fastScrollModifierLabel": "Schneller Scroll-Modifikator", - "fastScrollSensitivityLabel": "Schnelle Scroll Empfindlichkeit", - "moshCommandLabel": "Mosh-Befehl", - "startupSnippetLabel": "Startschlitz", - "keepaliveIntervalLabel": "Keepalive-Intervall (Sekunden)", - "maxKeepaliveMisses": "Max. Keepalive Vermisste", - "tunnelSettings": "Tunnel Einstellungen", - "enableTunneling": "Tunneling aktivieren", - "enableTunnelingDesc": "SSH-Tunnelfunktionalität für diesen Host aktivieren", - "serverTunnelsSection": "Servertunnel", - "addTunnelBtn": "Tunnel hinzufügen", - "noTunnelsConfigured": "Keine Tunnel konfiguriert.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Tunneltyp", - "tunnelModeLocalDesc": "Weiterleiten Sie einen lokalen Port an einen Port auf dem entfernten Server (oder einen Host, der von ihm erreichbar ist).", - "tunnelModeRemoteDesc": "Führen Sie einen Port auf dem entfernten Server zurück an einen lokalen Port auf Ihrem Rechner.", - "tunnelModeDynamicDesc": "Erstellen Sie einen SOCKS5 Proxy auf einem lokalen Port für dynamische Port-Weiterleitung.", - "sameHost": "Dieser Host (direkter Tunnel)", - "endpointHost": "Endpunkt-Host", - "endpointPort": "Endpunkt-Port", - "bindHost": "Verbinde Host", - "sourcePort": "Quellport", - "maxRetries": "Max. Wiederholungen", - "retryIntervalS": "Wiederholungsintervall (s)", - "autoStartLabel": "Auto-Start", - "autoStartDesc": "Diesen Tunnel automatisch verbinden, wenn der Host geladen wird", - "tunnelConnecting": "Verbindung zum Tunnel...", - "tunnelDisconnected": "Tunnel getrennt", - "failedToConnectTunnel": "Verbindung fehlgeschlagen", - "failedToDisconnectTunnel": "Fehler beim Trennen", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", "dockerIntegration": "Docker Integration", - "enableDockerMonitor": "Docker aktivieren", - "enableDockerMonitorDesc": "Überwachen und verwalten von Containern auf diesem Host über Docker", - "enableFileManagerMonitor": "Dateimanager aktivieren", - "enableFileManagerMonitorDesc": "Durchsuchen und verwalten von Dateien auf diesem Host über SFTP", - "defaultPathLabel": "Standardpfad", - "fileManagerPathHint": "Das zu öffnende Verzeichnis, wenn der Dateimanager für diesen Host gestartet wird.", - "statusChecksLabel": "Statusprüfungen", - "enableStatusChecks": "Statusüberprüfungen aktivieren", - "enableStatusChecksDesc": "Ping diesen Host regelmäßig, um die Verfügbarkeit zu überprüfen", - "useGlobalInterval": "Globales Intervall verwenden", - "useGlobalIntervalDesc": "Mit dem serverweiten Statusüberprüfungsintervall überschreiben", - "checkIntervalS": "Überprüfungsintervall (e)", - "checkIntervalDesc": "Sekunden zwischen jedem Konnektivitätsping", - "metricsCollectionLabel": "Metrik-Sammlung", - "enableMetricsLabel": "Metriken aktivieren", - "enableMetricsDesc": "Erfassen Sie CPU, RAM, Festplatte und Netzwerknutzung von diesem Host", - "useGlobalMetrics": "Globales Intervall verwenden", - "useGlobalMetricsDesc": "Mit dem serverweiten Metrik-Intervall überschreiben", - "metricsIntervalS": "Metrik-Intervall (e)", - "metricsIntervalDesc2": "Sekunden zwischen metrischen Snapshots", - "visibleWidgets": "Sichtbare Widgets", - "cpuUsageLabel": "CPU Auslastung", - "cpuUsageDesc": "CPU-Prozent, Durchschnittswert, Sparkline-Graph", - "memoryLabel": "Speichernutzung", - "memoryDesc": "RAM Auslastung, Swap, zwischengespeichert", - "storageLabel": "Plattennutzung", - "storageDesc": "Festplattenverbrauch pro Mount-Punkt", - "networkLabel": "Netzwerkschnittstellen", - "networkDesc": "Schnittstellenliste und Bandbreite", - "uptimeLabel": "Laufzeit", - "uptimeDesc": "System-Uptime und Boot-Zeit", - "systemInfoLabel": "System-Info", - "systemInfoDesc": "Betriebssystem, Kernel, Hostname, Architektur", - "recentLoginsLabel": "Neueste Anmeldungen", - "recentLoginsDesc": "Erfolgreiche und fehlgeschlagene Anmeldetermine", - "topProcessesLabel": "Top-Prozesse", - "topProcessesDesc": "PID, CPU%, MEM%, Befehl", - "listeningPortsLabel": "Höre Ports", - "listeningPortsDesc": "Ports mit Prozess- und Status öffnen", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, SELinux-Status", - "quickActionsLabel": "Schnelle Aktionen", - "quickActionsToolbar": "Schnelle Aktionen erscheinen als Buttons in der Server-Statistik-Symbolleiste für die Ausführung von Ein-Klick-Befehlen.", - "noQuickActions": "Noch keine schnellen Aktionen.", - "buttonLabel": "Schaltflächenbezeichnung", - "selectSnippetPlaceholder": "Snippet auswählen...", - "addActionBtn": "Aktion hinzufügen", - "hostSharedSuccessfully": "Host erfolgreich geteilt", - "failedToShareHost": "Fehler beim Teilen des Hosts", - "accessRevoked": "Zugriff widerrufen", - "failedToRevokeAccess": "Fehler beim Entfernen des Zugriffs", - "cancelBtn": "Abbrechen", - "savingBtn": "Speichern...", - "addHostBtn": "Host hinzufügen", - "hostUpdated": "Host aktualisiert", - "hostCreated": "Host erstellt", - "failedToSave": "Fehler beim Speichern des Hosts", - "credentialUpdated": "Anmeldeinformationen aktualisiert", - "credentialCreated": "Anmeldeinformationen erstellt", - "failedToSaveCredential": "Anmeldeinformationen konnten nicht gespeichert werden", - "backToHosts": "Zurück zu Hosts", - "backToCredentials": "Zurück zu Anmeldeinformationen", - "pinned": "Angeheftet", - "noHostsFound": "Keine Hosts gefunden", - "tryDifferentTerm": "Einen anderen Begriff versuchen", - "addFirstHost": "Füge deinen ersten Host hinzu, um loszulegen", - "noCredentialsFound": "Keine Anmeldedaten gefunden", - "addCredentialBtn": "Zugangsdaten hinzufügen", - "updateCredentialBtn": "Anmeldeinformationen aktualisieren", - "features": "Eigenschaften", - "noFolder": "(Kein Ordner)", - "deleteSelected": "Löschen", - "exitSelection": "Auswahl beenden", - "importSkip": "Import (Bestehende überspringen)", - "importOverwrite": "Import (Überschreiben)", - "collapseBtn": "Einklappen", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", "importExportBtn": "Import / Export", - "hostStatusesRefreshed": "Host-Status aktualisiert", - "failedToRefreshHosts": "Fehler beim Aktualisieren der Hosts", - "movedHostTo": "{{host}} nach \"{{folder}} \" verschoben", - "failedToMoveHost": "Fehler beim Verschieben des Hosts", - "folderRenamedTo": "Ordner umbenannt in \"{{name}}\"", - "deletedFolder": "Löschter Ordner \"{{name}}\"", - "failedToDeleteFolder": "Fehler beim Löschen des Ordners", - "deleteAllInFolder": "Alle Hosts in \"{{name}}\"löschen? Dies kann nicht rückgängig gemacht werden.", - "deletedHost": "Gelöschte {{name}}", - "copiedToClipboard": "In Zwischenablage kopiert", - "terminalUrlCopied": "Terminal URL kopiert", - "fileManagerUrlCopied": "URL des Datei-Managers kopiert", - "tunnelUrlCopied": "Tunnel-URL kopiert", - "dockerUrlCopied": "Docker URL kopiert", - "serverStatsUrlCopied": "Server-Statistik-URL kopiert", - "rdpUrlCopied": "RDP-URL kopiert", - "vncUrlCopied": "VNC-URL kopiert", - "telnetUrlCopied": "Telnet-URL kopiert", - "remoteDesktopUrlCopied": "Remote-Desktop-URL kopiert", - "expandActions": "Aktionen erweitern", - "collapseActions": "Aktionen zusammenbrechen", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Magisches Paket an {{name}} gesendet", - "wakeOnLanError": "Das Senden des Magic Packets ist fehlgeschlagen.", - "cloneHostAction": "Host klonen", - "copyAddress": "Adresse kopieren", - "copyLink": "Link kopieren", - "copyTerminalUrlAction": "URL des Terminals kopieren", - "copyFileManagerUrlAction": "Datei Manager URL kopieren", - "copyTunnelUrlAction": "Tunnel-URL kopieren", - "copyDockerUrlAction": "Docker URL kopieren", - "copyServerStatsUrlAction": "Server-Statistik-URL kopieren", - "copyRdpUrlAction": "RDP-URL kopieren", - "copyVncUrlAction": "VNC-URL kopieren", - "copyTelnetUrlAction": "Telnet-URL kopieren", - "copyRemoteDesktopUrlAction": "Remote-Desktop-URL kopieren", - "deleteCredentialConfirm": "Anmeldedaten \"{{name}} \"löschen?", - "deletedCredential": "Gelöschte {{name}}", - "deploySSHKeyTitle": "SSH-Schlüssel verteilen", - "deployingBtn": "Verteilen...", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", "deployBtn": "Deploy", - "failedToDeployKey": "Fehler beim Bereitstellen des Schlüssels", - "deleteHostsConfirm": "Löschen {{count}} Host{{plural}}? Dies kann nicht rückgängig gemacht werden.", - "movedToRoot": "Zum Root verschoben", - "failedToMoveHosts": "Fehler beim Verschieben der Hosts", - "enableTerminalFeature": "Terminal aktivieren", - "disableTerminalFeature": "Terminal deaktivieren", - "enableFilesFeature": "Dateien aktivieren", - "disableFilesFeature": "Dateien deaktivieren", - "enableTunnelsFeature": "Tunnel aktivieren", - "disableTunnelsFeature": "Tunnel deaktivieren", - "enableDockerFeature": "Docker aktivieren", - "disableDockerFeature": "Docker deaktivieren", - "addTagsPlaceholder": "Tags hinzufügen...", - "authDetails": "Authentifizierungsdetails", - "credType": "Typ", - "generateKeyPairDesc": "Generieren Sie ein neues Schlüsselpaar, sowohl private als auch öffentliche Schlüssel werden automatisch ausgefüllt.", - "generatingKey": "Erstellen...", - "generateLabel": "{{label}} generieren", - "uploadFileBtn": "Datei hochladen", - "keyPassphraseOptional": "Schlüsselpasswort (optional)", - "sshPublicKeyOptional": "SSH Öffentlicher Schlüssel (optional)", - "publicKeyGenerated": "Öffentlicher Schlüssel generiert", - "failedToGeneratePublicKey": "Öffentlichen Schlüssel konnte nicht abgerufen werden", - "publicKeyCopied": "Öffentlicher Schlüssel kopiert", - "keyPairGenerated": "{{label}} Schlüsselpaar generiert", - "failedToGenerateKeyPair": "Fehler beim Generieren des Schlüsselpaars", - "searchHostsPlaceholder": "Hosts, Adressen, Tags… suchen", - "searchCredentialsPlaceholder": "Anmeldeinformationen suchen…", - "refreshBtn": "Aktualisieren", - "addTag": "Tags hinzufügen...", - "deleteConfirmBtn": "Löschen", - "tunnelRequirementsText": "Der SSH-Server muss GatewayPorts yes, AllowTcpForwarding yes, und PermitRootLogin yes in /etc/ssh/sshd_config gesetzt haben.", - "deleteHostConfirm": "\"{{name}} \"löschen?", - "enableAtLeastOneProtocol": "Aktivieren Sie mindestens ein Protokoll oben, um Authentifizierung und Verbindungseinstellungen zu konfigurieren.", - "keyPassphrase": "Schlüsselpasswort", - "connectBtn": "Verbinden", - "disconnectBtn": "Verbindung trennen", - "basicInformation": "Grundlegende Informationen", - "authDetailsSection": "Authentifizierungsdetails", - "credTypeLabel": "Typ", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", "hostsTab": "Hosts", - "credentialsTab": "Anmeldedaten", - "selectMultiple": "Wählen Sie mehrere", - "selectHosts": "Hosts auswählen", - "connectionLabel": "Verbindung", - "authenticationLabel": "Authentifizierung", - "generateKeyPairTitle": "Schlüsselpaar generieren", - "generateKeyPairDescription": "Generieren Sie ein neues Schlüsselpaar, sowohl private als auch öffentliche Schlüssel werden automatisch ausgefüllt.", - "generateFromPrivateKey": "Aus Privatschlüssel generieren", - "refreshBtn2": "Aktualisieren", - "exitSelectionTitle": "Auswahl beenden", - "exportAll": "Alle exportieren", - "addHostBtn2": "Host hinzufügen", - "addCredentialBtn2": "Zugangsdaten hinzufügen", - "checkingHostStatuses": "Host-Status wird überprüft...", - "pinnedSection": "Angeheftet", - "hostsExported": "Hosts erfolgreich exportiert", - "exportFailed": "Fehler beim Exportieren der Hosts", - "sampleDownloaded": "Beispieldatei heruntergeladen", - "failedToDeleteCredential2": "Anmeldedaten konnten nicht gelöscht werden", - "noFolderOption": "(Kein Ordner)", - "nSelected": "{{count}} ausgewählt", - "featuresMenu": "Eigenschaften", - "moveMenu": "Bewegen", - "cancelSelection": "Abbrechen", - "deployDialogDesc": "Deploy {{name}} auf die autorisierten Schlüssel eines Hosts.", - "targetHostLabel": "Ziel-Host", - "selectHostOption": "Wähle einen Host...", - "keyDeployedSuccess": "Schlüssel erfolgreich verteilt", - "failedToDeployKey2": "Fehler beim Bereitstellen des Schlüssels", - "deletedCount": "{{count}} Hosts gelöscht", - "failedToDeleteCount": "Fehler beim Löschen der {{count}} Hosts", - "duplicatedHost": "Duplizierte \"{{name}}\"", - "failedToDuplicateHost": "Fehler beim Duplizieren des Hosts", - "updatedCount": "Aktualisierte {{count}} Hosts", - "friendlyNameLabel": "Freundlicher Name", - "descriptionLabel": "Beschreibung", - "loadingHost": "Host wird geladen...", - "loadingHosts": "Hosts werden geladen...", - "loadingCredentials": "Anmeldedaten werden geladen...", - "noHostsYet": "Noch keine Hosts", - "noHostsMatchSearch": "Keine Hosts entsprechen Ihrer Suche", - "hostNotFound": "Host nicht gefunden", - "searchHosts": "Hosts suchen...", - "sortHosts": "Hosts sortieren", - "sortDefault": "Standardreihenfolge", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", "sortNameAsc": "Name (A → Z)", "sortNameDesc": "Name (Z → A)", - "sortIpAsc": "IP-Adresse (aufsteigend)", - "sortIpDesc": "IP-Adresse (Beschreibung)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Als erstes angepinnt", - "filterHosts": "Hosts filtern", - "filterClearAll": "Filter löschen", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Angepinnt", - "filterAuthGroup": "Authentifizierungstyp", - "filterAuthPassword": "Passwort", - "filterAuthKey": "SSH-Schlüssel", - "filterAuthCredential": "Berechtigung", - "filterAuthNone": "Keiner", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokoll", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Merkmale", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Dateimanager", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Tags", - "shareHost": "Host teilen", - "shareHostTitle": "Weitergeben: {{name}}", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Dieser Host muss eine Anmeldeinformationen verwenden, um das Teilen zu ermöglichen. Bearbeiten Sie den Host und weisen Sie zuerst eine Anmeldeinformationen zu." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Verbindung", - "authentication": "Authentifizierung", - "connectionSettings": "Verbindungseinstellungen", - "displaySettings": "Anzeigeeinstellungen", - "audioSettings": "Audio-Einstellungen", - "rdpPerformance": "RDP Leistung", - "deviceRedirection": "Umleitung des Geräts", - "session": "Sitzung", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Zwischenablage", - "sessionRecording": "Sitzungsaufzeichnung", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC Einstellungen", - "terminalSettings": "Terminaleinstellungen", - "rdpPort": "RDP-Port", - "username": "Benutzername", - "password": "Passwort", - "domain": "Domäne", - "securityMode": "Sicherheitsmodus", - "colorDepth": "Farbtiefe", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Höhe", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Methode ändern", - "clientName": "Kundenname", - "initialProgram": "Initiales Programm", - "serverLayout": "Server-Layout", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", "gatewayPort": "Gateway Port", - "gatewayUsername": "Gateway-Benutzername", - "gatewayPassword": "Gateway-Passwort", - "gatewayDomain": "Gateway-Domain", - "remoteAppProgram": "RemoteApp-Programm", - "workingDirectory": "Arbeitsverzeichnis", - "arguments": "Argumente", - "normalizeLineEndings": "LinienEndings normalisieren", - "recordingPath": "Aufnahmepfad", - "recordingName": "Aufzeichnungsname", - "macAddress": "MAC-Adresse", - "broadcastAddress": "Broadcast-Adresse", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Wartezeit (n)", - "driveName": "Laufwerkname", - "drivePath": "Fahrpfad", - "ignoreCertificate": "Zertifikat ignorieren", - "ignoreCertificateDesc": "Erlaube Verbindungen zu Hosts mit selbstsignierten Zertifikaten", - "forceLossless": "Verlustfrei erzwingen", - "forceLosslessDesc": "Erzwinge verlustfreie Bildkodierung (höhere Qualität, mehr Bandbreite)", - "disableAudio": "Audio deaktivieren", - "disableAudioDesc": "Alle Audiodateien der Remote-Sitzung stummschalten", - "enableAudioInput": "Audio-Eingang aktivieren (Mikrofon)", - "enableAudioInputDesc": "Lokales Mikrofon an die entfernte Sitzung weiterleiten", - "wallpaper": "Hintergrundbild", - "wallpaperDesc": "Desktop-Hintergrund anzeigen (Deaktivierung verbessert die Leistung)", - "theming": "Design", - "themingDesc": "Visuelle Designs und Stile aktivieren", - "fontSmoothing": "Schriftglättung", - "fontSmoothingDesc": "ClearType-Schriftarten-Rendering aktivieren", - "fullWindowDrag": "Volles Fenster Drag", - "fullWindowDragDesc": "Fensterinhalt beim Ziehen anzeigen", - "desktopComposition": "Desktop-Komposition", - "desktopCompositionDesc": "Aero-Glaseffekte aktivieren", - "menuAnimations": "Menüanimationen", - "menuAnimationsDesc": "Menü- und Slide-Animationen aktivieren", - "disableBitmapCaching": "Bitmap-Cache deaktivieren", - "disableBitmapCachingDesc": "Bitmap-Cache deaktivieren (kann mit Glitches helfen)", - "disableOffscreenCaching": "Offscreen Cache deaktivieren", - "disableOffscreenCachingDesc": "Offscreen Cache ausschalten", - "disableGlyphCaching": "Glyph-Cache deaktivieren", - "disableGlyphCachingDesc": "Glyphencache ausschalten", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "RemoteFX-Grafik-Pipeline verwenden", - "enablePrinting": "Drucken aktivieren", - "enablePrintingDesc": "Lokale Drucker zur Remote-Sitzung weiterleiten", - "enableDriveRedirection": "Drive-Umleitung aktivieren", - "enableDriveRedirectionDesc": "Einen lokalen Ordner als Laufwerk in der Remote-Sitzung zuordnen", - "createDrivePath": "Laufwerkspfad erstellen", - "createDrivePathDesc": "Automatisch den Ordner erstellen, wenn er nicht existiert", - "disableDownload": "Download deaktivieren", - "disableDownloadDesc": "Verhindere das Herunterladen von Dateien von der Remote-Sitzung", - "disableUpload": "Upload deaktivieren", - "disableUploadDesc": "Das Hochladen von Dateien in die entfernte Sitzung verhindern", - "enableTouch": "Berührung aktivieren", - "enableTouchDesc": "Touch-Eingabeweiterleitung aktivieren", - "consoleSession": "Konsolen-Sitzung", - "consoleSessionDesc": "Verbinden Sie sich mit der Konsole (Sitzung 0) statt einer neuen Sitzung", - "sendWolPacket": "WOL-Paket senden", - "sendWolPacketDesc": "Senden Sie ein magisches Paket, um diesen Host aufzuwecken, bevor Sie sich verbinden", - "disableCopy": "Kopieren deaktivieren", - "disableCopyDesc": "Kopieren von Text aus der entfernten Sitzung verhindern", - "disablePaste": "Einfügen deaktivieren", - "disablePasteDesc": "Verhindern Sie das Einfügen von Text in die entfernte Sitzung", - "createPathIfMissing": "Pfad erstellen, wenn fehlt", - "createPathIfMissingDesc": "Automatisch das Aufnahmeverzeichnis erstellen", - "excludeOutput": "Ausgabe ausschließen", - "excludeOutputDesc": "Bildschirmausgabe nicht aufzeichnen (nur Metadaten)", - "excludeMouse": "Maus ausschließen", - "excludeMouseDesc": "Mausbewegungen nicht aufzeichnen", - "includeKeystrokes": "Tastenanschläge einbeziehen", - "includeKeystrokesDesc": "Zusätzlich zur Bildschirmausgabe rohe Tastenanschläge aufzeichnen", - "vncPort": "VNC-Port", - "vncPassword": "VNC Passwort", - "vncUsernameOptional": "Benutzername (optional)", - "vncLeaveBlank": "Leer lassen, wenn nicht erforderlich", - "cursorMode": "Cursor-Modus", - "swapRedBlue": "Rot/Blau tauschen", - "swapRedBlueDesc": "Vertausche die roten und blauen Kanäle (behebt einige Farbprobleme)", - "readOnly": "Nur lesen", - "readOnlyDesc": "Zeige den Remote-Bildschirm ohne Eingabe zu senden", - "telnetPort": "Telnet-Port", - "terminalType": "Terminaltyp", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Farbschema", - "backspaceKey": "Backspace-Taste", - "saveHostFirst": "Den Host zuerst speichern.", - "sharingOptionsAfterSave": "Freigabeoptionen sind verfügbar, nachdem der Host gespeichert wurde.", - "sharingLoadError": "Fehler beim Laden der Freigabedaten. Überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.", - "shareHostSection": "Host teilen", - "shareWithUser": "Mit Benutzer teilen", - "shareWithRole": "Mit Rolle teilen", - "selectUser": "Benutzer auswählen", - "selectRole": "Rolle auswählen", - "selectUserOption": "Benutzer auswählen...", - "selectRoleOption": "Wählen Sie eine Rolle...", - "permissionLevel": "Berechtigungsstufe", - "expiresInHours": "Läuft ab in (Stunden)", - "noExpiryPlaceholder": "Leer lassen für kein Ablaufdatum", - "shareBtn": "Teilen", - "currentAccess": "Aktueller Zugriff", - "typeHeader": "Typ", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Berechtigung", - "grantedByHeader": "Erteilt von", - "expiresHeader": "Gültig bis", - "noAccessEntries": "Noch keine Zugriffseinträge.", - "expiredLabel": "Abgelaufen", - "neverLabel": "Nie", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Abbrechen", - "savingBtn": "Speichern...", - "updateHostBtn": "Host aktualisieren", - "addHostBtn": "Host hinzufügen" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Hosts, Befehle oder Einstellungen suchen...", - "quickActions": "Schnelle Aktionen", - "hostManager": "Host-Manager", - "hostManagerDesc": "Hosts verwalten, hinzufügen oder bearbeiten", - "addNewHost": "Neuen Host hinzufügen", - "addNewHostDesc": "Neuen Host registrieren", - "adminSettings": "Admin-Einstellungen", - "adminSettingsDesc": "Systemeinstellungen und Benutzer konfigurieren", - "userProfile": "Benutzerprofil", - "userProfileDesc": "Konto und Einstellungen verwalten", - "addCredential": "Zugangsdaten hinzufügen", - "addCredentialDesc": "SSH-Schlüssel oder Passwörter speichern", - "recentActivity": "Letzte Aktivität", - "serversAndHosts": "Server & Hosts", - "noHostsFound": "Keine Hosts gefunden, die mit \"{{search}} \" übereinstimmen", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", "links": "Links", "navigate": "Navigate", - "select": "Auswählen", - "toggleWith": "Umschalten mit" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Kein Tab zugewiesen", - "focusedPane": "Aktiver Bereich" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Keine Verbindungen", - "noConnectionsDesc": "Öffnen Sie ein Terminal, einen Dateimanager oder eine Remote-Desktop-Verbindung, um die Verbindungen hier anzuzeigen.", - "connectedFor": "Verbunden für {{duration}}", - "connected": "Verbunden", - "disconnected": "Getrennt", - "closeTab": "Tab schließen", - "closeConnection": "Enge Verbindung", - "forgetTab": "Vergessen", - "removeBackground": "Entfernen", - "reconnect": "Wiederverbinden", - "reopenTab": "Wieder öffnen", - "sectionOpen": "Offen", - "sectionBackground": "Hintergrund", - "backgroundDesc": "Die Sitzungen bleiben nach der Trennung noch 30 Minuten bestehen und können wiederhergestellt werden.", - "persisted": "Im Hintergrund bestehen geblieben", - "expiresIn": "Läuft ab in {{duration}}", - "search": "Verbindungen suchen...", - "noSearchResults": "Es wurden keine Ergebnisse gefunden, die Ihrer Suche entsprechen." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Verbinde mit {{type}} Sitzung...", - "connectionError": "Verbindungsfehler", - "connectionFailed": "Verbindung fehlgeschlagen", - "failedToConnect": "Verbindungs-Token konnte nicht abgerufen werden", - "hostNotFound": "Host nicht gefunden", - "noHostSelected": "Kein Host ausgewählt", - "reconnect": "Neu verbinden", - "retry": "Wiederholen", - "guacdUnavailable": "Remote Desktop Service (guacd) ist nicht verfügbar. Bitte stellen Sie sicher, dass Guacd ausgeführt und in den Admin-Einstellungen korrekt konfiguriert und zugänglich ist.", - "ctrlAltDel": "Strg+Alt+Entf", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Strg+Alt+Entf", - "winL": "Win+L (Sperrbildschirm)", - "winKey": "Windows-Schlüssel", - "ctrl": "Strg", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "Schicht", - "win": "Sieg", - "stickyActive": "{{key}} (latched - Klicke zum Lösen)", - "stickyInactive": "{{key}} (Klicke auf latch)", - "esc": "Entfliehen", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Zuhause", - "end": "Ende", - "pageUp": "Seite oben", - "pageDown": "Seite runter", - "arrowUp": "Pfeil hoch", - "arrowDown": "Pfeil runter", - "arrowLeft": "Pfeil links", - "arrowRight": "Pfeil rechts", - "fnToggle": "Funktionstasten", - "reconnect": "Sitzung erneut verbinden", - "collapse": "Symbolleiste einklappen", - "expand": "Symbolleiste erweitern", - "dragHandle": "Zum Nachsetzen ziehen ziehen" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Mit Host verbinden", - "clear": "Leeren", - "paste": "Einfügen", - "reconnect": "Neu verbinden", - "connectionLost": "Verbindung verloren", - "connected": "Verbunden", - "clipboardWriteFailed": "Fehler beim Kopieren in die Zwischenablage. Stellen Sie sicher, dass die Seite über HTTPS oder localhost ausgeliefert wird.", - "clipboardReadFailed": "Lesen aus der Zwischenablage fehlgeschlagen. Stellen Sie sicher, dass die Zwischenablage Berechtigungen gewährt werden.", - "clipboardHttpWarning": "Einfügen erfordert HTTPS. Verwenden Sie Strg+Umschalt+V oder bedienen Termix über HTTPS.", - "unknownError": "Unbekannter Fehler aufgetreten", - "websocketError": "WebSocket-Verbindungsfehler", - "connecting": "Verbinden...", - "noHostSelected": "Kein Host ausgewählt", - "reconnecting": "Erneut verbinden... ({{attempt}}/{{max}})", - "reconnected": "Wiederverbindung erfolgreich", - "tmuxSessionCreated": "tmux Sitzung erstellt: {{name}}", - "tmuxSessionAttached": "tmux Sitzung angehängt: {{name}}", - "tmuxUnavailable": "tmux ist nicht auf dem entfernten Host installiert, zurück zur Standard-Shell", - "tmuxSessionPickerTitle": "tmux Sitzungen", - "tmuxSessionPickerDesc": "Vorhandene Tmux-Sitzungen auf diesem Host gefunden. Wählen Sie eine, um eine Sitzung neu anzuhängen oder zu erstellen.", - "tmuxWindows": "Fenster", - "tmuxWindowCount": "{{count}} Fenster", - "tmuxAttached": "Angehängte Clients", - "tmuxAttachedCount": "{{count}} hinzugefügt", - "tmuxLastActivity": "Letzte Aktivität", - "tmuxTimeJustNow": "gerade jetzt", - "tmuxTimeMinutes": "{{count}}m vor", - "tmuxTimeHours": "{{count}}h vor", - "tmuxTimeDays": "{{count}}d vor", - "tmuxCreateNew": "Neue Sitzung starten", - "tmuxCopyHint": "Auswahl anpassen und Enter drücken, um in die Zwischenablage zu kopieren", - "tmuxDetach": "Von tmux Sitzung trennen", - "tmuxDetached": "Von tmux Sitzung getrennt", - "maxReconnectAttemptsReached": "Maximale Wiederholungsversuche erreicht", - "closeTab": "Schließen", - "connectionTimeout": "Verbindungs-Timeout", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "{{command}} - {{host}}", - "totpRequired": "Zwei-Faktor-Authentifizierung erforderlich", - "totpCodeLabel": "Bestätigungscode", - "totpVerify": "Überprüfen", - "warpgateAuthRequired": "Warpgate-Authentifizierung erforderlich", - "warpgateSecurityKey": "Sicherheitsschlüssel", - "warpgateAuthUrl": "Authentifizierungs-URL", - "warpgateOpenBrowser": "Im Browser öffnen", - "warpgateContinue": "Ich habe die Authentifizierung abgeschlossen", - "opksshAuthRequired": "OPKSSH Authentifizierung erforderlich", - "opksshAuthDescription": "Vollständige Authentifizierung in Ihrem Browser, um fortzufahren. Diese Sitzung bleibt 24 Stunden gültig.", - "opksshOpenBrowser": "Browser zum Authentifizieren öffnen", - "opksshWaitingForAuth": "Warte auf Authentifizierung im Browser...", - "opksshAuthenticating": "Authentifizierung wird verarbeitet...", - "opksshTimeout": "Timeout der Authentifizierung. Bitte versuchen Sie es erneut.", - "opksshAuthFailed": "Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten und versuchen Sie es erneut.", - "opksshSignInWith": "Mit {{provider}} anmelden", - "sudoPasswordPopupTitle": "Passwort einfügen?", - "websocketAbnormalClose": "Verbindung wurde unerwartet geschlossen. Dies kann auf ein Problem mit der umgekehrten Proxy- oder SSL-Konfiguration zurückzuführen sein. Bitte überprüfen Sie die Serverprotokolle.", - "connectionLogTitle": "Verbindungsprotokoll", - "connectionLogCopy": "Protokolle in Zwischenablage kopieren", - "connectionLogEmpty": "Noch keine Verbindungsprotokolle", - "connectionLogWaiting": "Warte auf Verbindungsprotokolle...", - "connectionLogCopied": "Verbindungsprotokolle in Zwischenablage kopiert", - "connectionLogCopyFailed": "Fehler beim Kopieren der Logs in die Zwischenablage", - "connectionRejected": "Verbindung vom Server abgelehnt. Bitte überprüfen Sie Ihre Authentifizierung und Netzwerkkonfiguration.", - "hostKeyRejected": "Überprüfung des SSH-Host-Schlüssels abgelehnt. Verbindung abgebrochen.", - "sessionTakenOver": "Sitzung wurde in einem anderen Tab geöffnet. Wiederverbinden..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Kein Host ausgewählt", - "initializingEditor": "Initialisiere Editor...", - "file": "Datei", - "folder": "Ordner", - "uploadFile": "Datei hochladen", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", "downloadFile": "Download", - "extractArchive": "Archiv extrahieren", - "extractingArchive": "Extrahiere {{name}}...", - "archiveExtractedSuccessfully": "{{name}} erfolgreich extrahiert", - "extractFailed": "Entpacken fehlgeschlagen", - "compressFile": "Datei komprimieren", - "compressFiles": "Dateien komprimieren", - "compressFilesDesc": "{{count}} Elemente in ein Archiv komprimieren", - "archiveName": "Archivname", - "enterArchiveName": "Archivnamen eingeben...", - "compressionFormat": "Komprimierungsformat", - "selectedFiles": "Ausgewählte Dateien", - "andMoreFiles": "und {{count}} mehr...", - "compress": "Komprimieren", - "compressingFiles": "Komprimiere {{count}} Elemente in {{name}}...", - "filesCompressedSuccessfully": "{{name}} erfolgreich erstellt", - "compressFailed": "Komprimierung fehlgeschlagen", - "edit": "Bearbeiten", - "preview": "Vorschau", - "previous": "Vorherige", - "next": "Nächste", - "pageXOfY": "Seite {{current}} von {{total}}", - "zoomOut": "Verkleinern", - "zoomIn": "Vergrößern", - "newFile": "Neue Datei", - "newFolder": "Neuer Ordner", - "rename": "Umbenennen", - "uploading": "Hochladen...", - "uploadingFile": "Lade {{name}} hoch...", - "fileName": "Dateiname", - "folderName": "Ordnername", - "fileUploadedSuccessfully": "Datei \"{{name}}\" erfolgreich hochgeladen", - "failedToUploadFile": "Fehler beim Hochladen der Datei", - "fileDownloadedSuccessfully": "Datei \"{{name}}\" erfolgreich heruntergeladen", - "failedToDownloadFile": "Fehler beim Herunterladen der Datei", - "fileCreatedSuccessfully": "Datei \"{{name}}\" erfolgreich erstellt", - "folderCreatedSuccessfully": "Ordner \"{{name}}\" erfolgreich erstellt", - "failedToCreateItem": "Element konnte nicht erstellt werden", - "operationFailed": "{{operation}} Vorgang fehlgeschlagen für {{name}}: {{error}}", - "failedToResolveSymlink": "Fehler beim Auflösen des Symlinks", - "itemsDeletedSuccessfully": "{{count}} Elemente erfolgreich gelöscht", - "failedToDeleteItems": "Löschen der Elemente fehlgeschlagen", - "sudoPasswordRequired": "Administratorpasswort erforderlich", - "enterSudoPassword": "Sudo-Passwort eingeben, um diesen Vorgang fortzuführen", - "sudoPassword": "Sudo Passwort", - "sudoOperationFailed": "Sudo-Operation fehlgeschlagen", - "sudoAuthFailed": "Sudo-Authentifizierung fehlgeschlagen", - "dragFilesToUpload": "Dateien zum Hochladen hier ablegen", - "emptyFolder": "Dieser Ordner ist leer", - "searchFiles": "Dateien suchen...", - "upload": "Hochladen", - "selectHostToStart": "Wähle einen Host um die Dateiverwaltung zu starten", - "sshRequiredForFileManager": "Der Dateimanager benötigt SSH. Für diesen Host ist SSH nicht aktiviert.", - "failedToConnect": "Verbindung zu SSH fehlgeschlagen", - "failedToLoadDirectory": "Verzeichnis konnte nicht geladen werden", - "noSSHConnection": "Keine SSH-Verbindung verfügbar", - "copy": "Kopieren", - "cut": "Schneide", - "paste": "Einfügen", - "copyPath": "Pfad kopieren", - "copyPaths": "Pfade kopieren", - "delete": "Löschen", - "properties": "Eigenschaften", - "refresh": "Aktualisieren", - "downloadFiles": "Download von {{count}} Dateien in den Browser", - "copyFiles": "Kopiere {{count}} Elemente", - "cutFiles": "{{count}} Elemente schneiden", - "deleteFiles": "{{count}} Elemente löschen", - "filesCopiedToClipboard": "{{count}} Elemente in die Zwischenablage kopiert", - "filesCutToClipboard": "{{count}} Elemente in die Zwischenablage geschnitten", - "pathCopiedToClipboard": "Pfad in Zwischenablage kopiert", - "pathsCopiedToClipboard": "{{count}} Pfade in die Zwischenablage kopiert", - "failedToCopyPath": "Fehler beim Kopieren des Pfades in die Zwischenablage", - "movedItems": "Verschobene {{count}} Elemente", - "failedToDeleteItem": "Element konnte nicht gelöscht werden", - "itemRenamedSuccessfully": "{{type}} erfolgreich umbenannt", - "failedToRenameItem": "Umbenennen fehlgeschlagen", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", "download": "Download", - "permissions": "Berechtigungen", - "size": "Größe", - "modified": "Geändert", - "path": "Pfad", - "confirmDelete": "Sind Sie sicher, dass Sie {{name}} löschen möchten?", - "permissionDenied": "Berechtigung verweigert", - "serverError": "Serverfehler", - "fileSavedSuccessfully": "Datei erfolgreich gespeichert", - "failedToSaveFile": "Fehler beim Speichern der Datei", - "confirmDeleteSingleItem": "Sind Sie sicher, dass Sie \"{{name}} \"dauerhaft löschen möchten?", - "confirmDeleteMultipleItems": "Sind Sie sicher, dass Sie {{count}} Elemente dauerhaft löschen möchten?", - "confirmDeleteMultipleItemsWithFolders": "Sind Sie sicher, dass Sie {{count}} Elemente dauerhaft löschen möchten? Dies schließt Ordner und deren Inhalt mit ein.", - "confirmDeleteFolder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und dessen Inhalt dauerhaft löschen möchten?", - "permanentDeleteWarning": "Diese Aktion kann nicht rückgängig gemacht werden. Die Item(s) werden dauerhaft vom Server gelöscht.", - "recent": "Neueste", - "pinned": "Angeheftet", - "folderShortcuts": "Ordnerverknüpfungen", - "failedToReconnectSSH": "SSH-Sitzung konnte nicht wiederhergestellt werden", - "openTerminalHere": "Terminal hier öffnen", - "run": "Ausführen", - "openTerminalInFolder": "Terminal in diesem Ordner öffnen", - "openTerminalInFileLocation": "Öffne Terminal am Datei-Speicherort", - "runningFile": "Laufend - {{file}}", - "onlyRunExecutableFiles": "Kann nur ausführbare Dateien ausführen", - "directories": "Verzeichnisse", - "removedFromRecentFiles": "\"{{name}}\" aus den letzten Dateien entfernt", - "removeFailed": "Entfernen fehlgeschlagen", - "unpinnedSuccessfully": "\"{{name}}\" erfolgreich entfernt", - "unpinFailed": "Entfernen fehlgeschlagen", - "removedShortcut": "Verknüpfung \"{{name}} \" entfernt", - "removeShortcutFailed": "Entfernen der Verknüpfung fehlgeschlagen", - "clearedAllRecentFiles": "Alle letzten Dateien gelöscht", - "clearFailed": "Löschen fehlgeschlagen", - "removeFromRecentFiles": "Aus den letzten Dateien entfernen", - "clearAllRecentFiles": "Alle letzten Dateien löschen", - "unpinFile": "Datei lösen", - "removeShortcut": "Verknüpfung entfernen", - "pinFile": "Pin Datei", - "addToShortcuts": "Zu Verknüpfungen hinzufügen", - "pasteFailed": "Einfügen fehlgeschlagen", - "noUndoableActions": "Keine rückgängig machbaren Aktionen", - "undoCopySuccess": "Nicht kopierte Operation: Gelöschte {{count}} kopierte Dateien", - "undoCopyFailedDelete": "Rückgängig fehlgeschlagen: Konnte keine kopierten Dateien löschen", - "undoCopyFailedNoInfo": "Rückgängig fehlgeschlagen: Kopierte Dateiinformationen konnten nicht gefunden werden", - "undoMoveSuccess": "Vorgang nicht verschoben: Verschiebe {{count}} Dateien zurück zum ursprünglichen Speicherort", - "undoMoveFailedMove": "Rückgängig fehlgeschlagen: Konnte keine Dateien zurück verschieben", - "undoMoveFailedNoInfo": "Rückgängig fehlgeschlagen: Konnte die verschobene Dateiinformation nicht finden", - "undoDeleteNotSupported": "Löschvorgang kann nicht rückgängig gemacht werden: Dateien wurden dauerhaft vom Server gelöscht", - "undoTypeNotSupported": "Nicht unterstützter Undo-Operationstyp", - "undoOperationFailed": "Rückgängig fehlgeschlagen", - "unknownError": "Unbekannter Fehler", - "confirm": "Bestätigen", - "find": "Suchen...", - "replace": "Ersetzen", - "downloadInstead": "Stattdessen herunterladen", - "keyboardShortcuts": "Tastaturkürzel", - "searchAndReplace": "Suchen & Ersetzen", - "editing": "Bearbeiten", - "search": "Suchen", - "findNext": "Nächste suchen", - "findPrevious": "Vorheriges suchen", - "save": "Speichern", - "selectAll": "Alle auswählen", - "undo": "Rückgängig", - "redo": "Wiederholen", - "moveLineUp": "Linie nach oben", - "moveLineDown": "Linie runter bewegen", - "toggleComment": "Kommentar umschalten", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Fehler beim Laden des Bildes", - "startTyping": "Tippe ein...", - "unknownSize": "Unbekannte Größe", - "fileIsEmpty": "Datei ist leer", - "largeFileWarning": "Große Datei-Warnung", - "largeFileWarningDesc": "Diese Datei hat eine Größe von {{size}} , was Performance-Probleme verursachen kann, wenn sie als Text geöffnet wird.", - "fileNotFoundAndRemoved": "Datei \"{{name}}\" nicht gefunden und wurde aus den zuletzt angepinnten Dateien entfernt", - "failedToLoadFile": "Fehler beim Laden der Datei: {{error}}", - "serverErrorOccurred": "Serverfehler aufgetreten. Bitte versuchen Sie es später erneut.", - "autoSaveFailed": "Auto-Speichern fehlgeschlagen", - "fileAutoSaved": "Auto-Datei gespeichert", - "moveFileFailed": "Fehler beim Verschieben von {{name}}", - "moveOperationFailed": "Verschieben fehlgeschlagen", - "canOnlyCompareFiles": "Kann nur zwei Dateien vergleichen", - "comparingFiles": "Vergleiche Dateien: {{file1}} und {{file2}}", - "dragFailed": "Ziehvorgang fehlgeschlagen", - "filePinnedSuccessfully": "Datei \"{{name}}\" erfolgreich angepinnt", - "pinFileFailed": "Fehler beim Pin der Datei", - "fileUnpinnedSuccessfully": "Datei \"{{name}}\" erfolgreich entfernt", - "unpinFileFailed": "Fehler beim Entfernen der Datei", - "shortcutAddedSuccessfully": "Ordnerverknüpfung \"{{name}}\" erfolgreich hinzugefügt", - "addShortcutFailed": "Fehler beim Hinzufügen der Verknüpfung", - "operationCompletedSuccessfully": "{{operation}} {{count}} Elemente erfolgreich", - "operationCompleted": "{{operation}} {{count}} Elemente", - "downloadFileSuccess": "Datei {{name}} erfolgreich heruntergeladen", - "downloadFileFailed": "Download fehlgeschlagen", - "moveTo": "Zu {{name}} verschieben", - "diffCompareWith": "Diff vergleichen mit {{name}}", - "dragOutsideToDownload": "Zum Herunterladen nach außen ziehen ({{count}} Dateien)", - "newFolderDefault": "Neuer Ordner", - "newFileDefault": "Neue Datei.txt", - "successfullyMovedItems": "{{count}} Elemente erfolgreich nach {{target}} verschoben", - "move": "Bewegen", - "searchInFile": "Suche in Datei (Strg+F)", - "showKeyboardShortcuts": "Tastaturkürzel anzeigen", - "startWritingMarkdown": "Schreiben Sie Ihren Markdown-Inhalt...", - "loadingFileComparison": "Lade Dateivergleich...", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Vergleichen", - "sideBySide": "Seite für Seite", + "compare": "Compare", + "sideBySide": "Side by Side", "inline": "Inline", - "fileComparison": "Dateivergleich: {{file1}} vs {{file2}}", - "fileTooLarge": "Datei zu groß: {{error}}", - "sshConnectionFailed": "SSH-Verbindung fehlgeschlagen. Bitte überprüfe deine Verbindung zu {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Fehler beim Laden der Datei: {{error}}", - "connecting": "Verbinden...", - "connectedSuccessfully": "Verbunden erfolgreich", - "totpVerificationFailed": "TOTP-Verifizierung fehlgeschlagen", - "warpgateVerificationFailed": "Warpgate-Authentifizierung fehlgeschlagen", - "authenticationFailed": "Authentifizierung fehlgeschlagen", - "verificationCodePrompt": "Bestätigungscode:", - "changePermissions": "Berechtigungen ändern", - "currentPermissions": "Aktuelle Berechtigungen", - "owner": "Besitzer", - "group": "Gruppe", - "others": "Andere", - "read": "Lesen", - "write": "Schreiben", - "execute": "Ausführen", - "permissionsChangedSuccessfully": "Berechtigungen erfolgreich geändert", - "failedToChangePermissions": "Fehler beim Ändern der Berechtigungen", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", "name": "Name", "sortByName": "Name", - "sortByDate": "Änderungsdatum", - "sortBySize": "Größe", - "ascending": "Aufsteigend", - "descending": "Absteigend", - "root": "Wurzel", - "new": "Neu", - "sortBy": "Sortieren nach", - "items": "Artikel", - "selected": "Ausgewählt", - "editor": "Redakteur", - "octal": "Oktalität", - "storage": "Speicher", - "disk": "Platte", - "used": "Verwendet", - "of": "von", - "toggleSidebar": "Seitenleiste umschalten", - "cannotLoadPdf": "Kann PDF nicht laden", - "pdfLoadError": "Beim Laden dieser PDF-Datei ist ein Fehler aufgetreten.", - "loadingPdf": "Lade PDF...", - "loadingPage": "Seite wird geladen..." + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Zum Host kopieren…", - "moveToHost": "Zum Host wechseln…", - "copyItemsToHost": "Kopiere {{count}} Elemente in den Host…", - "moveItemsToHost": "Verschiebe {{count}} Elemente in den Host…", - "noHostsConnected": "Es sind keine anderen Dateimanager-Hosts verfügbar.", - "noHostsConnectedHint": "Fügen Sie im Host-Manager einen weiteren SSH-Host mit aktiviertem Dateimanager hinzu.", - "selectDestinationHost": "Zielhost auswählen", - "destinationPath": "Zielpfad", - "recentDestinations": "Aktuelle Reiseziele", - "collapseRecentDestinations": "Aktuelle Reiseziele ausblenden", - "expandRecentDestinations": "Aktuelle Reiseziele erweitern", - "browseFolders": "Zielordner durchsuchen", - "browseDestination": "Pfad durchsuchen oder eingeben", - "confirmCopy": "Kopie", - "confirmMove": "Bewegen", - "transferring": "Übertragen…", - "compressing": "Komprimierung…", - "extracting": "Extrahiere…", - "transferringItems": "Übertrage {{current}} von {{total}} Elementen…", - "transferSuccess": "Übertragung abgeschlossen", - "transferError": "Übertragung fehlgeschlagen", - "transferPartial": "Übertragung mit {{count}} Fehlern abgeschlossen", - "transferPartialHint": "Übertragung fehlgeschlagen: {{paths}}", - "itemsSummary": "{{count}} Artikel", - "destMustBeDirectory": "Das Ziel muss ein Verzeichnis für die Übertragung mehrerer Elemente sein.", - "selectThisFolder": "Wählen Sie diesen Ordner aus", - "browsePathWillBeCreated": "Dieser Ordner existiert noch nicht. Er wird erstellt, sobald die Übertragung beginnt.", - "browsePathError": "Dieser Pfad konnte auf dem Zielhost nicht geöffnet werden.", - "goUp": "Steigen", - "copyFolderToHost": "Ordner auf Host kopieren…", - "moveFolderToHost": "Ordner zum Host verschieben…", - "hostReady": "Bereit", - "hostConnecting": "Verbindet…", - "hostDisconnected": "Nicht verbunden", - "hostAuthRequired": "Authentifizierung erforderlich – öffnen Sie zuerst den Dateimanager auf diesem Host.", - "hostConnectionFailed": "Verbindung fehlgeschlagen", - "metricsTitle": "Übertragungszeiten", - "metricsPrepare": "Ziel vorbereiten: {{duration}}", - "metricsCompress": "Komprimierung auf der Quelle: {{duration}}", - "metricsHopSourceRead": "Quelle → Server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → Ziel (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → Ziel (lokal): {{throughput}}", - "metricsTransfer": "End-to-End: {{throughput}} ({{duration}})", - "metricsExtract": "Auszug am Zielort: {{duration}}", - "metricsSourceDelete": "Aus der Quelle entfernen: {{duration}}", - "metricsTotal": "Gesamt: {{duration}}", - "progressCompressing": "Komprimierung auf dem Quellhost…", - "progressExtracting": "Extraktion am Ziel…", - "progressTransferring": "Datenübertragung…", - "progressReconnecting": "Verbindung wird wiederhergestellt…", - "parallelSegmentsLabel": "Parallele Übergabespuren", - "parallelSegmentsOption": "{{count}} Spuren", - "parallelSegmentsHint": "Große Dateien werden in 256 MB große Teile aufgeteilt. Mehrere Übertragungswege nutzen separate Verbindungen (ähnlich wie beim Starten mehrerer Übertragungen), um einen höheren Gesamtdurchsatz zu erzielen.", - "progressTotalSpeed": "{{speed}} Gesamt ({{lanes}} Spuren)", - "progressTransferringItems": "Übertrage Dateien ({{current}} von {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} Dateien", - "sourceNotDeletedPartial": "Quelldateien beibehalten (teilweise Übertragung)", - "jumpHostLimitation": "Beide Hosts müssen vom Termix-Server aus erreichbar sein. Direktes Host-zu-Host-Routing wird nicht unterstützt.", - "cancel": "Stornieren", - "methodLabel": "Übertragungsmethode", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Tar-Archiv", - "methodItemSftp": "SFTP pro Datei", - "methodAutoHint": "Wählt je nach Dateianzahl, -größe und Komprimierbarkeit entweder tar-Archive oder dateibasierte SFTP-Dateien aus. Einzelne Dateien werden immer per Streaming-SFTP übertragen.", - "methodTarHint": "Komprimieren auf dem Quellsystem, ein Archiv übertragen, auf dem Zielsystem extrahieren. Erfordert tar auf beiden Unix-Hosts.", - "methodItemSftpHint": "Übertragen Sie jede Datei einzeln per SFTP. Funktioniert auf allen Hosts, einschließlich Windows.", - "methodPreviewLoading": "Berechnung der Transfermethode…", - "methodPreviewError": "Die Übertragungsmethode konnte nicht in der Vorschau angezeigt werden. Der Server wählt beim Start eine Methode aus.", - "methodPreviewWillUseTar": "Verwendet wird: Tar-Archiv", - "methodPreviewWillUseItemSftp": "Wird verwendet: SFTP pro Datei", - "methodPreviewScanSummary": "{{fileCount}} Dateien, {{totalSize}} insgesamt (auf dem Quellhost gescannt).", - "methodItemSftpLimitation": "Jede Datei nutzt denselben SFTP-Stream als Einzeldateikopie, die nacheinander erstellt werden. Der Fortschritt wird über alle Dateien hinweg kombiniert, daher bewegt sich der Balken bei großen Dateien nur langsam.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Sie haben SFTP pro Datei ausgewählt.", - "user_tar": "Sie haben das Tar-Archiv ausgewählt.", - "tar_unavailable": "Tar ist auf einem oder beiden Hosts nicht verfügbar – stattdessen wird SFTP pro Datei verwendet.", - "windows_host": "Es handelt sich um einen Windows-Host – tar wird nicht verwendet.", - "auto_multi_large": "Auto: Mehrere Dateien, einschließlich einer großen Datei ({{largestSize}}) mit komprimierbaren Daten - tar-Bundles in einer Übertragung.", - "auto_single_large_in_archive": "Auto: eine große Datei ({{largestSize}}) in diesem Satz — dateibasiertes SFTP.", - "auto_many_incompressible": "Auto: größtenteils nicht komprimierbare Daten — dateibasiertes SFTP.", - "auto_many_files": "Auto: viele Dateien ({{fileCount}}) — tar reduziert den Overhead pro Datei.", - "auto_default": "Auto: SFTP pro Datei für diesen Satz." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Stornieren", - "progressCancelling": "Abbrechen…", - "progressStalled": "Stillgelegt", - "resumedHint": "Die Verbindung zu einer in einem anderen Fenster gestarteten aktiven Übertragung wurde wiederhergestellt.", - "transferCancelled": "Transfer storniert", - "transferCancelledCopyHint": "Es können unvollständige Dateien am Zielort verbleiben.", - "transferCancelledMoveHint": "Es können noch unvollständige Dateien auf dem Zielsystem vorhanden sein. Die Quelldateien wurden nicht entfernt.", - "cleanupDestFiles": "Zielort aufräumen", - "cleanupDestFilesSuccess": "Teilweise Dateien aus dem Zielverzeichnis entfernt", - "cleanupDestFilesPartial": "Einige unvollständige Dateien konnten nicht entfernt werden.", - "cleanupDestFilesNothing": "Am Zielort gibt es nichts aufzuräumen.", - "cleanupDestFilesError": "Bereinigung fehlgeschlagen", - "retryTransfer": "Wiederholen", - "retryTransferError": "Wiederholungsversuch fehlgeschlagen", - "transferFailedRetryHint": "Auf dem Zielserver wurden nur Teildaten gespeichert. Der Verbindungsversuch wird fortgesetzt, sobald die Verbindung wiederhergestellt ist." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Keine SSH-Tunnel", - "createFirstTunnelMessage": "Sie haben noch keine SSH-Tunnel erstellt. Konfigurieren Sie Tunnelverbindungen im Host Manager, um loszulegen.", - "connected": "Verbunden", - "disconnected": "Verbindung getrennt", - "connecting": "Verbinden...", - "error": "Fehler", - "canceling": "Abbrechen...", - "connect": "Verbinden", - "disconnect": "Verbindung trennen", - "cancel": "Abbrechen", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Lokaler Port", - "remotePort": "Entfernter Port", - "currentHostPort": "Aktueller Host-Port", - "endpointPort": "Endpunkt-Port", - "bindIp": "Lokale IP", - "endpointSshConfig": "Endpunkt SSH Konfiguration", - "endpointSshHost": "Endpunkt SSH-Host", - "endpointSshHostPlaceholder": "Wähle einen konfigurierten Host", - "endpointSshHostRequired": "Wählen Sie einen Endpunkt-SSH-Host für jeden Client-Tunnel.", - "attempt": "{{current}} von {{max}} versuchen", - "nextRetryIn": "Nächste Wiederholung in {{seconds}} Sekunden", - "clientTunnels": "Kliententunnel", - "clientTunnel": "Kliententunnel", - "addClientTunnel": "Kliententunnel hinzufügen", - "noClientTunnels": "Keine Client-Tunnel auf diesem Desktop konfiguriert.", - "tunnelName": "Tunnelname", - "remoteHost": "Entfernter Host", - "autoStart": "Autostart", - "clientAutoStartDesc": "Startet, wenn sich dieser Desktop-Client öffnet und verbunden bleibt.", - "clientManualStartDesc": "Verwenden Sie Start und Stop aus dieser Zeile. Termix öffnet sie nicht automatisch.", - "clientRemoteServerNote": "Remote-Weiterleitung kann AllowTcpForwarding und GatewayPorts auf dem SSH-Server erfordern. Der entfernte Port wird geschlossen, wenn dieser Desktop sich trennt.", - "clientTunnelStarted": "Kliententunnel gestartet", - "clientTunnelStopped": "Kliententunnel gestoppt", - "tunnelTestSucceeded": "Tunneltest erfolgreich", - "tunnelTestFailed": "Tunneltest fehlgeschlagen", - "localSaved": "Kliententunnel gespeichert", - "localSaveError": "Fehler beim Speichern lokaler Client-Tunnel", - "invalidBindIp": "Lokale IP muss eine gültige IPv4-Adresse sein.", - "invalidLocalTargetIp": "Lokale Ziel-IP muss eine gültige IPv4-Adresse sein.", - "invalidLocalPort": "Der lokale Port muss zwischen 1 und 65535 liegen.", - "invalidRemotePort": "Der Remote Port muss zwischen 1 und 65535 liegen.", - "invalidLocalTargetPort": "Lokaler Port muss zwischen 1 und 65535 liegen.", - "invalidEndpointPort": "Endpunkt-Port muss zwischen 1 und 65535 liegen.", - "duplicateAutoStartBind": "Nur ein Kliententunnel kann {{bind}} verwenden.", - "manualControlError": "Tunnel Status konnte nicht aktualisiert werden.", - "active": "Aktiv", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", "start": "Start", - "stop": "Stoppen", - "test": "Testen", - "type": "Tunneltyp", - "typeLocal": "Lokal (-L)", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", "typeRemote": "Remote (-R)", - "typeDynamic": "Dynamisch (-D)", - "typeServerLocalDesc": "Aktueller Host bis Endpunkt.", - "typeServerRemoteDesc": "Endpunkt zurück zum aktuellen Host.", - "typeClientLocalDesc": "Lokaler Computer bis Endpunkt.", - "typeClientRemoteDesc": "Endpunkt zurück zum lokalen Computer.", - "typeClientDynamicDesc": "SOCKS auf lokalem Computer.", - "typeDynamicDesc": "SOCKS5 CONNECT Verkehr durch SSH weiterleiten", - "forwardDescriptionServerLocal": "Aktueller Host {{sourcePort}} → Endpunkt {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpunkt {{endpointPort}} → aktueller Host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS auf dem aktuellen Host {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokale {{sourcePort}} → Remote- {{endpointPort}}.", - "forwardDescriptionClientRemote": "Entfernte {{sourcePort}} → Lokale {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS auf lokalen Port {{sourcePort}}.", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "Lokale {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokale {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", "route": "Route:", - "lastStarted": "Zuletzt gestartet", - "lastTested": "Zuletzt geprüft", - "lastError": "Letzter Fehler", - "maxRetries": "Max. Wiederholungen", - "maxRetriesDescription": "Maximale Anzahl an Wiederholungsversuchen.", - "retryInterval": "Wiederholungsintervall (Sekunden)", - "retryIntervalDescription": "Wartezeit zwischen wiederholten Versuchen.", - "local": "Lokal", - "remote": "Entfernt", - "destination": "Ziel", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", "host": "Host", - "mode": "Modus", - "noHostSelected": "Kein Host ausgewählt", - "working": "Arbeiten..." + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Speicher", - "disk": "Platte", - "network": "Netzwerk", - "uptime": "Laufzeit", - "processes": "Prozesse", - "available": "Verfügbar", - "free": "Kostenlos", - "connecting": "Verbinden...", - "connectionFailed": "Verbindung zum Server fehlgeschlagen", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "{{count}} Kern", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU Auslastung", - "memoryUsage": "Speichernutzung", - "diskUsage": "Plattennutzung", - "failedToFetchHostConfig": "Fehler beim Abrufen der Host-Konfiguration", - "serverOffline": "Server offline", - "cannotFetchMetrics": "Metriken können nicht vom Offline-Server abgerufen werden", - "totpFailed": "TOTP-Verifizierung fehlgeschlagen", - "noneAuthNotSupported": "Serverstatistik unterstützt keine 'keine' Authentifizierungstyp.", - "load": "Laden", - "systemInfo": "Systeminformationen", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Betriebssystem", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "Sekunden", - "networkInterfaces": "Netzwerkschnittstellen", - "noInterfacesFound": "Keine Netzwerkschnittstellen gefunden", - "noProcessesFound": "Keine Prozesse gefunden", - "loginStats": "SSH-Login-Statistiken", - "noRecentLoginData": "Keine letzten Login-Daten", - "executingQuickAction": "Ausführe {{name}}...", - "quickActionSuccess": "{{name}} erfolgreich abgeschlossen", - "quickActionFailed": "{{name}} fehlgeschlagen", - "quickActionError": "Fehler beim Ausführen von {{name}}", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Höre Ports", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adresse", - "process": "Verarbeiten", - "noData": "Keine Daten zum Abhören" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inaktiv", - "policy": "Richtlinien", - "rules": "regeln", - "noData": "Keine Firewall-Daten verfügbar", - "action": "Aktion", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Quelle", - "anywhere": "Überall" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Ladeformat", - "swap": "Tausch", - "architecture": "Architektur", - "refresh": "Aktualisieren", - "retry": "Wiederholen" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Selbstgehostete SSH- und Remote-Desktop-Verwaltung", - "loginTitle": "Bei Termix anmelden", - "registerTitle": "Konto erstellen", - "forgotPassword": "Passwort vergessen?", - "rememberMe": "Gerät für 30 Tage merken (inklusive TOTP)", - "noAccount": "Sie haben noch kein Konto?", - "hasAccount": "Haben Sie bereits ein Konto?", - "twoFactorAuth": "Zwei-Faktor-Authentifizierung", - "enterCode": "Bestätigungscode eingeben", - "backupCode": "Oder Backup-Code verwenden", - "verifyCode": "Code überprüfen", - "redirectingToApp": "Weiterleitung zu App...", - "sshAuthenticationRequired": "SSH-Authentifizierung erforderlich", - "sshNoKeyboardInteractive": "Tastatur-Interaktive Authentifizierung nicht verfügbar", - "sshAuthenticationFailed": "Authentifizierung fehlgeschlagen", - "sshAuthenticationTimeout": "Authentifizierungs-Timeout", - "sshNoKeyboardInteractiveDescription": "Der Server unterstützt keine interaktive Keyboard-Authentifizierung. Bitte geben Sie Ihr Passwort oder SSH-Schlüssel ein.", - "sshAuthFailedDescription": "Die angegebenen Zugangsdaten waren falsch. Bitte versuchen Sie es mit gültigen Zugangsdaten erneut.", - "sshTimeoutDescription": "Timeout für den Authentifizierungsversuch. Bitte versuchen Sie es erneut.", - "sshProvideCredentialsDescription": "Bitte geben Sie Ihre SSH-Zugangsdaten an, um sich mit diesem Server zu verbinden.", - "sshPasswordDescription": "Geben Sie das Passwort für diese SSH-Verbindung ein.", - "sshKeyPasswordDescription": "Wenn Ihr SSH-Schlüssel verschlüsselt ist, geben Sie hier die Passphrase ein.", - "passphraseRequired": "Passphrase erforderlich", - "passphraseRequiredDescription": "Der SSH-Schlüssel ist verschlüsselt. Bitte geben Sie die Passphrase ein, um ihn zu entsperren.", - "back": "Zurück", - "firstUser": "Erster Benutzer", - "firstUserMessage": "Sie sind der erste Benutzer und werden zu einem Admin gemacht. Sie können die Admin-Einstellungen in der Seitenleiste anzeigen. Wenn du denkst, dass dies ein Fehler ist, überprüfe die Docker Protokolle oder erstelle ein GitHub Problem.", - "external": "Externe", - "loginWithExternal": "Mit externem Anbieter anmelden", - "loginWithExternalDesc": "Melden Sie sich mit Ihrem konfigurierten externen Identity Provider an", - "externalNotSupportedInElectron": "Externe Authentifizierung wird in der Electron-App noch nicht unterstützt. Bitte verwenden Sie die Web-Version für OIDC-Login.", - "resetPasswordButton": "Passwort zurücksetzen", - "sendResetCode": "Sende Reset-Code", - "resetCodeDesc": "Geben Sie Ihren Benutzernamen ein, um einen Code zum Zurücksetzen des Passworts zu erhalten. Der Code wird in die Docker Containerprotokolle eingeloggt.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Code überprüfen", - "enterResetCode": "Geben Sie den 6-stelligen Code aus dem Docker Containerprotokoll für Benutzer ein:", - "newPassword": "Neues Passwort", - "confirmNewPassword": "Passwort bestätigen", - "enterNewPassword": "Geben Sie Ihr neues Passwort für den Benutzer ein:", - "signUp": "Registrieren", - "desktopApp": "Desktop-App", - "loggingInToDesktopApp": "Anmelden in der Desktop-App", - "loadingServer": "Lade Server...", - "dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Anmeldedaten und andere verschlüsselte Daten gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Verwenden Sie dies nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind.", - "authenticationDisabled": "Authentifizierung deaktiviert", - "authenticationDisabledDesc": "Alle Authentifizierungsmethoden sind derzeit deaktiviert. Bitte kontaktieren Sie Ihren Administrator.", - "attemptsRemaining": "{{count}} verbleibende Versuche" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH-Hostschlüssel überprüfen", - "keyChangedWarning": "SSH-Hostschlüssel geändert", - "firstConnectionTitle": "Verbindung zu diesem Host zum ersten Mal", - "firstConnectionDescription": "Die Authentizität dieses Hosts kann nicht festgestellt werden. Überprüfen Sie den Fingerabdruck mit dem, was Sie erwarten.", - "keyChangedDescription": "Der Host-Schlüssel für diesen Server hat sich seit Ihrer letzten Verbindung geändert. Dies könnte auf ein Sicherheitsproblem hinweisen.", - "previousKey": "Vorheriger Schlüssel", - "newFingerprint": "Neuer Fingerabdruck", - "fingerprint": "Fingerabdruck", - "verifyInstructions": "Wenn Sie diesem Host vertrauen, klicken Sie auf Akzeptieren, um fortzufahren und den Fingerabdruck für zukünftige Verbindungen zu speichern.", - "securityWarning": "Sicherheitswarnung", - "acceptAndContinue": "Akzeptieren & fortfahren", - "acceptNewKey": "Neuen Schlüssel akzeptieren & fortfahren" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Keine Verbindung zur Datenbank möglich", - "unknownError": "Unbekannter Fehler", - "loginFailed": "Login fehlgeschlagen", - "failedPasswordReset": "Fehler beim Initiieren des Passwort-Resets", - "failedVerifyCode": "Fehler beim Überprüfen des Reset-Codes", - "failedCompleteReset": "Fehler beim Zurücksetzen des Passworts", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Fehler beim Starten des OIDC-Logins", - "silentSigninOidcUnavailable": "Stumme Anmeldung wurde angefordert, aber OIDC Login ist nicht verfügbar.", - "failedUserInfo": "Fehler beim Abrufen der Benutzerinformationen nach dem Login", - "oidcAuthFailed": "OIDC Authentifizierung fehlgeschlagen", - "invalidAuthUrl": "Ungültige Autorisierungs-URL vom Backend erhalten", - "requiredField": "Dieses Feld ist erforderlich", - "minLength": "Mindestlänge ist {{min}}", - "passwordMismatch": "Passwörter stimmen nicht überein", - "passwordLoginDisabled": "Benutzername/Passwort Login ist derzeit deaktiviert", - "sessionExpired": "Sitzung abgelaufen - Bitte melden Sie sich erneut an", - "totpRateLimited": "Rate begrenzt: Zu viele TOTP-Verifizierungsversuche. Bitte versuchen Sie es später erneut.", - "totpRateLimitedWithTime": "Rate begrenzt: Zu viele TOTP-Verifizierungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", - "resetCodeRateLimited": "Rate begrenzt: Zu viele Verifizierungsversuche. Bitte versuchen Sie es später erneut.", - "resetCodeRateLimitedWithTime": "Rate begrenzt: Zu viele Verifizierungsversuche. Bitte warten Sie {{time}} Sekunden, bevor Sie es erneut versuchen.", - "authTokenSaveFailed": "Authentifizierungstoken konnte nicht gespeichert werden", - "failedToLoadServer": "Fehler beim Laden des Servers" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Die Registrierung eines neuen Kontos ist derzeit von einem Administrator deaktiviert. Bitte melden Sie sich an oder kontaktieren Sie einen Administrator.", - "userNotAllowed": "Ihr Konto ist nicht berechtigt, sich zu registrieren. Bitte kontaktieren Sie einen Administrator.", - "databaseConnectionFailed": "Verbindung zum Datenbankserver fehlgeschlagen", - "resetCodeSent": "Code zurücksetzen gesendet an Docker Logs", - "codeVerified": "Code erfolgreich verifiziert", - "passwordResetSuccess": "Passwort erfolgreich zurückgesetzt", - "loginSuccess": "Anmeldung erfolgreich", - "registrationSuccess": "Registrierung erfolgreich" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokale Desktop-Tunnel, die auf konfigurierte SSH-Hosts abzielen.", - "c2sTunnelPresets": "Client-Tunnel Voreinstellungen", - "c2sTunnelPresetsDesc": "Speichere die lokale Tunnelliste dieses Desktop-Clients als benannte Server-Voreinstellung oder lade eine Voreinstellung zurück in diesen Client.", - "c2sTunnelPresetsUnavailable": "Voreinstellungen für den Client Tunnel sind nur im Desktop-Client verfügbar.", - "c2sPresetName": "Preset-Name", - "c2sPresetNamePlaceholder": "Name des Presets", - "c2sPresetToLoad": "Voreinstellung zum Laden", - "c2sNoPresetSelected": "Keine Vorlage ausgewählt", - "c2sNoPresets": "Keine Voreinstellungen gespeichert", - "c2sLoadPreset": "Laden", - "c2sCurrentLocalConfig": "{{count}} lokale Clienttunnel(s) auf dieser Arbeitsfläche konfiguriert.", - "c2sPresetSyncNote": "Voreinstellung sind explizite Schnappschüsse; das Laden eines Gerätes ersetzt die lokale Liste des Client-Tunnels.", - "c2sPresetSaved": "Voreinstellung für Kliententunnel gespeichert", - "c2sPresetLoaded": "Voreinstellung für Client-Tunnel lokal geladen", - "c2sPresetRenamed": "Voreinstellung für Kliententunnel umbenannt", - "c2sPresetDeleted": "Voreinstellung für Kliententunnel gelöscht", - "c2sPresetLoadError": "Fehler beim Laden der Client-Tunnel Voreinstellungen" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Sprache", - "keyPassword": "keypasswort", - "pastePrivateKey": "Fügen Sie Ihren privaten Schlüssel hier ein...", - "localListenerHost": "127.0.0.1 (lokal lauschen)", - "localTargetHost": "127.0.0.1 (Ziel auf diesem Computer)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Geben Sie Ihr Passwort ein", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "Dashboard", - "loading": "Dashboard wird geladen...", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Unterstützung", + "support": "Support", "discord": "Discord", - "serverOverview": "Serverübersicht", + "docs": "Docs", + "serverOverview": "Server Overview", "version": "Version", - "upToDate": "Aktuell", - "updateAvailable": "Update verfügbar", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Laufzeit", - "database": "Datenbank", - "healthy": "Gesund", - "error": "Fehler", - "totalHosts": "Hosts gesamt", - "totalTunnels": "Gesamte Tunnel", - "totalCredentials": "Gesamte Zugangsdaten", - "recentActivity": "Letzte Aktivität", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Letzte Aktivität wird geladen...", - "noRecentActivity": "Keine aktuellen Aktivitäten", - "quickActions": "Schnelle Aktionen", - "addHost": "Host hinzufügen", - "addCredential": "Zugangsdaten hinzufügen", - "adminSettings": "Admin-Einstellungen", - "userProfile": "Benutzerprofil", - "serverStats": "Serverstatistik", - "loadingServerStats": "Serverstatistiken werden geladen...", - "noServerData": "Keine Serverdaten verfügbar", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Dashboard anpassen", - "dashboardSettings": "Dashboard-Einstellungen", - "enableDisableCards": "Karten aktivieren/deaktivieren", - "resetLayout": "Auf Standard zurücksetzen", - "serverOverviewCard": "Serverübersicht", - "recentActivityCard": "Letzte Aktivität", - "networkGraphCard": "Netzwerkgrafik", - "networkGraph": "Netzwerkgrafik", - "quickActionsCard": "Schnelle Aktionen", - "serverStatsCard": "Serverstatistik", - "panelMain": "Haupt", - "panelSide": "Seite", - "justNow": "gerade jetzt" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { "stable": "STABLE", - "hostsOnline": "Hosts online", - "activeTunnels": "Aktive Tunnel", - "registerNewServer": "Neuen Server registrieren", - "storeSshKeysOrPasswords": "SSH-Schlüssel oder Passwörter speichern", - "manageUsersAndRoles": "Benutzer und Rollen verwalten", - "manageYourAccount": "Verwalten Sie Ihr Konto", - "hostStatus": "Host-Status", - "noHostsConfigured": "Keine Hosts konfiguriert", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Hinzufügen:", - "commandPalette": "Kommandopalette", - "done": "Fertig", - "editModeInstructions": "Ziehen Sie Karten um neu zu sortieren · Ziehen Sie den Spaltentrenner, um Spalten zu vergrößern · Ziehen Sie die untere Kante einer Karte um die Größe der Karte zu ändern · Papierkorb zum Entfernen", - "empty": "Leer", - "clear": "Leeren" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Host hinzufügen", - "addGroup": "Neue Gruppe", - "addLink": "Link hinzufügen", - "zoomIn": "Vergrößern", - "zoomOut": "Verkleinern", - "resetView": "Ansicht zurücksetzen", - "selectHost": "Host auswählen", - "chooseHost": "Wähle einen Host...", - "parentGroup": "Elterngruppe", - "noGroup": "Keine Gruppe", - "groupName": "Gruppenname", - "color": "Farbe", - "source": "Quelle", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "In Gruppe verschieben", - "selectGroup": "Gruppe auswählen...", - "addConnection": "Verbindung hinzufügen", - "hostDetails": "Host-Details", - "removeFromGroup": "Aus Gruppe entfernen", - "addHostHere": "Host hier hinzufügen", - "editGroup": "Gruppe bearbeiten", - "delete": "Löschen", - "add": "Neu", - "create": "Anlegen", - "move": "Bewegen", - "connect": "Verbinden", - "createGroup": "Gruppe erstellen", - "selectSourcePlaceholder": "Quelle auswählen...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Ungültige Datei", - "hostAlreadyExists": "Host ist bereits in der Topologie", - "connectionExists": "Verbindung existiert bereits", - "unknown": "Unbekannt", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", "name": "Name", "ip": "IP", "status": "Status", - "failedToAddNode": "Knoten konnte nicht hinzugefügt werden", - "sourceDifferentFromTarget": "Quelle und Ziel müssen unterschiedlich sein", - "exportJSON": "JSON exportieren", - "importJSON": "JSON importieren", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Datei-Manager", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Serverstatistik", - "noNodes": "Noch keine Knoten" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker ist für diesen Host nicht aktiviert", - "validating": "Docker wird überprüft...", - "connecting": "Verbinden...", - "error": "Fehler", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Verbindung zum Docker fehlgeschlagen", - "containerStarted": "Container {{name}} gestartet", - "failedToStartContainer": "Fehler beim Starten des Containers {{name}}", - "containerStopped": "Container {{name}} gestoppt", - "failedToStopContainer": "Fehler beim Beenden des Containers {{name}}", - "containerRestarted": "Container {{name}} neu gestartet", - "failedToRestartContainer": "Fehler beim Neustart des Containers {{name}}", - "containerPaused": "Container {{name}} pausiert", - "containerUnpaused": "Container {{name}} nicht pausiert", - "failedToTogglePauseContainer": "Fehler beim Umschalten der Pause für Container {{name}}", - "containerRemoved": "Container {{name}} entfernt", - "failedToRemoveContainer": "Fehler beim Entfernen des Containers {{name}}", - "image": "Bild", - "ports": "Häfen", - "noPorts": "Keine Ports", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", "start": "Start", - "confirmRemoveContainer": "Sind Sie sicher, dass Sie den Container '{{name}}' entfernen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", - "runningContainerWarning": "Warnung: Dieser Container wird gerade ausgeführt. Das Entfernen wird den Container zuerst stoppen.", - "loadingContainers": "Container werden geladen...", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", "manager": "Docker Manager", - "autoRefresh": "Auto-Aktualisierung", - "timestamps": "Zeitstempel", - "lines": "Linien", - "filterLogs": "Protokolle filtern...", - "refresh": "Aktualisieren", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", "download": "Download", - "clear": "Leeren", - "logsDownloaded": "Logs erfolgreich heruntergeladen", - "last50": "Letzten 50", - "last100": "Letzten 100", - "last500": "Letzten 500", - "last1000": "Letzten 1000", - "allLogs": "Alle Logs", - "noLogsMatching": "Keine Logs passend zu \"{{query}}\"", - "noLogsAvailable": "Keine Logs verfügbar", - "noContainersFound": "Keine Container gefunden", - "noContainersFoundHint": "Keine Docker-Container auf diesem Host verfügbar", - "searchPlaceholder": "Container suchen...", - "allStatuses": "Alle Status", - "stateRunning": "Laufend", - "statePaused": "Pausiert", - "stateExited": "Beendet", - "stateRestarting": "Neustart", - "noContainersMatchFilters": "Keine Container entsprechen Ihren Filtern", - "noContainersMatchFiltersHint": "Versuchen Sie, Ihre Such- oder Filterkriterien anzupassen", - "failedToFetchStats": "Fehler beim Abrufen der Containerstatistik", - "containerNotRunning": "Container läuft nicht", - "startContainerToViewStats": "Container starten, um Statistiken anzuzeigen", - "loadingStats": "Lade Statistik...", - "errorLoadingStats": "Fehler beim Laden der Statistiken", - "noStatsAvailable": "Keine Statistiken verfügbar", - "cpuUsage": "CPU Auslastung", - "current": "Aktuell", - "memoryUsage": "Speichernutzung", - "networkIo": "Netzwerk-I/O", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Ausgang", - "blockIo": "I/O blockieren", - "read": "Lesen", - "write": "Schreiben", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "Container-Informationen", + "containerInformation": "Container Information", "name": "Name", "id": "ID", - "state": "Bundesland", - "containerMustBeRunning": "Container muss ausgeführt werden, um auf die Konsole zuzugreifen", - "verificationCodePrompt": "Bestätigungscode eingeben", - "totpVerificationFailed": "TOTP-Verifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.", - "warpgateVerificationFailed": "Warpgate-Authentifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.", - "connectedTo": "Mit {{containerName}} verbunden", - "disconnected": "Verbindung getrennt", - "consoleError": "Konsolenfehler", - "errorMessage": "Fehler: {{message}}", - "failedToConnect": "Verbindung zum Container fehlgeschlagen", - "console": "Konsole", - "selectShell": "Shell auswählen", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "l", - "ash": "Asche", - "connect": "Verbinden", - "disconnect": "Verbindung trennen", - "notConnected": "Nicht verbunden", - "clickToConnect": "Klicken Sie auf Verbinden, um eine Shell-Sitzung zu starten", - "connectingTo": "Verbinde mit {{containerName}}...", - "containerNotFound": "Container nicht gefunden", - "backToList": "Zurück zur Liste", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", "logs": "Logs", - "stats": "Statistik", - "consoleTab": "Konsole", - "startContainerToAccess": "Container starten, um auf die Konsole zuzugreifen" + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Allgemein", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Benutzer", - "sectionSessions": "Sitzungen", - "sectionRoles": "Rollen", - "sectionDatabase": "Datenbank", - "sectionApiKeys": "API-Schlüssel", - "allowRegistration": "Benutzerregistrierung erlauben", - "allowRegistrationDesc": "Neue Benutzer selbst registrieren lassen", - "allowPasswordLogin": "Passwort-Login erlauben", - "allowPasswordLoginDesc": "Benutzername/Passwort Login", - "oidcAutoProvision": "OIDC Auto-Bereitstellung", - "oidcAutoProvisionDesc": "Konten für OIDC-Benutzer automatisch erstellen, auch wenn die Registrierung deaktiviert ist", - "allowPasswordReset": "Passwort zurücksetzen erlauben", - "allowPasswordResetDesc": "Code über Docker Logs zurücksetzen", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", "sessionTimeout": "Session Timeout", - "hours": "std", - "sessionTimeoutRange": "Min. 1 Stunde · Max. 720h", - "monitoringDefaults": "Standard überwachen", - "statusCheck": "Statusprüfung", - "metrics": "Metriken", - "sec": "s", - "logLevel": "Log-Level", - "enableGuacamole": "Guacamole aktivieren", - "enableGuacamoleDesc": "RDP/VNC Remote Desktop", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "OpenID Connect für SSO konfigurieren. Felder mit * sind Pflichtfelder.", - "oidcClientId": "Kunden-ID", - "oidcClientSecret": "Kundengeheimnis", - "oidcAuthUrl": "Autorisierungs-URL", - "oidcIssuerUrl": "Ausgabe-URL", - "oidcTokenUrl": "Token-URL", - "oidcUserIdentifier": "Benutzerbezeichner Pfad", - "oidcDisplayName": "Namenspfad anzeigen", - "oidcScopes": "Bereiche", - "oidcUserinfoUrl": "Userinfo-URL überschreiben", - "oidcAllowedUsers": "Erlaubte Benutzer", - "oidcAllowedUsersDesc": "Eine E-Mail pro Zeile. Leer lassen um alle zu erlauben.", - "removeOidc": "Entfernen", - "usersCount": "{{count}} Benutzer", - "createUser": "Anlegen", - "newRole": "Neue Rolle", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", "roleName": "Name", - "roleDisplayName": "Anzeigename", - "roleDescription": "Beschreibung", - "rolesCount": "{{count}} Rollen", - "createRole": "Anlegen", - "creating": "Erstellen...", - "exportDatabase": "Datenbank exportieren", - "exportDatabaseDesc": "Backup aller Hosts, Anmeldeinformationen und Einstellungen herunterladen", - "export": "Exportieren", - "exporting": "Exportiere...", - "importDatabase": "Datenbank importieren", - "importDatabaseDesc": "Wiederherstellen von einer .sqlite-Sicherungsdatei", - "importDatabaseSelected": "Ausgewählt: {{name}}", - "selectFile": "Datei auswählen", - "changeFile": "Ändern", - "import": "Importieren", - "importing": "Importieren...", - "apiKeysCount": "{{count}} Schlüssel", - "newApiKey": "Neuer API-Schlüssel", - "apiKeyCreatedWarning": "Schlüssel erstellt - kopieren Sie ihn jetzt, er wird nicht mehr angezeigt.", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", "apiKeyName": "Name", - "apiKeyUser": "Benutzer", - "apiKeySelectUser": "Benutzer auswählen...", - "apiKeyExpiresAt": "Gültig bis", - "createKey": "Schlüssel erstellen", - "apiKeyNoExpiry": "Kein Ablaufdatum", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokal", + "authTypeLocal": "Local", "adminStatusAdministrator": "Administrator", - "adminStatusRegularUser": "Normaler Benutzer", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "KUNDE", - "youBadge": "DU", - "sessionsActive": "{{count}} aktiv", - "sessionActive": "Aktiv: {{time}}", - "sessionExpires": "Erweitern: {{time}}", - "revokeAll": "Alle", - "revokeAllSessionsSuccess": "Alle Sitzungen des Benutzers widerrufen", - "revokeAllSessionsFailed": "Konnte Sitzungen nicht widerrufen", - "revokeSessionFailed": "Sitzung konnte nicht widerrufen werden", - "addRole": "Rolle hinzufügen", - "noCustomRoles": "Keine benutzerdefinierten Rollen definiert", - "removeRoleFailed": "Fehler beim Entfernen der Rolle", - "assignRoleFailed": "Fehler beim Zuweisen der Rolle", - "deleteRoleFailed": "Fehler beim Löschen der Rolle", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Voller Zugriff auf alle Admin-Einstellungen", - "userRoles": "Rollen", - "revokeAllUserSessions": "Alle Sitzungen widerrufen", - "revokeAllUserSessionsDesc": "Erneute Anmeldung auf allen Geräten erzwingen", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Löschen dieses Benutzers ist dauerhaft.", - "deleteUser": "{{username}} löschen", - "deleting": "Löschen...", - "deleteUserFailed": "Fehler beim Löschen des Benutzers", - "deleteUserSuccess": "Benutzer \"{{username}}\" gelöscht", - "deleteRoleSuccess": "Rolle \"{{name}}\" gelöscht", - "revokeKeySuccess": "Schlüssel \"{{name}}\" widerrufen", - "revokeKeyFailed": "Fehler beim Entfernen des Schlüssels", - "copiedToClipboard": "In Zwischenablage kopiert", - "done": "Fertig", - "createUserTitle": "Benutzer erstellen", - "createUserDesc": "Erstellen Sie ein neues lokales Konto.", - "createUserUsername": "Benutzername", - "createUserPassword": "Passwort", - "createUserPasswordHint": "Mindestens 6 Zeichen.", - "createUserEnterUsername": "Benutzernamen eingeben", - "createUserEnterPassword": "Passwort eingeben", - "createUserSubmit": "Benutzer erstellen", - "editUserTitle": "Benutzer verwalten: {{username}}", - "editUserDesc": "Rollen, Adminstatus, Sessions und Kontoeinstellungen bearbeiten.", - "editUserUsername": "Benutzername", - "editUserAuthType": "Auth Typ", - "editUserAdminStatus": "Adminstatus", - "editUserUserId": "Benutzer-ID", - "linkAccountTitle": "Verbinde OIDC mit Passwort Konto", - "linkAccountDesc": "Verbinden Sie das OIDC-Konto {{username}} mit einem bestehenden lokalen Konto.", - "linkAccountWarningTitle": "Dies wird:", - "linkAccountEffect1": "Lösche das OIDC-Nur-Konto", - "linkAccountEffect2": "OIDC-Login zum Zielkonto hinzufügen", - "linkAccountEffect3": "OIDC und Passwort Login erlauben", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Geben Sie den Benutzernamen des lokalen Kontos ein, auf den verlinkt werden soll", - "linkAccounts": "Konten verknüpfen", - "linkAccountSuccess": "OIDC-Konto verknüpft mit \"{{username}}\"", - "linkAccountFailed": "Verknüpfung des OIDC-Kontos fehlgeschlagen.", - "linkAccountInProgress": "Verknüpfung...", - "saving": "Speichern...", - "updateRegistrationFailed": "Registrierungseinstellung konnte nicht aktualisiert werden", - "updatePasswordLoginFailed": "Fehler beim Aktualisieren der Passwort-Login-Einstellung", - "updateOidcAutoProvisionFailed": "OIDC Auto-Bereitstellungs-Einstellung konnte nicht aktualisiert werden", - "updatePasswordResetFailed": "Fehler beim Aktualisieren der Passwort-Reset-Einstellung", - "sessionTimeoutRange2": "Sitzungs-Timeout muss zwischen 1 und 720 Stunden liegen", - "sessionTimeoutSaved": "Sitzungs-Timeout gespeichert", - "sessionTimeoutSaveFailed": "Speichern der Sitzungszeit fehlgeschlagen", - "monitoringIntervalInvalid": "Ungültige Intervallwerte", - "monitoringSaved": "Überwachungseinstellungen gespeichert", - "monitoringSaveFailed": "Fehler beim Speichern der Überwachungseinstellungen", - "guacamoleSaved": "Guacamole Einstellungen gespeichert", - "guacamoleSaveFailed": "Fehler beim Speichern der Guacamole Einstellungen", - "guacamoleUpdateFailed": "Fehler beim Aktualisieren der Guacamole Einstellung", - "logLevelUpdateFailed": "Fehler beim Aktualisieren der Log-Ebene", - "oidcSaved": "OIDC-Konfiguration gespeichert", - "oidcSaveFailed": "Fehler beim Speichern der OIDC-Konfiguration", - "oidcRemoved": "OIDC-Konfiguration entfernt", - "oidcRemoveFailed": "Fehler beim Entfernen der OIDC-Konfiguration", - "createUserRequired": "Benutzername und Passwort sind erforderlich", - "createUserPasswordTooShort": "Passwort muss mindestens 6 Zeichen lang sein", - "createUserSuccess": "Benutzer \"{{username}}\" erstellt", - "createUserFailed": "Fehler beim Erstellen des Benutzers", - "updateAdminStatusFailed": "Fehler beim Aktualisieren des Adminstatus", - "allSessionsRevoked": "Alle Sitzungen widerrufen", - "revokeSessionsFailed": "Konnte Sitzungen nicht widerrufen", - "createRoleRequired": "Name und Anzeigename sind erforderlich", - "createRoleSuccess": "Rolle \"{{name}}\" erstellt", - "createRoleFailed": "Fehler beim Erstellen der Rolle", - "apiKeyNameRequired": "Schlüsselname ist erforderlich", - "apiKeyUserRequired": "Benutzer-ID ist erforderlich", - "apiKeyCreatedSuccess": "API-Schlüssel \"{{name}}\" erstellt", - "apiKeyCreateFailed": "API-Schlüssel konnte nicht erstellt werden", - "exportSuccess": "Datenbank erfolgreich exportiert", - "exportFailed": "Datenbank-Export fehlgeschlagen", - "importSelectFile": "Bitte wählen Sie zuerst eine Datei", - "importCompleted": "Import abgeschlossen: {{total}} importierte Elemente, {{skipped}} übersprungen", - "importFailed": "Import fehlgeschlagen: {{error}}", - "importError": "Datenbankimport fehlgeschlagen" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { "hostLabel": "Host", - "hostPlaceholder": "192.168.1.1 oder beispiel.com", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Benutzername", - "usernamePlaceholder": "benutzername", - "authLabel": "Audi", - "passwordLabel": "Passwort", - "passwordPlaceholder": "passwort", - "privateKeyLabel": "Privater Schlüssel", - "privateKeyPlaceholder": "Privaten Schlüssel einfügen...", - "credentialLabel": "Anmeldedaten", - "credentialPlaceholder": "Gespeicherte Anmeldeinformationen auswählen", - "connectToTerminal": "Mit Terminal verbinden", - "connectToFiles": "Mit Dateien verbinden" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Kein Terminal ausgewählt", - "noTerminalSelectedHint": "Öffne einen SSH-Terminal-Tab um seine Befehlshistorie zu sehen", - "searchPlaceholder": "Verlauf suchen...", - "clearAll": "Alles löschen", - "noHistoryEntries": "Keine Verlaufseinträge", - "trackingDisabled": "Verlaufsverfolgung deaktiviert", - "trackingDisabledHint": "Aktivieren Sie es in Ihren Profileinstellungen, um Befehle aufzuzeichnen." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Tastenaufnahme", - "recordToTerminals": "In Terminals aufnehmen", - "selectAll": "Alle", - "selectNone": "Keine", - "noTerminalTabsOpen": "Keine Terminal-Tabs geöffnet", - "selectTerminalsAbove": "Wählen Sie oben Terminals", - "broadcastInputPlaceholder": "Tippen Sie hier, um Tastenanschläge zu senden...", - "stopRecording": "Aufnahme stoppen", - "startRecording": "Aufnahme starten", - "settingsTitle": "Einstellungen", - "enableRightClickCopyPaste": "Rechtsklick Kopieren/Einfügen aktivieren" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { "layoutTitle": "Layout", - "selectLayoutAbove": "Wählen Sie ein Layout oben", - "selectLayoutHint": "Anzahl der anzuzeigenden Bereiche auswählen", - "panesTitle": "Scheiben", - "openTabsTitle": "Tabs öffnen", - "dragTabsHint": "Ziehen Sie Registerkarten in die oberen Bereiche oder verwenden Sie die Schnellzuweisung.", - "dropHere": "Hier ablegen", - "emptyPane": "Leer", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", "dashboard": "Dashboard", - "clearSplitScreen": "Split Bildschirm löschen", - "quickAssign": "Schnellzuweisung", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", "alreadyAssigned": "Pane {{index}}", "splitTab": "Split Tab", - "addToSplit": "Zu Split hinzufügen", - "removeFromSplit": "Aus Split entfernen", - "assignToPane": "Dem Bereich zuweisen" + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Snippet erstellen", - "createSnippetDescription": "Erstellen eines neuen Befehlssnippets für eine schnelle Ausführung", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", "nameLabel": "Name", - "namePlaceholder": "z. B. Nginx neu starten", - "descriptionLabel": "Beschreibung", - "descriptionPlaceholder": "Optionale Beschreibung", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", "optional": "Optional", - "folderLabel": "Ordner", - "noFolder": "Kein Ordner (unkategorisiert)", - "commandLabel": "Befehl", - "commandPlaceholder": "z. B. sudo systemctl neustarten nginx", - "cancel": "Abbrechen", - "createSnippetButton": "Snippet erstellen", - "createFolderTitle": "Ordner erstellen", - "createFolderDescription": "Organisieren Sie Ihre Snippets in Ordner", - "folderNameLabel": "Ordnername", - "folderNamePlaceholder": "z. B. Systembefehle, Docker-Skripte", - "folderColorLabel": "Ordnerfarbe", - "folderIconLabel": "Ordnersymbol", - "previewLabel": "Vorschau", - "folderNameFallback": "Ordnername", - "createFolderButton": "Ordner erstellen", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Alle", - "selectNone": "Keine", - "noTerminalTabsOpen": "Keine Terminal-Tabs geöffnet", - "searchPlaceholder": "Suche Snippets...", - "newSnippet": "Neues Schnipsel", - "newFolder": "Neuer Ordner", - "run": "Ausführen", - "noSnippetsInFolder": "Keine Snippets in diesem Ordner", - "uncategorized": "Nicht kategorisiert", - "editSnippetTitle": "Snippet bearbeiten", - "editSnippetDescription": "Diesen Befehlssnippet aktualisieren", - "saveSnippetButton": "Änderungen speichern", - "createSuccess": "Snippet erfolgreich erstellt", - "createFailed": "Fehler beim Erstellen des Snippets", - "updateSuccess": "Snippet erfolgreich aktualisiert", - "updateFailed": "Fehler beim Aktualisieren des Snippets", - "deleteFailed": "Fehler beim Löschen des Snippets", - "folderCreateSuccess": "Ordner erfolgreich erstellt", - "folderCreateFailed": "Fehler beim Erstellen des Ordners", - "editFolderTitle": "Ordner bearbeiten", - "editFolderDescription": "Umbenennen oder ändern Sie das Aussehen dieses Ordners", - "saveFolderButton": "Änderungen speichern", - "editFolder": "Ordner bearbeiten", - "deleteFolder": "Ordner löschen", - "folderDeleteSuccess": "Ordner \"{{name}}\" gelöscht", - "folderDeleteFailed": "Fehler beim Löschen des Ordners", - "folderEditSuccess": "Ordner erfolgreich aktualisiert", - "folderEditFailed": "Fehler beim Aktualisieren des Ordners", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", "confirmRunMessage": "Run \"{{name}}\"?", - "confirmRunButton": "Laufen", - "runSuccess": "\"{{name}}\" in {{count}} Terminal(n) ran", - "copySuccess": "Kopiert \"{{name}}\" in die Zwischenablage", - "shareTitle": "Snippet teilen", - "shareUser": "Benutzer", - "shareRole": "Rolle", - "selectUser": "Benutzer auswählen...", - "selectRole": "Wählen Sie eine Rolle...", - "shareSuccess": "Snippet erfolgreich freigegeben", - "shareFailed": "Snippet konnte nicht geteilt werden", - "revokeSuccess": "Zugriff widerrufen", - "revokeFailed": "Fehler beim Entfernen des Zugriffs", - "currentAccess": "Aktueller Zugriff", - "shareLoadError": "Fehler beim Laden der Freigabedaten", - "loading": "Wird geladen...", - "close": "Schließen", - "reorderFailed": "Fehler beim Speichern der Snippet-Reihenfolge" + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Konto", - "sectionAppearance": "Erscheinung", - "sectionSecurity": "Sicherheit", - "sectionApiKeys": "API-Schlüssel", - "sectionC2sTunnels": "C2S-Tunnel", - "usernameLabel": "Benutzername", - "roleLabel": "Rolle", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", "roleAdministrator": "Administrator", - "authMethodLabel": "Auth Methode", - "authMethodLocal": "Lokal", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "An", - "twoFaOff": "Aus", + "twoFaOn": "On", + "twoFaOff": "Off", "versionLabel": "Version", - "deleteAccount": "Konto löschen", - "deleteAccountDescription": "Lösche dein Konto dauerhaft", - "deleteButton": "Löschen", - "deleteAccountPermanent": "Diese Aktion ist dauerhaft und kann nicht rückgängig gemacht werden.", - "deleteAccountWarning": "Alle Sitzungen, Hosts, Anmeldeinformationen und Einstellungen werden dauerhaft gelöscht.", - "confirmPasswordDeletePlaceholder": "Gib zum Bestätigen dein Passwort ein", - "languageLabel": "Sprache", - "themeLabel": "Thema", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Akzentfarbe", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Befehl Autovervollständigung", - "commandAutocompleteDesc": "Beim Tippen Autovervollständigung anzeigen", - "historyTracking": "Verlaufsverfolgung", - "historyTrackingDesc": "Terminalbefehle verfolgen", - "syntaxHighlighting": "Syntaxhervorhebung", - "syntaxHighlightingDesc": "Terminalausgabe hervorheben", - "commandPalette": "Kommandopalette", - "commandPaletteDesc": "Tastaturkürzel aktivieren", - "reopenTabsOnLogin": "Tabs beim Anmelden wieder öffnen", - "reopenTabsOnLoginDesc": "Ihre geöffneten Tabs werden beim Anmelden oder Aktualisieren der Seite wiederhergestellt, sogar von einem anderen Gerät aus.", - "confirmTabClose": "Tab-Schließen bestätigen", - "confirmTabCloseDesc": "Vor Schließen von Terminal-Tabs fragen", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Host-Tags anzeigen", - "showHostTagsDesc": "Tags in der Hostliste anzeigen", - "hostTrayOnClick": "Klicken Sie hier, um die Host-Aktionen zu erweitern.", - "hostTrayOnClickDesc": "Verbindungsschaltflächen immer anzeigen; zum Erweitern der Verwaltungsoptionen klicken statt mit der Maus darüberfahren", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Die linke Seitenleiste der App-Bereiche sollte immer ausgeklappt sein, anstatt sich erst beim Überfahren mit der Maus zu erweitern.", - "settingsSnippets": "Schnipsel", - "foldersCollapsed": "Ordner zusammenklappt", - "foldersCollapsedDesc": "Ordner standardmäßig einklappen", - "confirmExecution": "Ausführung bestätigen", - "confirmExecutionDesc": "Vor dem Ausführen von Snippets bestätigen", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", "settingsUpdates": "Updates", - "disableUpdateChecks": "Updateprüfungen deaktivieren", - "disableUpdateChecksDesc": "Suche nach Updates stoppen", - "totpAuthenticator": "TOTP-Authenticator", - "totpEnabled": "2FA ist aktiviert", - "totpDisabled": "Zusätzliche Login-Sicherheit hinzufügen", - "disable": "Deaktivieren", - "enable": "Aktivieren", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scanne QR-Code oder gib Geheimnis in deiner Authentifizierungs-App ein und gib dann den 6-stelligen Code ein", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Überprüfen", - "changePassword": "Passwort ändern", - "currentPasswordLabel": "Aktuelles Passwort", - "currentPasswordPlaceholder": "Aktuelles Passwort", - "newPasswordLabel": "Neues Passwort", - "newPasswordPlaceholder": "Neues Passwort", - "confirmPasswordLabel": "Neues Passwort bestätigen", - "confirmPasswordPlaceholder": "Neues Passwort bestätigen", - "updatePassword": "Passwort aktualisieren", - "createApiKeyTitle": "API-Schlüssel erstellen", - "createApiKeyDescription": "Generieren Sie einen neuen API-Schlüssel für den programmatischen Zugriff.", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", "optional": "optional", - "cancel": "Abbrechen", - "createKey": "Schlüssel erstellen", - "apiKeyCount": "{{count}} Schlüssel", - "newKey": "Neuer Schlüssel", - "noApiKeys": "Noch keine API-Schlüssel.", - "apiKeyActive": "Aktiv", - "apiKeyUsageHint": "Fügen Sie Ihren Schlüssel in die", - "apiKeyUsageHintHeader": "kopf.", - "apiKeyPermissionsHint": "Schlüssel erben die Berechtigungen des erzeugenden Benutzers.", - "roleUser": "Benutzer", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Fehler beim Starten der TOTP-Einrichtung", - "totpEnter6Digits": "Geben Sie einen 6-stelligen Code ein", - "totpEnabledSuccess": "Zwei-Faktor-Authentifizierung aktiviert", - "totpInvalidCode": "Ungültiger Code, bitte versuchen Sie es erneut", - "totpDisableInputRequired": "TOTP-Code oder Passwort eingeben", - "totpDisabledSuccess": "Zwei-Faktor-Authentifizierung deaktiviert", - "totpDisableFailed": "Fehler beim Deaktivieren von 2FA", - "totpDisableTitle": "2FA deaktivieren", - "totpDisablePlaceholder": "TOTP-Code oder Passwort eingeben", - "totpDisableConfirm": "2FA deaktivieren", - "totpContinueVerify": "Weiter zur Verifizierung", - "totpVerifyTitle": "Code überprüfen", - "totpBackupTitle": "Backup-Codes", - "totpDownloadBackup": "Backup-Codes herunterladen", - "done": "Fertig", - "secretCopied": "Geheimnis in die Zwischenablage kopiert", - "apiKeyNameRequired": "Schlüsselname ist erforderlich", - "apiKeyCreated": "API-Schlüssel \"{{name}}\" erstellt", - "apiKeyCreateFailed": "API-Schlüssel konnte nicht erstellt werden", - "apiKeyUser": "Benutzer", - "apiKeyExpires": "Gültig bis", - "apiKeyRevoked": "API-Schlüssel \"{{name}}\" widerrufen", - "apiKeyRevokeFailed": "API-Schlüssel konnte nicht widerrufen", - "passwordFieldsRequired": "Aktuelle und neue Passwörter sind erforderlich", - "passwordMismatch": "Passwörter stimmen nicht überein", - "passwordTooShort": "Passwort muss mindestens 6 Zeichen lang sein", - "passwordUpdated": "Passwort erfolgreich aktualisiert", - "passwordUpdateFailed": "Fehler beim Aktualisieren des Passworts", - "deletePasswordRequired": "Passwort wird benötigt, um Ihr Konto zu löschen", - "deleteFailed": "Konto konnte nicht gelöscht werden", - "deleting": "Löschen...", - "colorPickerTooltip": "Farbauswahl öffnen", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", "themeSystem": "System", - "themeLight": "Hell", - "themeDark": "Dunkel", - "themeDracula": "Gabriel", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarisiert", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Ein Dunkler", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/el_GR.json b/src/ui/locales/translated/el_GR.json index 45f8e691..9bc5af32 100644 --- a/src/ui/locales/translated/el_GR.json +++ b/src/ui/locales/translated/el_GR.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Φάκελοι", - "folder": "Φάκελος", - "password": "Κωδικός", - "key": "Κλειδί", - "sshPrivateKey": "Ιδιωτικό Κλειδί SSH", - "upload": "Ανέβασμα", - "keyPassword": "Κωδικός Πρόσβασης Κλειδιού", - "sshKey": "SSH Κλειδί", - "uploadPrivateKeyFile": "Ανεβάστε Αρχείο Ιδιωτικού Κλειδιού", - "searchCredentials": "Αναζήτηση διαπιστευτηρίων...", - "addCredential": "Προσθήκη Διαπιστευτηρίου", - "caCertificate": "Πιστοποιητικό CA (-cert.pub)", - "caCertificateDescription": "Προαιρετικό: Ανεβάστε ή επικολλήστε το αρχείο πιστοποιητικού υπογεγραμμένο CA (π.χ. id_ed25519-cert.pub). Απαιτείται όταν ο διακομιστής SSH σας χρησιμοποιεί εξουσιοδότηση βασισμένη σε πιστοποιητικά.", - "uploadCertFile": "Μεταφόρτωση Αρχείου -cert.pub", - "clearCert": "Εκκαθάριση", - "certLoaded": "Το πιστοποιητικό φορτώθηκε", - "certPublicKeyLabel": "Πιστοποιητικό CA", - "certTypeLabel": "Τύπος πιστοποιητικού", - "pasteOrUploadCert": "Επικόλληση ή μεταφόρτωση πιστοποιητικού -cert.pub...", - "hasCaCert": "Έχει Πιστοποιητικό CA", - "noCaCert": "Χωρίς Πιστοποιητικό Ca" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Αποτυχία φόρτωσης ειδοποιήσεων", - "failedToDismissAlert": "Αποτυχία απόρριψης ειδοποίησης" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Ρύθμιση Παραμέτρων Διακομιστή", - "description": "Ρυθμίστε τη διεύθυνση URL του διακομιστή Termix για να συνδεθείτε με τις υπηρεσίες του συστήματος υποστήριξης", - "serverUrl": "Url Διακομιστή", - "enterServerUrl": "Παρακαλώ εισάγετε μια διεύθυνση URL διακομιστή", - "saveFailed": "Αποτυχία αποθήκευσης των ρυθμίσεων", - "saveError": "Σφάλμα αποθήκευσης ρυθμίσεων", - "saving": "Αποθηκεύεται...", - "saveConfig": "Αποθήκευση Ρυθμίσεων", - "helpText": "Εισάγετε τη διεύθυνση URL όπου εκτελείται ο διακομιστής Termix (π.χ., http://localhost:30001 ή https://your-server.com)", - "changeServer": "Αλλαγή Διακομιστή", - "mustIncludeProtocol": "Η διεύθυνση URL του διακομιστή πρέπει να ξεκινά με http:// ή http://", - "allowInvalidCertificate": "Να επιτρέπεται το μη έγκυρο πιστοποιητικό", - "allowInvalidCertificateDesc": "Χρησιμοποιήστε μόνο για αξιόπιστους αυτο-φιλοξενούμενους διακομιστές με αυτο-υπογεγραμμένα πιστοποιητικά ή πιστοποιητικά διεύθυνσης IP.", - "useEmbedded": "Χρήση Τοπικού Διακομιστή", - "embeddedDesc": "Εκτέλεση Termix με τον ενσωματωμένο τοπικό διακομιστή (δεν απαιτείται απομακρυσμένος διακομιστής)", - "embeddedConnecting": "Σύνδεση στον τοπικό διακομιστή...", - "embeddedNotReady": "Ο τοπικός διακομιστής δεν είναι έτοιμος ακόμα. Παρακαλώ περιμένετε λίγο και προσπαθήστε ξανά.", - "localServer": "Τοπικός Διακομιστής" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Σφάλμα Ελέγχου Έκδοσης", - "checkFailed": "Αποτυχία ελέγχου για ενημερώσεις", - "upToDate": "Η εφαρμογή είναι μέχρι την ημερομηνία", - "currentVersion": "Εκτελείτε την έκδοση {{version}}", - "updateAvailable": "Διαθέσιμη Ενημέρωση", - "newVersionAvailable": "Μια νέα έκδοση είναι διαθέσιμη! Εκτελείτε {{current}}αλλά το {{latest}} είναι διαθέσιμο.", - "betaVersion": "Έκδοση Beta", - "betaVersionDesc": "Τρέχετε {{current}}, το οποίο είναι νεότερο από την τελευταία σταθερή κυκλοφορία {{latest}}.", - "releasedOn": "Κυκλοφόρησε στο {{date}}", - "downloadUpdate": "Λήψη Ενημέρωσης", - "checking": "Έλεγχος για ενημερώσεις...", - "checkUpdates": "Έλεγχος για ενημερώσεις", - "checkingUpdates": "Έλεγχος για ενημερώσεις...", - "updateRequired": "Απαιτείται Ενημέρωση" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Κλείσιμο", + "close": "Close", "minimize": "Minimize", - "online": "Συνδεδεμένος", - "offline": "Αποσυνδεδεμένος", - "continue": "Συνέχεια", - "maintenance": "Συντήρηση", - "degraded": "Μειώθηκε", - "error": "Σφάλμα", - "warning": "Προειδοποίηση", - "unsavedChanges": "Μη αποθηκευμένες αλλαγές", - "dismiss": "Απόρριψη", - "loading": "Φόρτωση...", - "optional": "Προαιρετικό", - "connect": "Σύνδεση", - "copied": "Αντιγράφηκε", - "connecting": "Σύνδεση...", - "updateAvailable": "Διαθέσιμη Ενημέρωση", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Άνοιγμα σε νέα καρτέλα", - "noReleases": "Καμία Κυκλοφορία", - "updatesAndReleases": "Ενημερώσεις & Εκδόσεις", - "newVersionAvailable": "Μια νέα έκδοση ({{version}}) είναι διαθέσιμη.", - "failedToFetchUpdateInfo": "Αποτυχία λήψης πληροφοριών ενημέρωσης", - "preRelease": "Προ-έκδοση", - "noReleasesFound": "Δεν βρέθηκαν εκδόσεις.", - "cancel": "Ακύρωση", - "username": "Όνομα Χρήστη", - "login": "Είσοδος", - "register": "Εγγραφή", - "password": "Κωδικός", - "confirmPassword": "Επιβεβαίωση Κωδικού Πρόσβασης", - "back": "Πίσω", - "save": "Αποθήκευση", - "saving": "Αποθηκεύεται...", - "delete": "Διαγραφή", - "rename": "Μετονομασία", - "edit": "Επεξεργασία", - "add": "Προσθήκη", - "confirm": "Επιβεβαίωση", - "no": "Όχι", - "or": "Ή", - "next": "Επόμενο", - "previous": "Προηγούμενο", - "refresh": "Ανανέωση", - "language": "Γλώσσα", - "checking": "Έλεγχος...", - "checkingDatabase": "Έλεγχος σύνδεσης βάσης δεδομένων...", - "checkingAuthentication": "Έλεγχος ταυτοποίησης...", - "backendReconnected": "Έγινε επαναφορά σύνδεσης διακομιστή", - "connectionDegraded": "Η σύνδεση διακομιστή χάθηκε, ανάκτηση…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Αφαίρεση", - "create": "Δημιουργία", - "update": "Ενημέρωση", - "copy": "Αντιγραφή", - "copyFailed": "Αποτυχία αντιγραφής στο πρόχειρο", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Επαναφορά", - "of": "από" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Αρχική", - "terminal": "Τερματικό", - "docker": "Προσάρτηση", - "tunnels": "Σηράγγες", - "fileManager": "Διαχειριστής Αρχείων", - "serverStats": "Στατιστικά Διακομιστή", - "admin": "Διαχειριστής", - "userProfile": "Προφίλ Χρήστη", - "splitScreen": "Διαίρεση Οθόνης", - "confirmClose": "Κλείσιμο αυτής της ενεργής συνεδρίας?", - "close": "Κλείσιμο", - "cancel": "Ακύρωση", - "sshManager": "Διαχειριστής SSH", - "cannotSplitTab": "Αδυναμία διαχωρισμού αυτής της καρτέλας", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Αντιγραφή Κωδικού Πρόσβασης", - "copySudoPassword": "Αντιγραφή Κωδικού Πρόσβασης Sudo", - "passwordCopied": "Ο κωδικός αντιγράφηκε στο πρόχειρο", - "noPasswordAvailable": "Δεν υπάρχει διαθέσιμος κωδικός πρόσβασης", - "failedToCopyPassword": "Αποτυχία αντιγραφής κωδικού πρόσβασης", - "refreshTab": "Ανανέωση σύνδεσης", - "openFileManager": "Άνοιγμα Διαχειριστή Αρχείων", - "dashboard": "Ταμπλό", - "networkGraph": "Γράφημα Δικτύου", - "quickConnect": "Γρήγορη Σύνδεση", - "sshTools": "Εργαλεία SSH", - "history": "Ιστορικό", - "hosts": "Υπολογιστές", - "snippets": "Δείγματα", - "hostManager": "Διαχειριστής Υπολογιστών", - "credentials": "Διαπιστευτήρια", - "connections": "Συνδέσεις", - "roleAdministrator": "Διαχειριστής", - "roleUser": "Χρήστης" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Υπολογιστές", - "noHosts": "Δεν Υπάρχουν Υπολογιστές SSH", - "retry": "Επανάληψη", - "refresh": "Ανανέωση", - "optional": "Προαιρετικό", - "downloadSample": "Λήψη Δείγματος", - "failedToDeleteHost": "Αποτυχία διαγραφής {{name}}", - "importSkipExisting": "Εισαγωγή (παράλειψη υπάρχει)", - "connectionDetails": "Λεπτομέρειες Σύνδεσης", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Απομακρυσμένη Επιφάνεια Εργασίας", - "port": "Θύρα", - "username": "Όνομα Χρήστη", - "folder": "Φάκελος", - "tags": "Ετικέτες", - "pin": "Καρφίτσα", - "addHost": "Προσθήκη Διακομιστή", - "editHost": "Επεξεργασία Υπολογιστή", - "cloneHost": "Κλωνοποίηση Υπολογιστή", - "enableTerminal": "Ενεργοποίηση Τερματικού", - "enableTunnel": "Ενεργοποίηση Tunnel", - "enableFileManager": "Ενεργοποίηση Διαχειριστή Αρχείων", - "enableDocker": "Ενεργοποίηση Docker", - "defaultPath": "Προεπιλεγμένη Διαδρομή", - "connection": "Σύνδεση", - "upload": "Ανέβασμα", - "authentication": "Ταυτοποίηση", - "password": "Κωδικός", - "key": "Κλειδί", - "credential": "Διαπιστευτήριο", - "none": "Κανένα", - "sshPrivateKey": "Ιδιωτικό Κλειδί SSH", - "keyType": "Τύπος Κλειδιού", - "uploadFile": "Μεταφόρτωση Αρχείου", - "tabGeneral": "Γενικά", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Σηράγγες", - "tabDocker": "Προσάρτηση", - "tabFiles": "Αρχεία", - "tabStats": "Στατιστικά", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Κοινή χρήση", - "tabAuthentication": "Ταυτοποίηση", - "terminal": "Τερματικό", - "tunnel": "Σήραγγα", - "fileManager": "Διαχειριστής Αρχείων", - "serverStats": "Στατιστικά Διακομιστή", - "status": "Κατάσταση", - "folderRenamed": "Φάκελος \"{{oldName}}\" μετονομάστηκε σε \"{{newName}}\" επιτυχώς", - "failedToRenameFolder": "Αποτυχία μετονομασίας φακέλου", - "movedToFolder": "Μετακινήθηκε στο \"{{folder}}\"", - "editHostTooltip": "Επεξεργασία κεντρικού υπολογιστή", - "statusChecks": "Έλεγχοι Κατάστασης", - "metricsCollection": "Συλλογή Μετρικών", - "metricsInterval": "Διάστημα Συλλογής Μετρήσεων", - "metricsIntervalDesc": "Πόσο συχνά συλλέγουν στατιστικά στοιχεία διακομιστή (5s - 1h)", - "behavior": "Συμπεριφορά", - "themePreview": "Προεπισκόπηση Θέματος", - "theme": "Θέμα", - "fontFamily": "Οικογένεια Γραμματοσειρών", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Διάστημα Γραμμάτων", - "lineHeight": "Ύψος Γραμμής", - "cursorStyle": "Στυλ Δρομέα", - "cursorBlink": "Αναβόσβημα Δρομέα", - "scrollbackBuffer": "Προσωρινή Μνήμη Κύλισης", - "bellStyle": "Στυλ Κουδουνιού", - "rightClickSelectsWord": "Δεξί Κλικ Επιλέγει Λέξη", - "fastScrollModifier": "Τροποποιητής Γρήγορης Κύλισης", - "fastScrollSensitivity": "Ευαισθησία Γρήγορης Κύλισης", - "sshAgentForwarding": "Προώθηση SSH Πράκτορα", - "backspaceMode": "Λειτουργία Backspace", - "startupSnippet": "Δείγμα Εκκίνησης", - "selectSnippet": "Επιλογή αποσπάσματος", - "forceKeyboardInteractive": "Εξαναγκασμός Keyboard-Interactive", - "overrideCredentialUsername": "Παράκαμψη Ονόματος Διαπιστευτηρίου Χρήστη", - "overrideCredentialUsernameDesc": "Χρησιμοποιήστε το όνομα χρήστη που καθορίστηκε παραπάνω αντί για το όνομα χρήστη του διαπιστευτηρίου", - "jumpHostChain": "Αλυσίδα Άλματος", - "portKnocking": "Λιμάνι Knocking", - "addKnock": "Προσθήκη Θύρας", - "addProxyNode": "Προσθήκη Κόμβου", - "proxyNode": "Κόμβος Διαμεσολαβητή", - "proxyType": "Τύπος Διαμεσολαβητή", - "quickActions": "Γρήγορες Ενέργειες", - "sudoPasswordAutoFill": "Αυτόματη Συμπλήρωση Κωδικού Πρόσβασης Sudo", - "sudoPassword": "Κωδικός Πρόσβασης Sudo", - "keepaliveInterval": "Διάστημα Διατροφής (ms)", - "moshCommand": "Διοίκηση MOSH", - "environmentVariables": "Μεταβλητές Περιβάλλοντος", - "addVariable": "Προσθήκη Μεταβλητής", - "docker": "Προσάρτηση", - "copyTerminalUrl": "Αντιγραφή URL Τερματικού", - "copyFileManagerUrl": "Αντιγραφή URL Διαχειριστή Αρχείων", - "copyRemoteDesktopUrl": "Αντιγραφή URL Απομακρυσμένης Επιφάνειας Εργασίας", - "failedToConnect": "Αποτυχία σύνδεσης στην κονσόλα", - "connect": "Σύνδεση", - "disconnect": "Αποσύνδεση", - "start": "Έναρξη", - "enableStatusCheck": "Ενεργοποίηση Ελέγχου Κατάστασης", - "enableMetrics": "Ενεργοποίηση Μετρήσεων", - "bulkUpdateFailed": "Η μαζική ενημέρωση απέτυχε", - "selectAll": "Επιλογή Όλων", - "deselectAll": "Αποεπιλογή Όλων", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Ασφαλές Κέλυφος", - "virtualNetwork": "Εικονικό Δίκτυο", - "unencryptedShell": "Μη κρυπτογραφημένο κέλυφος", - "addressIp": "Διεύθυνση / IP", - "friendlyName": "Φιλικό Όνομα", - "folderAndAdvanced": "& Προχωρημένος Φάκελος", - "privateNotes": "Ιδιωτικές Σημειώσεις", - "privateNotesPlaceholder": "Λεπτομέρειες σχετικά με αυτόν τον διακομιστή...", - "pinToTop": "Καρφίτσωμα στην κορυφή", - "pinToTopDesc": "Να εμφανίζεται πάντα αυτός ο κεντρικός υπολογιστής στην κορυφή της λίστας", - "portKnockingSequence": "Ακολουθία Του Port Knocking", - "addKnockBtn": "Προσθήκη Κτυπήματος", - "noPortKnocking": "Δεν έχει ρυθμιστεί καμία θύρα.", - "knockPort": "Θύρα Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Καθυστέρηση Μετά (ms)", - "useSocks5Proxy": "Χρήση Διακομιστή Μεσολάβησης SOCKS5", - "useSocks5ProxyDesc": "Σύνδεση διαδρομής μέσω διακομιστή μεσολάβησης", - "proxyHost": "Διακομιστής Μεσολάβησης", - "proxyPort": "Θύρα Διαμεσολαβητή", - "proxyUsername": "Όνομα Χρήστη Διακομιστή Μεσολάβησης", - "proxyPassword": "Κωδικός Διακομιστή Μεσολάβησης", - "proxySingleMode": "Μονός Διακομιστής Μεσολάβησης", - "proxyChainMode": "Αλυσίδα Διαμεσολαβητή", - "you": "Εσείς", - "jumpHostChainLabel": "Αλυσίδα Άλματος", - "addJumpBtn": "Προσθήκη Άλματος", - "noJumpHosts": "Δεν έχει ρυθμιστεί κανένας υπολογιστής άλματος.", - "selectAServer": "Επιλέξτε έναν διακομιστή...", - "sshPort": "Θύρα SSH", - "authMethod": "Μέθοδος Πιστοποίησης", - "storedCredential": "Αποθηκευμένα Διαπιστευτήρια", - "selectACredential": "Επιλέξτε ένα πιστοποιητικό...", - "keyTypeLabel": "Τύπος Κλειδιού", - "keyTypeAuto": "Αυτόματη Ανίχνευση", - "keyPasteTab": "Επικόλληση", - "keyUploadTab": "Ανέβασμα", - "keyFileLoaded": "Το αρχείο κλειδί φορτώθηκε", - "keyUploadClick": "Κάντε κλικ για να ανεβάσετε .pem / .key / .ppk", - "clearKey": "Καθαρισμός κλειδιού", - "keySaved": "SSH κλειδί αποθηκεύτηκε", - "keyReplaceNotice": "επικολλήστε ένα νέο κλειδί κάτω για να το αντικαταστήσετε", - "keyPassphraseSaved": "Η φράση πρόσβασης αποθηκεύτηκε, πληκτρολογήστε για αλλαγή", - "replaceKey": "Αντικατάσταση κλειδιού", - "forceKeyboardInteractiveLabel": "Εξαναγκασμός Πληκτρολογίου Διαδραστικού", - "forceKeyboardInteractiveShortDesc": "Εξαναγκασμός καταχώρησης χειροκίνητου κωδικού πρόσβασης ακόμη και αν υπάρχουν κλειδιά", - "terminalAppearance": "Εμφάνιση Τερματικού", - "colorTheme": "Χρώμα Θέματος", - "fontFamilyLabel": "Οικογένεια Γραμματοσειρών", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Στυλ Δρομέα", - "letterSpacingPx": "Διάστημα Γραμμάτων (px)", - "lineHeightLabel": "Ύψος Γραμμής", - "bellStyleLabel": "Στυλ Κουδουνιού", - "backspaceModeLabel": "Λειτουργία Backspace", - "cursorBlinking": "Αναβόσβημα Δρομέα", - "cursorBlinkingDesc": "Ενεργοποίηση εφέ αναβοσβήσματος για τον κέρσορα τερματικού", - "rightClickSelectsWordLabel": "Κάντε δεξί κλικ Επιλογή Λέξης", - "rightClickSelectsWordShortDesc": "Επιλέξτε τη λέξη κάτω από το δρομέα με δεξί κλικ", - "behaviorAndAdvanced": "Συμπεριφορά & Προχωρημένη", - "scrollbackBufferLabel": "Προσωρινή Μνήμη Κύλισης", - "scrollbackMaxLines": "Μέγιστος αριθμός γραμμών που διατηρούνται στο ιστορικό", - "sshAgentForwardingLabel": "Προώθηση SSH Πράκτορα", - "sshAgentForwardingShortDesc": "Περάστε τα τοπικά SSH κλειδιά σας σε αυτόν τον υπολογιστή", - "enableAutoMosh": "Ενεργοποίηση Αυτόματη-Mosh", - "enableAutoMoshDesc": "Προτίμηση του Mosh πάνω από SSH εάν είναι διαθέσιμο", - "enableAutoTmux": "Ενεργοποίηση Auto-Tmux", - "enableAutoTmuxDesc": "Αυτόματη εκκίνηση ή επισύναψη σε συνεδρία tmux", - "sudoPasswordAutoFillLabel": "Αυτόματος Γέμισμα Κωδικού Πρόσβασης Sudo", - "sudoPasswordAutoFillShortDesc": "Αυτόματη παροχή sudo κωδικού πρόσβασης όταν σας ζητηθεί", - "sudoPasswordLabel": "Κωδικός Πρόσβασης Sudo", - "environmentVariablesLabel": "Μεταβλητές Περιβάλλοντος", - "addVariableBtn": "Προσθήκη Μεταβλητής", - "noEnvVars": "Δεν έχουν ρυθμιστεί μεταβλητές περιβάλλοντος.", - "fastScrollModifierLabel": "Τροποποιητής Γρήγορης Κύλισης", - "fastScrollSensitivityLabel": "Ευαισθησία Γρήγορης Κύλισης", - "moshCommandLabel": "Εντολή Mosh", - "startupSnippetLabel": "Δείγμα Εκκίνησης", - "keepaliveIntervalLabel": "Διάστημα διατήρησης ζωντανής λειτουργίας (δευτερόλεπτα)", - "maxKeepaliveMisses": "Μέγιστες Αστοχίες Keepalive", - "tunnelSettings": "Ρυθμίσεις Tunnel", - "enableTunneling": "Ενεργοποίηση Tunneling", - "enableTunnelingDesc": "Ενεργοποίηση λειτουργίας SSH tunnel για αυτόν τον κεντρικό υπολογιστή", - "serverTunnelsSection": "Σήραγγες Διακομιστή", - "addTunnelBtn": "Προσθήκη Tunnel", - "noTunnelsConfigured": "Δεν έχουν ρυθμιστεί σήραγγες.", - "tunnelLabel": "Σήραγγα {{number}}", - "tunnelType": "Τύπος Tunnel", - "tunnelModeLocalDesc": "Προώθηση μιας τοπικής θύρας σε μια θύρα στον απομακρυσμένο διακομιστή (ή έναν εξυπηρετητή προσβάσιμο από αυτό).", - "tunnelModeRemoteDesc": "Προώθηση μιας θύρας στον απομακρυσμένο διακομιστή πίσω σε μια τοπική θύρα στον υπολογιστή σας.", - "tunnelModeDynamicDesc": "Δημιουργήστε ένα διακομιστή μεσολάβησης SOCKS5 σε μια τοπική θύρα για δυναμική προώθηση θύρας.", - "sameHost": "Αυτός ο κεντρικός υπολογιστής (άμεση σήραγγα)", - "endpointHost": "Διακομιστής Τελικού Σημείου", - "endpointPort": "Θύρα Τελικού Σημείου", - "bindHost": "Δεσμευμένος Διακομιστής", - "sourcePort": "Θύρα Πηγής", - "maxRetries": "Μέγιστες Επαναλήψεις", - "retryIntervalS": "Επανάληψη Διαστήματος (ων)", - "autoStartLabel": "Αυτόματη εκκίνηση", - "autoStartDesc": "Αυτόματη σύνδεση αυτής της διοχέτευσης όταν φορτωθεί ο υπολογιστής", - "tunnelConnecting": "Σύνδεση σήραγγας...", - "tunnelDisconnected": "Η σήραγγα αποσυνδέθηκε", - "failedToConnectTunnel": "Αποτυχία σύνδεσης", - "failedToDisconnectTunnel": "Αποτυχία αποσύνδεσης", - "dockerIntegration": "Ενσωμάτωση Docker", - "enableDockerMonitor": "Ενεργοποίηση Docker", - "enableDockerMonitorDesc": "Παρακολούθηση και διαχείριση δοχείων σε αυτόν τον υπολογιστή μέσω Docker", - "enableFileManagerMonitor": "Ενεργοποίηση Διαχειριστή Αρχείων", - "enableFileManagerMonitorDesc": "Περιηγηθείτε και διαχειριστείτε αρχεία σε αυτόν τον κεντρικό υπολογιστή μέσω SFTP", - "defaultPathLabel": "Προεπιλεγμένη Διαδρομή", - "fileManagerPathHint": "Ο κατάλογος που θα ανοίξει όταν ο διαχειριστής αρχείων ξεκινήσει για αυτόν τον υπολογιστή.", - "statusChecksLabel": "Έλεγχοι Κατάστασης", - "enableStatusChecks": "Ενεργοποίηση Ελέγχου Κατάστασης", - "enableStatusChecksDesc": "Περιοδικά ping σε αυτόν τον κεντρικό υπολογιστή για να επαληθεύσει τη διαθεσιμότητα", - "useGlobalInterval": "Χρήση Καθολικού Διαστήματος", - "useGlobalIntervalDesc": "Παράκαμψη με το διάστημα ελέγχου κατάστασης για ολόκληρο το διακομιστή", - "checkIntervalS": "Χρονικό Διάστημα Ελέγχου", - "checkIntervalDesc": "Δευτερόλεπτα μεταξύ κάθε συνδεσιμότητας ping", - "metricsCollectionLabel": "Συλλογή Μετρικών", - "enableMetricsLabel": "Ενεργοποίηση Μετρήσεων", - "enableMetricsDesc": "Συλλογή CPU, RAM, δίσκου και χρήσης δικτύου από αυτόν τον υπολογιστή", - "useGlobalMetrics": "Χρήση Καθολικού Διαστήματος", - "useGlobalMetricsDesc": "Παράκαμψη με το διάστημα μετρήσεων σε κλίμακα διακομιστή", - "metricsIntervalS": "Διάστημα Μετρήσεων", - "metricsIntervalDesc2": "Δευτερόλεπτα μεταξύ μετρικών στιγμιότυπων", - "visibleWidgets": "Ορατά Widgets", - "cpuUsageLabel": "Χρήση CPU", - "cpuUsageDesc": "CPU τοις εκατό, μέσοι όροι φορτίου, σπινθηρογράφημα", - "memoryLabel": "Χρήση Μνήμης", - "memoryDesc": "Χρήση RAM, swap, cached", - "storageLabel": "Χρήση Δίσκου", - "storageDesc": "Χρήση δίσκου ανά σημείο προσάρτησης", - "networkLabel": "Διεπαφές Δικτύου", - "networkDesc": "Κατάλογος διεπαφών και εύρος ζώνης", - "uptimeLabel": "Χρόνος", - "uptimeDesc": "Χρόνος λειτουργίας συστήματος και χρόνος εκκίνησης", - "systemInfoLabel": "Πληροφορίες Συστήματος", - "systemInfoDesc": "OS, πυρήνας, όνομα hostname, αρχιτεκτονική", - "recentLoginsLabel": "Πρόσφατες Συνδέσεις", - "recentLoginsDesc": "Επιτυχημένες και αποτυχημένες εκδηλώσεις σύνδεσης", - "topProcessesLabel": "Κορυφαίες Διεργασίες", - "topProcessesDesc": "PID, CPU%, MEM%, εντολή", - "listeningPortsLabel": "Θύρες Ακρόασης", - "listeningPortsDesc": "Άνοιγμα θυρών με διεργασία και πολιτεία", - "firewallLabel": "Τείχος Προστασίας", - "firewallDesc": "Firewall, AppArmor, SELinux κατάσταση", - "quickActionsLabel": "Γρήγορες Ενέργειες", - "quickActionsToolbar": "Οι γρήγορες ενέργειες εμφανίζονται ως κουμπιά στη γραμμή στατιστικών διακομιστή για εκτέλεση εντολών με ένα κλικ.", - "noQuickActions": "Δεν υπάρχουν ακόμα γρήγορες ενέργειες.", - "buttonLabel": "Ετικέτα κουμπιού", - "selectSnippetPlaceholder": "Επιλέξτε απόφραξη...", - "addActionBtn": "Προσθήκη Ενέργειας", - "hostSharedSuccessfully": "Επιτυχής κοινή χρήση εξυπηρετητή", - "failedToShareHost": "Αποτυχία κοινής χρήσης κεντρικού υπολογιστή", - "accessRevoked": "Η πρόσβαση ανακλήθηκε", - "failedToRevokeAccess": "Αποτυχία ανάκλησης πρόσβασης", - "cancelBtn": "Ακύρωση", - "savingBtn": "Αποθηκεύεται...", - "addHostBtn": "Προσθήκη Διακομιστή", - "hostUpdated": "Ο υπολογιστής ενημερώθηκε", - "hostCreated": "Ο υπολογιστής δημιουργήθηκε", - "failedToSave": "Αποτυχία αποθήκευσης κεντρικού υπολογιστή", - "credentialUpdated": "Το διαπιστευτήριο ενημερώθηκε", - "credentialCreated": "Δημιουργήθηκε διαπιστευτήριο", - "failedToSaveCredential": "Αποτυχία αποθήκευσης διαπιστευτηρίων", - "backToHosts": "Πίσω στους οικοδεσπότες", - "backToCredentials": "Πίσω στα διαπιστευτήρια", - "pinned": "Καρφιτσωμένο", - "noHostsFound": "Δεν βρέθηκαν υπολογιστές", - "tryDifferentTerm": "Δοκιμάστε έναν διαφορετικό όρο", - "addFirstHost": "Προσθέστε τον πρώτο σας υπολογιστή για να ξεκινήσετε", - "noCredentialsFound": "Δεν βρέθηκαν διαπιστευτήρια", - "addCredentialBtn": "Προσθήκη Διαπιστευτηρίου", - "updateCredentialBtn": "Ενημέρωση Διαπιστευτηρίων", - "features": "Χαρακτηριστικά", - "noFolder": "(Χωρίς φάκελο)", - "deleteSelected": "Διαγραφή", - "exitSelection": "Έξοδος από επιλογή", - "importSkip": "Εισαγωγή (παράλειψη υπάρχει)", - "importOverwrite": "Εισαγωγή (αντικατάσταση)", - "collapseBtn": "Σύμπτυξη", - "importExportBtn": "Εισαγωγή / Εξαγωγή", - "hostStatusesRefreshed": "Ανανεωμένες καταστάσεις κεντρικού υπολογιστή", - "failedToRefreshHosts": "Αποτυχία ανανέωσης υπολογιστών", - "movedHostTo": "Μετακινήθηκε {{host}} στο \"{{folder}}\"", - "failedToMoveHost": "Αποτυχία μετακίνησης κεντρικού υπολογιστή", - "folderRenamedTo": "Ο φάκελος μετονομάστηκε σε \"{{name}}\"", - "deletedFolder": "Διαγραμμένος φάκελος \"{{name}}\"", - "failedToDeleteFolder": "Αποτυχία διαγραφής φακέλου", - "deleteAllInFolder": "Διαγραφή όλων των υπολογιστών στο \"{{name}}\"? Αυτό δεν μπορεί να αναιρεθεί.", - "deletedHost": "Διαγράφηκε {{name}}", - "copiedToClipboard": "Αντιγράφηκε στο πρόχειρο", - "terminalUrlCopied": "Το URL τερματικού αντιγράφηκε", - "fileManagerUrlCopied": "Το URL διαχείρισης αρχείων αντιγράφηκε", - "tunnelUrlCopied": "Το URL διοχέτευσης αντιγράφηκε", - "dockerUrlCopied": "Το URL προσάρτησης αντιγράφηκε", - "serverStatsUrlCopied": "Η διεύθυνση URL αντιγράφηκε διακομιστή", - "rdpUrlCopied": "Το URL RDP αντιγράφηκε", - "vncUrlCopied": "Το URL VNC αντιγράφηκε", - "telnetUrlCopied": "Το URL Telnet αντιγράφηκε", - "remoteDesktopUrlCopied": "Η διεύθυνση URL απομακρυσμένης επιφάνειας εργασίας αντιγράφηκε", - "expandActions": "Ανάπτυξη ενεργειών", - "collapseActions": "Σύμπτυξη ενεργειών", - "wakeOnLanAction": "Αφύπνιση σε LAN", - "wakeOnLanSuccess": "Μαγικό πακέτο στάλθηκε στον/στην {{name}}", - "wakeOnLanError": "Αποτυχία αποστολής μαγικού πακέτου", - "cloneHostAction": "Κλωνοποίηση Υπολογιστή", - "copyAddress": "Αντιγραφή Διεύθυνσης", - "copyLink": "Αντιγραφή Συνδέσμου", - "copyTerminalUrlAction": "Αντιγραφή URL Τερματικού", - "copyFileManagerUrlAction": "Αντιγραφή URL Διαχειριστή Αρχείων", - "copyTunnelUrlAction": "Αντιγραφή Διεύθυνσης Tunnel", - "copyDockerUrlAction": "Αντιγραφή URL Προσάρτησης", - "copyServerStatsUrlAction": "Αντιγραφή Url Στατιστικών Διακομιστή", - "copyRdpUrlAction": "Αντιγραφή URL RDP", - "copyVncUrlAction": "Αντιγραφή Διεύθυνσης Vnc", - "copyTelnetUrlAction": "Αντιγραφή Διεύθυνσης Telnet", - "copyRemoteDesktopUrlAction": "Αντιγραφή URL Απομακρυσμένης Επιφάνειας Εργασίας", - "deleteCredentialConfirm": "Διαγραφή διαπιστευτηρίων \"{{name}}\";", - "deletedCredential": "Διαγράφηκε {{name}}", - "deploySSHKeyTitle": "Ανάπτυξη Κλειδιού SSH", - "deployingBtn": "Εφαρμογή...", - "deployBtn": "Ανάπτυξη", - "failedToDeployKey": "Αποτυχία ανάπτυξης κλειδιού", - "deleteHostsConfirm": "Διαγραφή {{count}} host{{plural}}? Αυτό δεν μπορεί να αναιρεθεί.", - "movedToRoot": "Μεταφέρθηκε στη ρίζα", - "failedToMoveHosts": "Αποτυχία μετακίνησης υπολογιστών", - "enableTerminalFeature": "Ενεργοποίηση Τερματικού", - "disableTerminalFeature": "Απενεργοποίηση Τερματικού", - "enableFilesFeature": "Ενεργοποίηση Αρχείων", - "disableFilesFeature": "Απενεργοποίηση Αρχείων", - "enableTunnelsFeature": "Ενεργοποίηση Σηράγγων", - "disableTunnelsFeature": "Απενεργοποίηση Των Tunnels", - "enableDockerFeature": "Ενεργοποίηση Docker", - "disableDockerFeature": "Απενεργοποίηση Προσάρτησης", - "addTagsPlaceholder": "Προσθήκη ετικετών ...", - "authDetails": "Λεπτομέρειες Ταυτοποίησης", - "credType": "Τύπος", - "generateKeyPairDesc": "Δημιουργήστε ένα νέο ζεύγος κλειδιών, τόσο ιδιωτικά όσο και δημόσια κλειδιά θα συμπληρώνονται αυτόματα.", - "generatingKey": "Δημιουργία...", - "generateLabel": "Δημιουργία {{label}}", - "uploadFileBtn": "Μεταφόρτωση αρχείου", - "keyPassphraseOptional": "Φράση Πρόσβασης Κλειδιού (Προαιρετικό)", - "sshPublicKeyOptional": "SSH Δημόσιο Κλειδί (Προαιρετικό)", - "publicKeyGenerated": "Το δημόσιο κλειδί δημιουργήθηκε", - "failedToGeneratePublicKey": "Αποτυχία άντλησης δημόσιου κλειδιού", - "publicKeyCopied": "Το δημόσιο κλειδί αντιγράφηκε", - "keyPairGenerated": "Δημιουργήθηκε ένα ζευγάρι κλειδιών {{label}}", - "failedToGenerateKeyPair": "Αποτυχία δημιουργίας ζεύγους κλειδιών", - "searchHostsPlaceholder": "Αναζήτηση κεντρικών υπολογιστών, διευθύνσεων, ετικετών…", - "searchCredentialsPlaceholder": "Αναζήτηση διαπιστευτηρίων…", - "refreshBtn": "Ανανέωση", - "addTag": "Προσθήκη ετικετών ...", - "deleteConfirmBtn": "Διαγραφή", - "tunnelRequirementsText": "Ο διακομιστής SSH πρέπει να έχει GatewayPorts ναι, AllowTcpForwarding ναι και PermitRootLogin ναι που έχει οριστεί στο /etc/ssh/sshd_config.", - "deleteHostConfirm": "Διαγραφή \"{{name}}\"?", - "enableAtLeastOneProtocol": "Ενεργοποιήστε τουλάχιστον ένα παραπάνω πρωτόκολλο για να ρυθμίσετε τον έλεγχο ταυτότητας και τις ρυθμίσεις σύνδεσης.", - "keyPassphrase": "Συνθηματική Φράση Κλειδιού", - "connectBtn": "Σύνδεση", - "disconnectBtn": "Αποσύνδεση", - "basicInformation": "Βασικές Πληροφορίες", - "authDetailsSection": "Λεπτομέρειες Ταυτοποίησης", - "credTypeLabel": "Τύπος", - "hostsTab": "Υπολογιστές", - "credentialsTab": "Διαπιστευτήρια", - "selectMultiple": "Επιλογή πολλαπλών", - "selectHosts": "Επιλέξτε οικοδεσπότες", - "connectionLabel": "Σύνδεση", - "authenticationLabel": "Ταυτοποίηση", - "generateKeyPairTitle": "Δημιουργία Ζεύγους Κλειδιών", - "generateKeyPairDescription": "Δημιουργήστε ένα νέο ζεύγος κλειδιών, τόσο ιδιωτικά όσο και δημόσια κλειδιά θα συμπληρώνονται αυτόματα.", - "generateFromPrivateKey": "Δημιουργία από Ιδιωτικό Κλειδί", - "refreshBtn2": "Ανανέωση", - "exitSelectionTitle": "Έξοδος από επιλογή", - "exportAll": "Εξαγωγή Όλων", - "addHostBtn2": "Προσθήκη Διακομιστή", - "addCredentialBtn2": "Προσθήκη Διαπιστευτηρίου", - "checkingHostStatuses": "Έλεγχος κατάστασης κεντρικού υπολογιστή...", - "pinnedSection": "Καρφιτσωμένο", - "hostsExported": "Οι υπολογιστές εξήχθησαν επιτυχώς", - "exportFailed": "Αποτυχία εξαγωγής κεντρικών υπολογιστών", - "sampleDownloaded": "Δείγμα αρχείου που λήφθηκε", - "failedToDeleteCredential2": "Αποτυχία διαγραφής διαπιστευτηρίων", - "noFolderOption": "(Χωρίς φάκελο)", - "nSelected": "επιλεγμένο {{count}}", - "featuresMenu": "Χαρακτηριστικά", - "moveMenu": "Μετακίνηση", - "cancelSelection": "Ακύρωση", - "deployDialogDesc": "Ανάπτυξη {{name}} σε εξουσιοδοτημένου_κλειδιά ενός κεντρικού υπολογιστή.", - "targetHostLabel": "Διακομιστής Στόχου", - "selectHostOption": "Επιλέξτε έναν υπολογιστή...", - "keyDeployedSuccess": "Το κλειδί αναπτύχθηκε με επιτυχία", - "failedToDeployKey2": "Αποτυχία ανάπτυξης κλειδιού", - "deletedCount": "Διαγράφηκαν {{count}} κεντρικοί υπολογιστές", - "failedToDeleteCount": "Αποτυχία διαγραφής {{count}} hosts", - "duplicatedHost": "Διπλότυπο \"{{name}}\"", - "failedToDuplicateHost": "Αποτυχία αντιγραφής κεντρικού υπολογιστή", - "updatedCount": "Ενημερώθηκαν οι {{count}} hosts", - "friendlyNameLabel": "Φιλικό Όνομα", - "descriptionLabel": "Περιγραφή", - "loadingHost": "Φόρτωση κεντρικού υπολογιστή...", - "loadingHosts": "Φόρτωση κεντρικών υπολογιστών...", - "loadingCredentials": "Φόρτωση διαπιστευτηρίων...", - "noHostsYet": "Κανένας υπολογιστής ακόμα", - "noHostsMatchSearch": "Κανένας υπολογιστής δεν ταιριάζει με την αναζήτησή σας", - "hostNotFound": "Δεν βρέθηκε υπολογιστής", - "searchHosts": "Αναζήτηση υπολογιστών...", - "sortHosts": "Ταξινόμηση κεντρικών υπολογιστών", - "sortDefault": "Προεπιλεγμένη παραγγελία", - "sortNameAsc": "Όνομα (Α → Ω)", - "sortNameDesc": "Όνομα (Ω → Α)", - "sortIpAsc": "Διεύθυνση IP (Αύξουσα)", - "sortIpDesc": "Διεύθυνση IP (Περιγραφή)", - "sortOnlineFirst": "Πρώτα στο διαδίκτυο", - "sortOfflineFirst": "Πρώτα εκτός σύνδεσης", - "sortPinnedFirst": "Καρφιτσώθηκε πρώτα", - "filterHosts": "Φιλτράρισμα κεντρικών υπολογιστών", - "filterClearAll": "Καθαρισμός φίλτρων", - "filterStatusGroup": "Κατάσταση", - "filterOnline": "Διαδικτυακά", - "filterOffline": "Εκτός σύνδεσης", - "filterPinned": "Καρφιτσωμένο", - "filterAuthGroup": "Τύπος Εξουσιοδότησης", - "filterAuthPassword": "Σύνθημα", - "filterAuthKey": "Κλειδί SSH", - "filterAuthCredential": "Πιστοποιητικό", - "filterAuthNone": "Κανένας", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Πρωτόκολλο", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "ΠΑΑ", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Τέλνετ", - "filterFeaturesGroup": "Χαρακτηριστικά", - "filterFeatureTerminal": "Τερματικό", - "filterFeatureFileManager": "Διαχειριστής αρχείων", - "filterFeatureTunnel": "Σήραγγα", - "filterFeatureDocker": "Λιμενεργάτης", - "filterTagsGroup": "Ετικέτες", - "shareHost": "Κοινοποίηση Διακομιστή", - "shareHostTitle": "Κοινή χρήση: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Αυτός ο υπολογιστής πρέπει να χρησιμοποιήσει ένα διαπιστευτήριο για να επιτρέψει την κοινή χρήση. Επεξεργαστείτε τον κεντρικό υπολογιστή και εκχωρήστε πρώτα ένα διαπιστευτήριο." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Σύνδεση", - "authentication": "Ταυτοποίηση", - "connectionSettings": "Ρυθμίσεις Σύνδεσης", - "displaySettings": "Ρυθμίσεις Εμφάνισης", - "audioSettings": "Ρυθμίσεις Ήχου", - "rdpPerformance": "Απόδοση RDP", - "deviceRedirection": "Ανακατεύθυνση Συσκευής", - "session": "Συνεδρία", - "gateway": "Πύλη", - "remoteApp": "Απομακρυσμένη Εφαρμογή", - "clipboard": "Πρόχειρο", - "sessionRecording": "Καταγραφή Συνεδρίας", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Ρυθμίσεις VNC", - "terminalSettings": "Ρυθμίσεις Τερματικού", - "rdpPort": "Θύρα RDP", - "username": "Όνομα Χρήστη", - "password": "Κωδικός", - "domain": "Τομέας", - "securityMode": "Λειτουργία Ασφαλείας", - "colorDepth": "Βάθος Χρώματος", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Ύψος", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Μέθοδος Αλλαγή Μεγέθους", - "clientName": "Όνομα Πελάτη", - "initialProgram": "Αρχικό Πρόγραμμα", - "serverLayout": "Διάταξη Διακομιστή", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Θύρα Πύλης", - "gatewayUsername": "Όνομα Χρήστη Πύλης", - "gatewayPassword": "Κωδικός Πύλης", - "gatewayDomain": "Τομέας Πύλης", - "remoteAppProgram": "Πρόγραμμα Απομακρυσμένης Εφαρμογής", - "workingDirectory": "Κατάλογος Εργασίας", - "arguments": "Παράμετροι", - "normalizeLineEndings": "Κανονικοποίηση Καταχωρήσεων Γραμμής", - "recordingPath": "Διαδρομή Καταγραφής", - "recordingName": "Όνομα Εγγραφής", - "macAddress": "Διεύθυνση MAC", - "broadcastAddress": "Διεύθυνση Μετάδοσης", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Χρόνος Αναμονής (s)", - "driveName": "Όνομα Δίσκου", - "drivePath": "Διαδρομή Οδηγού", - "ignoreCertificate": "Αγνόηση Πιστοποιητικού", - "ignoreCertificateDesc": "Να επιτρέπονται συνδέσεις σε υπολογιστές με αυτο-υπογεγραμμένα πιστοποιητικά", - "forceLossless": "Εξαναγκασμός Απώλειας", - "forceLosslessDesc": "Εξαναγκασμός κωδικοποίησης χωρίς απώλειες εικόνας (υψηλότερη ποιότητα, μεγαλύτερο εύρος ζώνης)", - "disableAudio": "Απενεργοποίηση Ήχου", - "disableAudioDesc": "Σίγαση όλων των ήχων από την απομακρυσμένη συνεδρία", - "enableAudioInput": "Ενεργοποίηση Εισόδου Ήχου (Μικρόφωνο)", - "enableAudioInputDesc": "Προώθηση τοπικού μικροφώνου στην απομακρυσμένη συνεδρία", - "wallpaper": "Φόντο", - "wallpaperDesc": "Εμφάνιση ταπετσαρίας επιφάνειας εργασίας (η απενεργοποίηση βελτιώνει την απόδοση)", - "theming": "Θέμα", - "themingDesc": "Ενεργοποίηση οπτικών θεμάτων και στυλ", - "fontSmoothing": "Εξομάλυνση Γραμματοσειράς", - "fontSmoothingDesc": "Ενεργοποίηση απόδοσης γραμματοσειράς ClearType", - "fullWindowDrag": "Πλήρες Παράθυρο", - "fullWindowDragDesc": "Εμφάνιση περιεχομένων παραθύρου κατά το σύρσιμο", - "desktopComposition": "Σύνθεση Επιφάνειας Εργασίας", - "desktopCompositionDesc": "Ενεργοποίηση Aero glass effects", - "menuAnimations": "Εφέ Μενού", - "menuAnimationsDesc": "Ενεργοποίηση κίνησης ξεθωριάσματος μενού και σλάιντ", - "disableBitmapCaching": "Απενεργοποίηση Προσωρινής Αποθήκευσης Bitmap", - "disableBitmapCachingDesc": "Απενεργοποίηση προσωρινής μνήμης bitmap (μπορεί να βοηθήσει με δυσλειτουργίες)", - "disableOffscreenCaching": "Απενεργοποίηση Προσωρινής Αποθήκευσης Offscreen", - "disableOffscreenCachingDesc": "Απενεργοποίηση προσωρινής μνήμης εκτός οθόνης", - "disableGlyphCaching": "Απενεργοποίηση Προσωρινής Αποθήκευσης Glyph", - "disableGlyphCachingDesc": "Απενεργοποίηση glyph cache", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Χρήση αγωγού γραφικών RemoteFX", - "enablePrinting": "Ενεργοποίηση Εκτύπωσης", - "enablePrintingDesc": "Ανακατεύθυνση τοπικών εκτυπωτών στην απομακρυσμένη συνεδρία", - "enableDriveRedirection": "Ενεργοποίηση Ανακατεύθυνσης Δίσκου", - "enableDriveRedirectionDesc": "Αντιστοίχηση ενός τοπικού φακέλου ως μονάδας δίσκου στην απομακρυσμένη συνεδρία", - "createDrivePath": "Δημιουργία Διαδρομής Δίσκου", - "createDrivePathDesc": "Αυτόματη δημιουργία φακέλου αν δεν υπάρχει", - "disableDownload": "Απενεργοποίηση Λήψης", - "disableDownloadDesc": "Αποτροπή λήψης αρχείων από την απομακρυσμένη συνεδρία", - "disableUpload": "Απενεργοποίηση Αποστολής", - "disableUploadDesc": "Αποτροπή μεταφόρτωσης αρχείων στην απομακρυσμένη συνεδρία", - "enableTouch": "Ενεργοποίηση Αφής", - "enableTouchDesc": "Ενεργοποίηση προώθησης εισαγωγής αφής", - "consoleSession": "Συνεδρία Κονσόλας", - "consoleSessionDesc": "Σύνδεση στην κονσόλα (συνεδρία 0) αντί για μια νέα συνεδρία", - "sendWolPacket": "Αποστολή Πακέτου Wol", - "sendWolPacketDesc": "Στείλτε ένα μαγικό πακέτο για να ξυπνήσει αυτόν τον κεντρικό υπολογιστή πριν από τη σύνδεση", - "disableCopy": "Απενεργοποίηση Αντιγραφής", - "disableCopyDesc": "Αποτροπή αντιγραφής κειμένου από την απομακρυσμένη συνεδρία", - "disablePaste": "Απενεργοποίηση Επικόλλησης", - "disablePasteDesc": "Αποτροπή επικόλλησης κειμένου στην απομακρυσμένη συνεδρία", - "createPathIfMissing": "Δημιουργία διαδρομής αν Λείπει", - "createPathIfMissingDesc": "Αυτόματη δημιουργία του φακέλου εγγραφής", - "excludeOutput": "Εξαίρεση Εξόδου", - "excludeOutputDesc": "Να μην καταγράφεται η έξοδος οθόνης (μόνο μεταδεδομένα)", - "excludeMouse": "Εξαίρεση Ποντικιού", - "excludeMouseDesc": "Να μην καταγράφονται κινήσεις του ποντικιού", - "includeKeystrokes": "Συμπερίληψη Πλήκτρων", - "includeKeystrokesDesc": "Εγγραφή ακατέργαστων πληκτρολογίων εκτός από την έξοδο οθόνης", - "vncPort": "Θύρα VNC", - "vncPassword": "Κωδικός VNC", - "vncUsernameOptional": "Όνομα Χρήστη (Προαιρετικό)", - "vncLeaveBlank": "Αφήστε κενό εάν δεν απαιτείται", - "cursorMode": "Λειτουργία Δρομέα", - "swapRedBlue": "Εναλλαγή Κόκκινο/Μπλε", - "swapRedBlueDesc": "Εναλλαγή των καναλιών κόκκινου και μπλε χρώματος (διορθώνει ορισμένα ζητήματα χρώματος)", - "readOnly": "Μόνο για ανάγνωση", - "readOnlyDesc": "Δείτε την απομακρυσμένη οθόνη χωρίς να στείλετε καμία είσοδο", - "telnetPort": "Θύρα Telnet", - "terminalType": "Τύπος Τερματικού", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Θέμα Χρωμάτων", - "backspaceKey": "Κλειδί Backspace", - "saveHostFirst": "Αποθηκεύστε πρώτα τον κεντρικό υπολογιστή.", - "sharingOptionsAfterSave": "Οι επιλογές κοινής χρήσης είναι διαθέσιμες μετά την αποθήκευση του κεντρικού υπολογιστή.", - "sharingLoadError": "Αποτυχία φόρτωσης δεδομένων κοινής χρήσης. Ελέγξτε τη σύνδεσή σας και προσπαθήστε ξανά.", - "shareHostSection": "Κοινοποίηση Διακομιστή", - "shareWithUser": "Κοινή χρήση με το χρήστη", - "shareWithRole": "Κοινή χρήση με το ρόλο", - "selectUser": "Επιλογή Χρήστη", - "selectRole": "Επιλογή Ρόλου", - "selectUserOption": "Επιλέξτε έναν χρήστη...", - "selectRoleOption": "Επιλέξτε ένα ρόλο...", - "permissionLevel": "Επίπεδο Δικαιωμάτων", - "expiresInHours": "Λήγει σε (ώρες)", - "noExpiryPlaceholder": "Αφήστε κενό για να μην λήξει", - "shareBtn": "Κοινοποίηση", - "currentAccess": "Τρέχουσα Πρόσβαση", - "typeHeader": "Τύπος", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Άδεια", - "grantedByHeader": "Χορηγήθηκε Από", - "expiresHeader": "Λήγει", - "noAccessEntries": "Δεν υπάρχουν καταχωρήσεις πρόσβασης.", - "expiredLabel": "Έληξε", - "neverLabel": "Ποτέ", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Ακύρωση", - "savingBtn": "Αποθηκεύεται...", - "updateHostBtn": "Διακομιστής Ενημέρωσης", - "addHostBtn": "Προσθήκη Διακομιστή" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Αναζήτηση hosts, εντολών ή ρυθμίσεων...", - "quickActions": "Γρήγορες Ενέργειες", - "hostManager": "Διαχειριστής Υπολογιστών", - "hostManagerDesc": "Διαχείριση, προσθήκη ή επεξεργασία υπολογιστών", - "addNewHost": "Προσθήκη Νέου Διακομιστή", - "addNewHostDesc": "Εγγραφή νέου κεντρικού υπολογιστή", - "adminSettings": "Ρυθμίσεις Διαχειριστή", - "adminSettingsDesc": "Ρύθμιση προτιμήσεων συστήματος και χρηστών", - "userProfile": "Προφίλ Χρήστη", - "userProfileDesc": "Διαχειριστείτε το λογαριασμό σας και τις προτιμήσεις σας", - "addCredential": "Προσθήκη Διαπιστευτηρίου", - "addCredentialDesc": "Αποθήκευση SSH κλειδιών ή κωδικών πρόσβασης", - "recentActivity": "Πρόσφατη Δραστηριότητα", - "serversAndHosts": "Εξυπηρετητές & Υπολογιστές", - "noHostsFound": "Δεν βρέθηκαν υπολογιστές που να ταιριάζουν \"{{search}}\"", - "links": "Σύνδεσμοι", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Επιλογή", - "toggleWith": "Εναλλαγή με" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Δεν έχει ανατεθεί καρτέλα", - "focusedPane": "Ενεργό παράθυρο" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Δεν υπάρχουν συνδέσεις", - "noConnectionsDesc": "Ανοίξτε ένα τερματικό, έναν διαχειριστή αρχείων ή μια απομακρυσμένη επιφάνεια εργασίας για να δείτε τις συνδέσεις εδώ", - "connectedFor": "Συνδεδεμένος για {{duration}}", - "connected": "Συνδεδεμένος", - "disconnected": "Ασύνδετος", - "closeTab": "Κλείσιμο καρτέλας", - "closeConnection": "Κλείσιμο σύνδεσης", - "forgetTab": "Ξεχνώ", - "removeBackground": "Αφαιρώ", - "reconnect": "Επανασύνδεση", - "reopenTab": "Ξανανοίγω", - "sectionOpen": "Ανοιχτό", - "sectionBackground": "Φόντο", - "backgroundDesc": "Οι συνεδρίες διαρκούν για 30 λεπτά μετά την αποσύνδεση και μπορούν να επανασυνδεθούν.", - "persisted": "Επέμεινε στο παρασκήνιο", - "expiresIn": "Λήγει σε {{duration}}", - "search": "Αναζήτηση συνδέσεων...", - "noSearchResults": "Δεν υπάρχουν συνδέσεις που να αντιστοιχούν στην αναζήτησή σας" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Σύνδεση σε συνεδρία {{type}}...", - "connectionError": "Σφάλμα σύνδεσης", - "connectionFailed": "Αποτυχία σύνδεσης", - "failedToConnect": "Αποτυχία λήψης διακριτικού σύνδεσης", - "hostNotFound": "Δεν βρέθηκε υπολογιστής", - "noHostSelected": "Δεν επιλέχθηκε κεντρικός υπολογιστής", - "reconnect": "Επανασύνδεση", - "retry": "Επανάληψη", - "guacdUnavailable": "Η υπηρεσία απομακρυσμένου υπολογιστή (guacd) δεν είναι διαθέσιμη. Παρακαλώ βεβαιωθείτε ότι το guacd λειτουργεί και είναι προσβάσιμο και ρυθμισμένο σωστά στις ρυθμίσεις διαχειριστή.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Οθόνη Κλειδώματος)", - "winKey": "Κλειδί Παραθύρων", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", "shift": "Shift", "win": "Win", - "stickyActive": "{{key}} (μανωμένο - κάντε κλικ στην έκδοση)", - "stickyInactive": "{{key}} (κάντε κλικ για να κλειδώσετε)", - "esc": "Διαφυγής", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Αρχική", - "end": "Τέλος", - "pageUp": "Σελίδα Επάνω", - "pageDown": "Σελίδα Κάτω", - "arrowUp": "Βέλος Πάνω", - "arrowDown": "Βέλος Κάτω", - "arrowLeft": "Αριστερό Βέλος", - "arrowRight": "Βέλος Δεξιά", - "fnToggle": "Πλήκτρα Συναρτήσεων", - "reconnect": "Επανασύνδεση Συνεδρίας", - "collapse": "Σύμπτυξη γραμμής εργαλείων", - "expand": "Ανάπτυξη γραμμής εργαλείων", - "dragHandle": "Σύρετε για επανατοποθέτηση" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Σύνδεση στον εξυπηρετητή", - "clear": "Εκκαθάριση", - "paste": "Επικόλληση", - "reconnect": "Επανασύνδεση", - "connectionLost": "Η σύνδεση χάθηκε", - "connected": "Συνδεδεμένο", - "clipboardWriteFailed": "Αποτυχία αντιγραφής στο πρόχειρο. Βεβαιωθείτε ότι η σελίδα εξυπηρετείται μέσω HTTPS ή localhost.", - "clipboardReadFailed": "Αποτυχία ανάγνωσης από το πρόχειρο. Βεβαιωθείτε ότι έχουν χορηγηθεί δικαιώματα πρόχειρου.", - "clipboardHttpWarning": "Η Επικόλληση απαιτεί HTTPS. Χρησιμοποιήστε Ctrl+Shift+V ή εξυπηρετήστε Termix μέσω HTTPS.", - "unknownError": "Προέκυψε άγνωστο σφάλμα", - "websocketError": "Σφάλμα σύνδεσης WebSocket", - "connecting": "Σύνδεση...", - "noHostSelected": "Δεν επιλέχθηκε κεντρικός υπολογιστής", - "reconnecting": "Επανασύνδεση... ({{attempt}}/{{max}})", - "reconnected": "Επιτυχής επανασύνδεση", - "tmuxSessionCreated": "δημιουργήθηκε η συνεδρία tmux: {{name}}", - "tmuxSessionAttached": "Συνεδρία tmux επισυνάπτεται: {{name}}", - "tmuxUnavailable": "το tmux δεν είναι εγκατεστημένο στον απομακρυσμένο υπολογιστή, επιστρέφοντας στο τυπικό κέλυφος", - "tmuxSessionPickerTitle": "συνεδρίες tmux", - "tmuxSessionPickerDesc": "Υπάρχουσες συνεδρίες tmux που βρέθηκαν σε αυτόν τον υπολογιστή. Επιλέξτε μία για να επαναφέρετε ή να δημιουργήσετε μια νέα συνεδρία.", - "tmuxWindows": "Παράθυρα", - "tmuxWindowCount": "παράθυρο {{count}}", - "tmuxAttached": "Συνημμένοι πελάτες", - "tmuxAttachedCount": "{{count}} επισυνάπτεται", - "tmuxLastActivity": "Τελευταία δραστηριότητα", - "tmuxTimeJustNow": "μόλις τώρα", - "tmuxTimeMinutes": "{{count}}m πριν", - "tmuxTimeHours": "{{count}}h πριν", - "tmuxTimeDays": "{{count}}πριν από ημέρες", - "tmuxCreateNew": "Έναρξη νέας συνεδρίας", - "tmuxCopyHint": "Προσαρμόστε την επιλογή και πατήστε Enter για αντιγραφή στο πρόχειρο", - "tmuxDetach": "Αποσύνδεση από τη συνεδρία tmux", - "tmuxDetached": "Αποκλεισμένο από τη συνεδρία tmux", - "maxReconnectAttemptsReached": "Επιτεύχθηκαν μέγιστες προσπάθειες επανασύνδεσης", - "closeTab": "Κλείσιμο", - "connectionTimeout": "Χρονικό όριο σύνδεσης", - "terminalTitle": "Τερματικό - {{host}}", - "terminalWithPath": "Τερματικό - {{host}}:{{path}}", - "runTitle": "Εκτέλεση {{command}} - {{host}}", - "totpRequired": "Απαιτείται Έλεγχος Ταυτότητας Δύο Παραγόντων", - "totpCodeLabel": "Κωδικός Επαλήθευσης", - "totpVerify": "Επαλήθευση", - "warpgateAuthRequired": "Απαιτείται Πιστοποίηση Warpgate", - "warpgateSecurityKey": "Κλειδί Ασφαλείας", - "warpgateAuthUrl": "URL Ταυτοποίησης", - "warpgateOpenBrowser": "Άνοιγμα σε περιηγητή", - "warpgateContinue": "Έχω Ολοκληρώσει την Πιστοποίηση", - "opksshAuthRequired": "Απαιτείται Πιστοποίηση OPKSSH", - "opksshAuthDescription": "Πλήρης ταυτοποίηση στο πρόγραμμα περιήγησής σας για να συνεχίσετε. Αυτή η συνεδρία θα παραμείνει έγκυρη για 24 ώρες.", - "opksshOpenBrowser": "Άνοιγμα περιηγητή για έλεγχο ταυτότητας", - "opksshWaitingForAuth": "Αναμονή για έλεγχο ταυτότητας στο πρόγραμμα περιήγησης...", - "opksshAuthenticating": "Επεξεργασία ταυτοποίησης...", - "opksshTimeout": "Λήξη χρονικού ορίου επαλήθευσης. Παρακαλώ προσπαθήστε ξανά.", - "opksshAuthFailed": "Ο έλεγχος ταυτότητας απέτυχε. Παρακαλώ ελέγξτε τα στοιχεία σας και προσπαθήστε ξανά.", - "opksshSignInWith": "Σύνδεση με {{provider}}", - "sudoPasswordPopupTitle": "Εισαγωγή Κωδικού Πρόσβασης?", - "websocketAbnormalClose": "Η σύνδεση έκλεισε απροσδόκητα. Αυτό μπορεί να οφείλεται σε πρόβλημα αντίστροφης διαμεσολάβησης ή ρύθμισης SSL. Παρακαλώ ελέγξτε τα αρχεία καταγραφής διακομιστή.", - "connectionLogTitle": "Αρχείο Καταγραφής Σύνδεσης", - "connectionLogCopy": "Αντιγραφή αρχείων καταγραφής στο πρόχειρο", - "connectionLogEmpty": "Δεν υπάρχουν αρχεία καταγραφής σύνδεσης ακόμα", - "connectionLogWaiting": "Αναμονή για αρχεία καταγραφής σύνδεσης...", - "connectionLogCopied": "Η σύνδεση καταγραφής αντιγράφηκε στο πρόχειρο", - "connectionLogCopyFailed": "Αποτυχία αντιγραφής αρχείων καταγραφής στο πρόχειρο", - "connectionRejected": "Η σύνδεση απορρίφθηκε από το διακομιστή. Παρακαλώ ελέγξτε τον έλεγχο ταυτότητας και τις ρυθμίσεις δικτύου.", - "hostKeyRejected": "Η επαλήθευση SSH κεντρικού υπολογιστή απορρίφθηκε. Η σύνδεση ακυρώθηκε.", - "sessionTakenOver": "Η συνεδρία άνοιξε σε άλλη καρτέλα. Επανασύνδεση..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Δεν επιλέχθηκε κεντρικός υπολογιστής", - "initializingEditor": "Αρχικοποίηση επεξεργαστή...", - "file": "Αρχείο", - "folder": "Φάκελος", - "uploadFile": "Μεταφόρτωση Αρχείου", - "downloadFile": "Λήψη", - "extractArchive": "Εξαγωγή Αρχειοθήκης", - "extractingArchive": "Εξαγωγή {{name}}...", - "archiveExtractedSuccessfully": "Το {{name}} εξήχθη επιτυχώς", - "extractFailed": "Η εξαγωγή απέτυχε", - "compressFile": "Συμπίεση Αρχείου", - "compressFiles": "Συμπίεση Αρχείων", - "compressFilesDesc": "Συμπίεση {{count}} αντικειμένων σε μια αρχειοθήκη", - "archiveName": "Όνομα Αρχειοθέτησης", - "enterArchiveName": "Εισάγετε όνομα αρχειοθήκης...", - "compressionFormat": "Μορφή Συμπίεσης", - "selectedFiles": "Επιλεγμένα αρχεία", - "andMoreFiles": "και {{count}} περισσότερα...", - "compress": "Συμπίεση", - "compressingFiles": "Συμπίεση {{count}} αντικειμένων σε {{name}}...", - "filesCompressedSuccessfully": "Το {{name}} δημιουργήθηκε με επιτυχία", - "compressFailed": "Η συμπίεση απέτυχε", - "edit": "Επεξεργασία", - "preview": "Προεπισκόπηση", - "previous": "Προηγούμενο", - "next": "Επόμενο", - "pageXOfY": "Σελίδα {{current}} του {{total}}", - "zoomOut": "Σμίκρυνση", - "zoomIn": "Μεγέθυνση", - "newFile": "Νέο Αρχείο", - "newFolder": "Νέος Φάκελος", - "rename": "Μετονομασία", - "uploading": "Μεταφόρτωση...", - "uploadingFile": "Μεταφόρτωση {{name}}...", - "fileName": "Όνομα Αρχείου", - "folderName": "Όνομα Φακέλου", - "fileUploadedSuccessfully": "Αρχείο \"{{name}}\" μεταφορτώθηκε επιτυχώς", - "failedToUploadFile": "Αποτυχία μεταφόρτωσης αρχείου", - "fileDownloadedSuccessfully": "Αρχείο \"{{name}}\" κατεβασμένο επιτυχώς", - "failedToDownloadFile": "Αποτυχία λήψης αρχείου", - "fileCreatedSuccessfully": "Αρχείο \"{{name}}\" δημιουργήθηκε με επιτυχία", - "folderCreatedSuccessfully": "Ο φάκελος \"{{name}}\" δημιουργήθηκε με επιτυχία", - "failedToCreateItem": "Αποτυχία δημιουργίας στοιχείου", - "operationFailed": "{{operation}} λειτουργία απέτυχε για {{name}}: {{error}}", - "failedToResolveSymlink": "Αποτυχία επίλυσης συντόμευσης", - "itemsDeletedSuccessfully": "{{count}} στοιχεία διαγράφηκαν με επιτυχία", - "failedToDeleteItems": "Αποτυχία διαγραφής στοιχείων", - "sudoPasswordRequired": "Απαιτείται Κωδικός Διαχειριστή", - "enterSudoPassword": "Εισάγετε sudo password για να συνεχίσετε αυτή τη λειτουργία", - "sudoPassword": "Κωδικός πρόσβασης Sudo", - "sudoOperationFailed": "Η επιχείρηση Sudo απέτυχε", - "sudoAuthFailed": "Ο έλεγχος ταυτότητας Sudo απέτυχε", - "dragFilesToUpload": "Αποθέστε αρχεία εδώ για μεταφόρτωση", - "emptyFolder": "Αυτός ο φάκελος είναι κενός", - "searchFiles": "Αναζήτηση αρχείων...", - "upload": "Ανέβασμα", - "selectHostToStart": "Επιλέξτε έναν εξυπηρετητή για να ξεκινήσετε τη διαχείριση αρχείων", - "sshRequiredForFileManager": "Ο διαχειριστής αρχείων απαιτεί SSH. Αυτός ο υπολογιστής δεν έχει SSH ενεργοποιημένο.", - "failedToConnect": "Αποτυχία σύνδεσης στο SSH", - "failedToLoadDirectory": "Αποτυχία φόρτωσης φακέλου", - "noSSHConnection": "Δεν υπάρχει διαθέσιμη σύνδεση SSH", - "copy": "Αντιγραφή", - "cut": "Αποκοπή", - "paste": "Επικόλληση", - "copyPath": "Αντιγραφή Διαδρομής", - "copyPaths": "Αντιγραφή Διαδρομών", - "delete": "Διαγραφή", - "properties": "Ιδιότητες", - "refresh": "Ανανέωση", - "downloadFiles": "Λήψη {{count}} αρχείων στον περιηγητή", - "copyFiles": "Αντιγραφή {{count}} αντικειμένων", - "cutFiles": "Αποκοπή {{count}} αντικειμένων", - "deleteFiles": "Διαγραφή στοιχείων {{count}}", - "filesCopiedToClipboard": "Τα στοιχεία {{count}} αντιγράφηκαν στο πρόχειρο", - "filesCutToClipboard": "{{count}} στοιχεία κομμένα στο πρόχειρο", - "pathCopiedToClipboard": "Η διαδρομή αντιγράφηκε στο πρόχειρο", - "pathsCopiedToClipboard": "Οι διαδρομές {{count}} αντιγράφηκαν στο πρόχειρο", - "failedToCopyPath": "Αποτυχία αντιγραφής διαδρομής στο πρόχειρο", - "movedItems": "Μετακινήθηκαν {{count}} στοιχεία", - "failedToDeleteItem": "Αποτυχία διαγραφής στοιχείου", - "itemRenamedSuccessfully": "Το {{type}} μετονομάστηκε με επιτυχία", - "failedToRenameItem": "Αποτυχία μετονομασίας στοιχείου", - "download": "Λήψη", - "permissions": "Δικαιώματα", - "size": "Μέγεθος", - "modified": "Τροποποιήθηκε", - "path": "Διαδρομή", - "confirmDelete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το {{name}}?", - "permissionDenied": "Άρνηση πρόσβασης", - "serverError": "Σφάλμα Διακομιστή", - "fileSavedSuccessfully": "Το αρχείο αποθηκεύτηκε επιτυχώς", - "failedToSaveFile": "Αποτυχία αποθήκευσης αρχείου", - "confirmDeleteSingleItem": "Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα \"{{name}}\"?", - "confirmDeleteMultipleItems": "Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα {{count}} αντικείμενα?", - "confirmDeleteMultipleItemsWithFolders": "Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα {{count}} αντικείμενα? Αυτό περιλαμβάνει φακέλους και τα περιεχόμενά τους.", - "confirmDeleteFolder": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά τον φάκελο \"{{name}}\" και όλα τα περιεχόμενά του;", - "permanentDeleteWarning": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Το αντικείμενο(α) θα διαγραφεί οριστικά από το διακομιστή.", - "recent": "Πρόσφατα", - "pinned": "Καρφιτσωμένο", - "folderShortcuts": "Συντομεύσεις Φακέλου", - "failedToReconnectSSH": "Αποτυχία επανασύνδεσης SSH συνεδρίας", - "openTerminalHere": "Άνοιγμα Τερματικού Εδώ", - "run": "Εκτέλεση", - "openTerminalInFolder": "Άνοιγμα τερματικού σε αυτόν τον φάκελο", - "openTerminalInFileLocation": "Άνοιγμα τερματικού στην τοποθεσία αρχείου", - "runningFile": "Τρέξιμο - {{file}}", - "onlyRunExecutableFiles": "Μπορέστε να εκτελέσετε μόνο εκτελέσιμα αρχεία", - "directories": "Κατάλογοι", - "removedFromRecentFiles": "Αφαιρέθηκε το \"{{name}}\" από τα πρόσφατα αρχεία", - "removeFailed": "Αποτυχία κατάργησης", - "unpinnedSuccessfully": "Unpinned \"{{name}}\" επιτυχώς", - "unpinFailed": "Αποτυχία αποσύνδεσης", - "removedShortcut": "Αφαιρέθηκε συντόμευση \"{{name}}\"", - "removeShortcutFailed": "Κατάργηση συντόμευσης απέτυχε", - "clearedAllRecentFiles": "Εκκαθάριση όλων των πρόσφατων αρχείων", - "clearFailed": "Αποτυχία εκκαθάρισης", - "removeFromRecentFiles": "Αφαίρεση από τα πρόσφατα αρχεία", - "clearAllRecentFiles": "Εκκαθάριση όλων των πρόσφατων αρχείων", - "unpinFile": "Ξεκαρφίτσωμα αρχείου", - "removeShortcut": "Αφαίρεση συντόμευσης", - "pinFile": "Καρφίτσωμα αρχείου", - "addToShortcuts": "Προσθήκη στις συντομεύσεις", - "pasteFailed": "Η επικόλληση απέτυχε", - "noUndoableActions": "Καμία μη αναιρέσιμη ενέργεια", - "undoCopySuccess": "Αναίρεση λειτουργίας αντιγραφής: Διαγράφηκαν {{count}} αντιγραμμένα αρχεία", - "undoCopyFailedDelete": "Αποτυχία αναίρεσης: Δεν ήταν δυνατή η διαγραφή αντιγραμμένων αρχείων", - "undoCopyFailedNoInfo": "Αποτυχία αναίρεσης: Αδυναμία εύρεσης αντιγραμμένων πληροφοριών αρχείου", - "undoMoveSuccess": "Undid move operation: Μετακινήθηκε {{count}} αρχεία πίσω στην αρχική τοποθεσία", - "undoMoveFailedMove": "Αποτυχία αναίρεσης: Δεν ήταν δυνατή η μετακίνηση αρχείων πίσω", - "undoMoveFailedNoInfo": "Αποτυχία αναίρεσης: Αδυναμία εύρεσης πληροφοριών αρχείου που μετακινήθηκαν", - "undoDeleteNotSupported": "Η λειτουργία διαγραφής δεν μπορεί να αναιρεθεί: Τα αρχεία έχουν διαγραφεί οριστικά από το διακομιστή", - "undoTypeNotSupported": "Μη υποστηριζόμενος τύπος λειτουργίας αναίρεσης", - "undoOperationFailed": "Η λειτουργία αναίρεσης απέτυχε", - "unknownError": "Άγνωστο σφάλμα", - "confirm": "Επιβεβαίωση", - "find": "Εύρεση...", - "replace": "Αντικατάσταση", - "downloadInstead": "Λήψη Αντ 'αυτού", - "keyboardShortcuts": "Συντομεύσεις Πληκτρολογίου", - "searchAndReplace": "Αναζήτηση & Αντικατάσταση", - "editing": "Επεξεργασία", - "search": "Αναζήτηση", - "findNext": "Εύρεση Επόμενου", - "findPrevious": "Εύρεση Προηγούμενου", - "save": "Αποθήκευση", - "selectAll": "Επιλογή Όλων", - "undo": "Αναίρεση", - "redo": "Επανάληψη", - "moveLineUp": "Μετακίνηση Γραμμής Πάνω", - "moveLineDown": "Μετακίνηση Γραμμής Κάτω", - "toggleComment": "Εναλλαγή Σχολίου", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Αποτυχία φόρτωσης εικόνας", - "startTyping": "Αρχίστε να πληκτρολογείτε...", - "unknownSize": "Άγνωστο μέγεθος", - "fileIsEmpty": "Το αρχείο είναι κενό", - "largeFileWarning": "Προειδοποίηση Μεγάλου Αρχείου", - "largeFileWarningDesc": "Αυτό το αρχείο είναι {{size}} σε μέγεθος, το οποίο μπορεί να προκαλέσει προβλήματα απόδοσης όταν ανοιχθεί ως κείμενο.", - "fileNotFoundAndRemoved": "Αρχείο \"{{name}}\" δεν βρέθηκε και έχει αφαιρεθεί από πρόσφατα / καρφιτσωμένα αρχεία", - "failedToLoadFile": "Απέτυχε η φόρτωση του αρχείου: {{error}}", - "serverErrorOccurred": "Παρουσιάστηκε σφάλμα διακομιστή. Παρακαλώ προσπαθήστε ξανά αργότερα.", - "autoSaveFailed": "Η αυτόματη αποθήκευση απέτυχε", - "fileAutoSaved": "Αυτόματα αποθηκευμένο αρχείο", - "moveFileFailed": "Αποτυχία μετακίνησης του {{name}}", - "moveOperationFailed": "Αποτυχία λειτουργίας μετακίνησης", - "canOnlyCompareFiles": "Μπορεί να συγκρίνει μόνο δύο αρχεία", - "comparingFiles": "Σύγκριση αρχείων: {{file1}} και {{file2}}", - "dragFailed": "Αποτυχία λειτουργίας συρσίματος", - "filePinnedSuccessfully": "Αρχείο \"{{name}}\" καρφιτσώθηκε επιτυχώς", - "pinFileFailed": "Αποτυχία καρφιτσώματος", - "fileUnpinnedSuccessfully": "Αρχείο \"{{name}}\" ξεκαρφιτσώθηκε επιτυχώς", - "unpinFileFailed": "Αποτυχία ξεκαρφιτσώματος", - "shortcutAddedSuccessfully": "Συντόμευση φακέλου \"{{name}}\" προστέθηκε επιτυχώς", - "addShortcutFailed": "Αποτυχία προσθήκης συντόμευσης", - "operationCompletedSuccessfully": "{{operation}} {{count}} στοιχεία με επιτυχία", - "operationCompleted": "{{operation}} {{count}} στοιχεία", - "downloadFileSuccess": "Αρχείο {{name}} κατέβηκε επιτυχώς", - "downloadFileFailed": "Αποτυχία λήψης", - "moveTo": "Μετακίνηση στο {{name}}", - "diffCompareWith": "Σύγκριση διαφοράς με το {{name}}", - "dragOutsideToDownload": "Σύρετε έξω από το παράθυρο για λήψη ({{count}} αρχεία)", - "newFolderDefault": "Νέος Φάκελος", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Επιτυχής μετακίνηση {{count}} στοιχείων στο {{target}}", - "move": "Μετακίνηση", - "searchInFile": "Αναζήτηση σε αρχείο (Ctrl+F)", - "showKeyboardShortcuts": "Εμφάνιση συντομεύσεων πληκτρολογίου", - "startWritingMarkdown": "Αρχίστε να γράφετε το περιεχόμενό σας markdown...", - "loadingFileComparison": "Φόρτωση σύγκρισης αρχείου...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Σύγκριση", - "sideBySide": "Πλευρά δίπλα", - "inline": "Εμβόλιμο", - "fileComparison": "Σύγκριση αρχείων: {{file1}} vs {{file2}}", - "fileTooLarge": "Πολύ μεγάλο αρχείο: {{error}}", - "sshConnectionFailed": "Αποτυχία σύνδεσης SSH. Παρακαλώ ελέγξτε τη σύνδεσή σας στο {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Απέτυχε η φόρτωση του αρχείου: {{error}}", - "connecting": "Σύνδεση...", - "connectedSuccessfully": "Επιτυχής σύνδεση", - "totpVerificationFailed": "Αποτυχία επαλήθευσης TOTP", - "warpgateVerificationFailed": "Η ταυτοποίηση Warpgate απέτυχε", - "authenticationFailed": "Αποτυχία ταυτοποίησης", - "verificationCodePrompt": "Κωδικός επιβεβαίωσης:", - "changePermissions": "Αλλαγή Δικαιωμάτων", - "currentPermissions": "Τρέχοντα Δικαιώματα", - "owner": "Ιδιοκτήτης", - "group": "Ομάδα", - "others": "Άλλα", - "read": "Ανάγνωση", - "write": "Εγγραφή", - "execute": "Εκτέλεση", - "permissionsChangedSuccessfully": "Τα δικαιώματα άλλαξαν επιτυχώς", - "failedToChangePermissions": "Αποτυχία αλλαγής δικαιωμάτων", - "name": "Όνομα", - "sortByName": "Όνομα", - "sortByDate": "Ημερομηνία Τροποποίησης", - "sortBySize": "Μέγεθος", - "ascending": "Αύξουσα", - "descending": "Φθίνουσα", - "root": "Ρίζα", - "new": "Νέο", - "sortBy": "Ταξινόμηση Κατά", - "items": "Στοιχεία", - "selected": "Επιλεγμένο", - "editor": "Επεξεργαστής", - "octal": "Οκταδική", - "storage": "Αποθήκευση", - "disk": "Δίσκος", - "used": "Χρησιμοποιείται", - "of": "από", - "toggleSidebar": "Εναλλαγή Πλευρικής Μπάρας", - "cannotLoadPdf": "Αδυναμία φόρτωσης PDF", - "pdfLoadError": "Παρουσιάστηκε σφάλμα κατά τη φόρτωση αυτού του αρχείου PDF.", - "loadingPdf": "Φόρτωση PDF...", - "loadingPage": "Φόρτωση σελίδας..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Αντιγραφή στον κεντρικό υπολογιστή…", - "moveToHost": "Μετακίνηση στον κεντρικό υπολογιστή…", - "copyItemsToHost": "Αντιγραφή στοιχείων {{count}} για φιλοξενία…", - "moveItemsToHost": "Μετακίνηση στοιχείων {{count}} για φιλοξενία…", - "noHostsConnected": "Δεν υπάρχουν άλλοι διαθέσιμοι κεντρικοί υπολογιστές διαχείρισης αρχείων.", - "noHostsConnectedHint": "Προσθέστε έναν άλλο κεντρικό υπολογιστή SSH με ενεργοποιημένο το File Manager στο Host Manager.", - "selectDestinationHost": "Επιλέξτε κεντρικό υπολογιστή προορισμού", - "destinationPath": "Διαδρομή προορισμού", - "recentDestinations": "Πρόσφατοι προορισμοί", - "collapseRecentDestinations": "Σύμπτυξη πρόσφατων προορισμών", - "expandRecentDestinations": "Ανάπτυξη πρόσφατων προορισμών", - "browseFolders": "Αναζήτηση φακέλων προορισμού", - "browseDestination": "Περιήγηση ή εισαγωγή διαδρομής", - "confirmCopy": "Αντίγραφο", - "confirmMove": "Κίνηση", - "transferring": "Μεταφορά…", - "compressing": "Συμπίεση…", - "extracting": "Εξαγωγή…", - "transferringItems": "Μεταφορά {{current}} από {{total}} στοιχεία…", - "transferSuccess": "Η μεταφορά ολοκληρώθηκε", - "transferError": "Η μεταφορά απέτυχε", - "transferPartial": "Η μεταφορά ολοκληρώθηκε με σφάλματα {{count}}", - "transferPartialHint": "Δεν ήταν δυνατή η μεταφορά: {{paths}}", - "itemsSummary": "{{count}} στοιχεία", - "destMustBeDirectory": "Ο προορισμός πρέπει να είναι ένας κατάλογος για μεταφορές πολλαπλών στοιχείων", - "selectThisFolder": "Επιλέξτε αυτόν τον φάκελο", - "browsePathWillBeCreated": "Αυτός ο φάκελος δεν υπάρχει ακόμη. Θα δημιουργηθεί όταν ξεκινήσει η μεταφορά.", - "browsePathError": "Δεν ήταν δυνατό το άνοιγμα αυτής της διαδρομής στον κεντρικό υπολογιστή προορισμού.", - "goUp": "Ανεβαίνω", - "copyFolderToHost": "Αντιγραφή φακέλου στον κεντρικό υπολογιστή…", - "moveFolderToHost": "Μετακίνηση φακέλου στον κεντρικό υπολογιστή…", - "hostReady": "Ετοιμος", - "hostConnecting": "Σύνδεση…", - "hostDisconnected": "Δεν είναι συνδεδεμένο", - "hostAuthRequired": "Απαιτείται έλεγχος ταυτότητας — ανοίξτε πρώτα τη Διαχείριση αρχείων σε αυτόν τον κεντρικό υπολογιστή", - "hostConnectionFailed": "Η σύνδεση απέτυχε", - "metricsTitle": "Χρόνοι μεταφοράς", - "metricsPrepare": "Προετοιμασία προορισμού: {{duration}}", - "metricsCompress": "Συμπίεση στην πηγή: {{duration}}", - "metricsHopSourceRead": "Πηγή → διακομιστής: {{throughput}}", - "metricsHopDestSftpWrite": "Διακομιστής → προορισμός (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Διακομιστής → προορισμός (τοπικός): {{throughput}}", - "metricsTransfer": "Από άκρο σε άκρο: {{throughput}} ({{duration}})", - "metricsExtract": "Εξαγωγή στον προορισμό: {{duration}}", - "metricsSourceDelete": "Αφαίρεση από την πηγή: {{duration}}", - "metricsTotal": "Σύνολο: {{duration}}", - "progressCompressing": "Συμπίεση στον κεντρικό υπολογιστή προέλευσης…", - "progressExtracting": "Εξαγωγή στον προορισμό…", - "progressTransferring": "Μεταφορά δεδομένων…", - "progressReconnecting": "Επανασύνδεση…", - "parallelSegmentsLabel": "Παράλληλες λωρίδες μετεπιβίβασης", - "parallelSegmentsOption": "{{count}} λωρίδες", - "parallelSegmentsHint": "Τα μεγάλα αρχεία χωρίζονται σε κομμάτια των 256 MB. Πολλαπλές λωρίδες χρησιμοποιούν ξεχωριστές συνδέσεις (όπως η έναρξη αρκετών μεταφορών) για υψηλότερη συνολική απόδοση.", - "progressTotalSpeed": "{{speed}} σύνολο ({{lanes}} λωρίδες)", - "progressTransferringItems": "Μεταφορά αρχείων ({{current}} από {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} αρχεία", - "sourceNotDeletedPartial": "Τα αρχεία προέλευσης διατηρούνται (μερική μεταφορά)", - "jumpHostLimitation": "Και οι δύο κεντρικοί υπολογιστές πρέπει να είναι προσβάσιμοι από τον διακομιστή Termix. Δεν υποστηρίζεται η άμεση δρομολόγηση από κεντρικό υπολογιστή σε κεντρικό υπολογιστή.", - "cancel": "Ματαίωση", - "methodLabel": "Μέθοδος μεταφοράς", - "methodAuto": "Αυτο", - "methodTar": "Αρχείο Tar", - "methodItemSftp": "SFTP ανά αρχείο", - "methodAutoHint": "Επιλέγει SFTP tar ή ανά αρχείο με βάση τον αριθμό αρχείων, το μέγεθος και τη συμπιεστότητα. Τα μεμονωμένα αρχεία χρησιμοποιούν πάντα SFTP ροής.", - "methodTarHint": "Συμπίεση στην πηγή, μεταφορά ενός αρχείου, εξαγωγή στον προορισμό. Απαιτείται tar και στους δύο κεντρικούς υπολογιστές Unix.", - "methodItemSftpHint": "Μεταφέρετε κάθε αρχείο ξεχωριστά μέσω SFTP. Λειτουργεί σε όλους τους κεντρικούς υπολογιστές, συμπεριλαμβανομένων των Windows.", - "methodPreviewLoading": "Υπολογισμός μεθόδου μεταφοράς…", - "methodPreviewError": "Δεν ήταν δυνατή η προεπισκόπηση της μεθόδου μεταφοράς. Ο διακομιστής θα επιλέξει μια μέθοδο κατά την εκκίνηση.", - "methodPreviewWillUseTar": "Θα χρησιμοποιηθεί: Αρχείο Tar", - "methodPreviewWillUseItemSftp": "Θα χρησιμοποιηθεί: SFTP ανά αρχείο", - "methodPreviewScanSummary": "{{fileCount}} αρχεία, {{totalSize}} συνολικά (σαρωμένα στον κεντρικό υπολογιστή προέλευσης).", - "methodItemSftpLimitation": "Κάθε αρχείο χρησιμοποιεί την ίδια ροή SFTP ως αντίγραφο ενός μόνο αρχείου, το ένα μετά το άλλο. Η πρόοδος συνδυάζεται σε όλα τα αρχεία, επομένως η γραμμή κινείται αργά κατά τη διάρκεια μεγάλων αρχείων.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Επιλέξατε SFTP ανά αρχείο.", - "user_tar": "Επιλέξατε το αρχείο tar.", - "tar_unavailable": "Το Tar δεν είναι διαθέσιμο σε έναν ή και στους δύο κεντρικούς υπολογιστές — θα χρησιμοποιηθεί αντ' αυτού το SFTP ανά αρχείο.", - "windows_host": "Εμπλέκεται ένας κεντρικός υπολογιστής των Windows — δεν χρησιμοποιείται το tar.", - "auto_multi_large": "Αυτόματα: πολλά αρχεία, συμπεριλαμβανομένου ενός μεγάλου αρχείου ({{largestSize}}) με συμπιέσιμα δεδομένα — tar ομαδοποιεί σε μία μεταφορά.", - "auto_single_large_in_archive": "Αυτόματα: ένα μεγάλο αρχείο ({{largestSize}}) σε αυτό το σύνολο — ανά αρχείο SFTP.", - "auto_many_incompressible": "Αυτόματα: ως επί το πλείστον ασυμπίεστα δεδομένα — SFTP ανά αρχείο.", - "auto_many_files": "Αυτόματα: πολλά αρχεία ({{fileCount}}) — το tar μειώνει την επιβάρυνση ανά αρχείο.", - "auto_default": "Αυτόματα: SFTP ανά αρχείο για αυτό το σύνολο." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Ματαίωση", - "progressCancelling": "Ακύρωση…", - "progressStalled": "Σταματημένο", - "resumedHint": "Επανασύνδεση με μια ενεργή μεταφορά που ξεκίνησε σε άλλο παράθυρο.", - "transferCancelled": "Η μεταφορά ακυρώθηκε.", - "transferCancelledCopyHint": "Ενδέχεται να παραμείνουν μερικά αρχεία στον προορισμό.", - "transferCancelledMoveHint": "Ενδέχεται να παραμείνουν μερικά αρχεία στον προορισμό. Τα αρχεία προέλευσης δεν καταργήθηκαν.", - "cleanupDestFiles": "Καθαρισμός προορισμού", - "cleanupDestFilesSuccess": "Αφαιρέθηκαν μερικά αρχεία από τον προορισμό", - "cleanupDestFilesPartial": "Δεν ήταν δυνατή η κατάργηση ορισμένων τμηματικών αρχείων", - "cleanupDestFilesNothing": "Δεν υπάρχει τίποτα προς διόρθωση στον προορισμό", - "cleanupDestFilesError": "Ο καθαρισμός απέτυχε", - "retryTransfer": "Δοκιμάζω πάλι", - "retryTransferError": "Η επανάληψη απέτυχε", - "transferFailedRetryHint": "Μερικά δεδομένα διατηρήθηκαν στον προορισμό. Η επανάληψη θα συνεχιστεί όταν αποκατασταθεί η σύνδεση." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Χωρίς SSH Tunnels", - "createFirstTunnelMessage": "Δεν έχετε δημιουργήσει ακόμα SSH tunnel. Ρυθμίστε τις συνδέσεις tunnel στο Host Manager για να ξεκινήσετε.", - "connected": "Συνδεδεμένο", - "disconnected": "Αποσυνδέθηκε", - "connecting": "Σύνδεση...", - "error": "Σφάλμα", - "canceling": "Ακύρωση...", - "connect": "Σύνδεση", - "disconnect": "Αποσύνδεση", - "cancel": "Ακύρωση", - "port": "Θύρα", - "localPort": "Τοπική Θύρα", - "remotePort": "Απομακρυσμένη Θύρα", - "currentHostPort": "Τρέχουσα Θύρα Διακομιστή", - "endpointPort": "Θύρα Τελικού Σημείου", - "bindIp": "Τοπική IP", - "endpointSshConfig": "Ρύθμιση Τελικού Σημείου SSH", - "endpointSshHost": "Τελικός Διακομιστής SSH", - "endpointSshHostPlaceholder": "Επιλέξτε έναν ρυθμισμένο διακομιστή", - "endpointSshHostRequired": "Επιλέξτε έναν υπολογιστή SSH τελικού σημείου για κάθε δρομολόγηση υπολογιστή-πελάτη.", - "attempt": "Προσπάθεια {{current}} του {{max}}", - "nextRetryIn": "Επόμενη προσπάθεια σε {{seconds}} δευτερόλεπτα", - "clientTunnels": "Σήραγγες Πελάτη", - "clientTunnel": "Σήραγγα Πελάτη", - "addClientTunnel": "Προσθήκη Σήραγγας Πελάτη", - "noClientTunnels": "Δεν έχουν ρυθμιστεί σήραγγες πελάτη σε αυτήν την επιφάνεια εργασίας.", - "tunnelName": "Όνομα Tunnel", - "remoteHost": "Απομακρυσμένος Διακομιστής", - "autoStart": "Αυτόματη Εκκίνηση", - "clientAutoStartDesc": "Ξεκινά όταν ανοίγει αυτός ο υπολογιστής-πελάτης και παραμένει συνδεδεμένος.", - "clientManualStartDesc": "Χρησιμοποιήστε Έναρξη και Διακοπή από αυτή τη σειρά. Το Termix δεν θα το ανοίξει αυτόματα.", - "clientRemoteServerNote": "Η απομακρυσμένη προώθηση μπορεί να απαιτεί AllowTcpForwarding και GatewayPorts στον εξυπηρετητή τελικού σημείου SSH. Η απομακρυσμένη θύρα κλείνει όταν αποσυνδέεται αυτή η επιφάνεια εργασίας.", - "clientTunnelStarted": "Η σήραγγα πελάτη ξεκίνησε", - "clientTunnelStopped": "Η σήραγγα πελάτη σταμάτησε", - "tunnelTestSucceeded": "Επιτυχής δοκιμή σήραγγας", - "tunnelTestFailed": "Η δοκιμή σήραγγας απέτυχε", - "localSaved": "Οι σήραγγες πελατών αποθηκεύτηκαν", - "localSaveError": "Αποτυχία αποθήκευσης τοπικών tunnels πελάτη", - "invalidBindIp": "Η τοπική IP πρέπει να είναι μια έγκυρη διεύθυνση IPv4.", - "invalidLocalTargetIp": "Το τοπικό IP προορισμού πρέπει να είναι μια έγκυρη διεύθυνση IPv4.", - "invalidLocalPort": "Η τοπική θύρα πρέπει να είναι μεταξύ 1 και 65535.", - "invalidRemotePort": "Η απομακρυσμένη θύρα πρέπει να είναι μεταξύ 1 και 65535.", - "invalidLocalTargetPort": "Η τοπική θύρα προορισμού πρέπει να είναι μεταξύ 1 και 65535.", - "invalidEndpointPort": "Ο λιμένας τελικού σημείου πρέπει να είναι μεταξύ 1 και 65535.", - "duplicateAutoStartBind": "Μόνο μία αυτόματη εκκίνηση tunnel πελάτη μπορεί να χρησιμοποιήσει το {{bind}}.", - "manualControlError": "Αποτυχία ενημέρωσης κατάστασης διοχέτευσης.", - "active": "Ενεργό", - "start": "Έναρξη", - "stop": "Διακοπή", - "test": "Δοκιμή", - "type": "Τύπος Tunnel", - "typeLocal": "Τοπικό (-L)", - "typeRemote": "Απομακρυσμένο (-R)", - "typeDynamic": "Δυναμικό (-D)", - "typeServerLocalDesc": "Τρέχων υπολογιστής στο τελικό σημείο.", - "typeServerRemoteDesc": "Τελικό σημείο πίσω στον τρέχοντα υπολογιστή.", - "typeClientLocalDesc": "Τοπικός υπολογιστής στο τελικό σημείο.", - "typeClientRemoteDesc": "Τελικό σημείο πίσω στον τοπικό υπολογιστή.", - "typeClientDynamicDesc": "SOCKS στον τοπικό υπολογιστή.", - "typeDynamicDesc": "Προώθηση κίνησης SOCKS5 ΣΥΝΔΕΣΗΣ μέσω SSH", - "forwardDescriptionServerLocal": "Τρέχων κεντρικός υπολογιστής {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Τελικό σημείο {{endpointPort}} → τρέχων κεντρικός υπολογιστής {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS στον τρέχοντα κεντρικό υπολογιστή {{sourcePort}}.", - "forwardDescriptionClientLocal": "Τοπικό {{sourcePort}} → remote {{endpointPort}}.", - "forwardDescriptionClientRemote": "Απομακρυσμένο {{sourcePort}} → τοπικό {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS στο τοπικό λιμάνι {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS μέσω {{endpoint}}", - "autoNameClientLocal": "Τοπικό {{localPort}} → {{endpoint}} {{remotePort}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} μέσω {{endpoint}}", - "route": "Διαδρομή:", - "lastStarted": "Τελευταία εκκίνηση", - "lastTested": "Τελευταία δοκιμή", - "lastError": "Τελευταίο σφάλμα", - "maxRetries": "Μέγιστες Επαναλήψεις", - "maxRetriesDescription": "Μέγιστος αριθμός προσπαθειών επανάληψης.", - "retryInterval": "Διάστημα Επανάληψη (δευτερόλεπτα)", - "retryIntervalDescription": "Χρόνος αναμονής μεταξύ προσπαθειών επανάληψης.", - "local": "Τοπικό", - "remote": "Απομακρυσμένο", - "destination": "Προορισμός", - "host": "Διακομιστής", - "mode": "Λειτουργία", - "noHostSelected": "Δεν επιλέχθηκε κεντρικός υπολογιστής", - "working": "Εργάζεται..." + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Επεξεργαστής", - "memory": "Μνήμη", - "disk": "Δίσκος", - "network": "Δίκτυο", - "uptime": "Χρόνος", - "processes": "Διεργασίες", - "available": "Διαθέσιμο", - "free": "Δωρεάν", - "connecting": "Σύνδεση...", - "connectionFailed": "Αποτυχία σύνδεσης με το διακομιστή", - "naCpus": "Δ/Α ΚΜΕ", - "cpuCores_one": "{{count}} Πυρήνας", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Χρήση CPU", - "memoryUsage": "Χρήση Μνήμης", - "diskUsage": "Χρήση Δίσκου", - "failedToFetchHostConfig": "Αποτυχία ανάκτησης ρύθμισης παραμέτρων κεντρικού υπολογιστή", - "serverOffline": "Διακομιστής Χωρίς Σύνδεση", - "cannotFetchMetrics": "Αδυναμία λήψης μετρήσεων από διακομιστή εκτός σύνδεσης", - "totpFailed": "Αποτυχία επαλήθευσης TOTP", - "noneAuthNotSupported": "Τα Στατιστικά Διακομιστή δεν υποστηρίζουν τύπο ελέγχου ταυτότητας 'none'.", - "load": "Φόρτωση", - "systemInfo": "Πληροφορίες Συστήματος", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Λειτουργικό Σύστημα", - "kernel": "Πυρήνας", - "seconds": "δευτερόλεπτα", - "networkInterfaces": "Διεπαφές Δικτύου", - "noInterfacesFound": "Δεν βρέθηκαν διεπαφές δικτύου", - "noProcessesFound": "Δεν βρέθηκαν διεργασίες", - "loginStats": "Στατιστικά Σύνδεσης SSH", - "noRecentLoginData": "Δεν υπάρχουν πρόσφατα δεδομένα σύνδεσης", - "executingQuickAction": "Εκτέλεση {{name}}...", - "quickActionSuccess": "{{name}} ολοκληρώθηκε με επιτυχία", - "quickActionFailed": "{{name}} απέτυχε", - "quickActionError": "Απέτυχε η εκτέλεση του {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Θύρες Ακρόασης", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Θύρα", - "address": "Διεύθυνση", - "process": "Διεργασία", - "noData": "Δεν υπάρχουν δεδομένα θυρών ακρόασης" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Τείχος Προστασίας", - "inactive": "Ανενεργό", - "policy": "Πολιτική", - "rules": "κανόνες", - "noData": "Δεν υπάρχουν διαθέσιμα δεδομένα τείχους προστασίας", - "action": "Ενέργεια", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Θύρα", - "source": "Πηγή", - "anywhere": "Οπουδήποτε" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Μέση Φόρτωση", - "swap": "Εναλλαγή", - "architecture": "Αρχιτεκτονική", - "refresh": "Ανανέωση", - "retry": "Επανάληψη" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Αυτοφιλοξενούμενη διαχείριση SSH και απομακρυσμένης επιφάνειας εργασίας", - "loginTitle": "Συνδεθείτε στο Termix", - "registerTitle": "Δημιουργία Λογαριασμού", - "forgotPassword": "Ξεχάσατε Τον Κωδικό Πρόσβασης?", - "rememberMe": "Απομνημόνευση συσκευής για 30 ημέρες (περιλαμβάνει TOTP)", - "noAccount": "Δεν έχετε λογαριασμό?", - "hasAccount": "Έχετε ήδη λογαριασμό?", - "twoFactorAuth": "Έλεγχος Ταυτότητας Δύο Παραγόντων", - "enterCode": "Εισάγετε τον κωδικό επαλήθευσης", - "backupCode": "Ή χρησιμοποιήστε εφεδρικό κωδικό", - "verifyCode": "Επαλήθευση Κωδικού", - "redirectingToApp": "Ανακατεύθυνση στην εφαρμογή...", - "sshAuthenticationRequired": "Απαιτείται Πιστοποίηση SSH", - "sshNoKeyboardInteractive": "Διαδραστική Πιστοποίηση Πληκτρολογίου Μη Διαθέσιμη", - "sshAuthenticationFailed": "Αποτυχία Ταυτοποίησης", - "sshAuthenticationTimeout": "Χρονικό Όριο Ταυτοποίησης", - "sshNoKeyboardInteractiveDescription": "Ο διακομιστής δεν υποστηρίζει έλεγχο ταυτότητας διαδραστικού πληκτρολογίου. Παρακαλώ δώστε τον κωδικό σας ή το SSH κλειδί.", - "sshAuthFailedDescription": "Τα παρεχόμενα διαπιστευτήρια ήταν εσφαλμένα. Παρακαλώ δοκιμάστε ξανά με έγκυρα διαπιστευτήρια.", - "sshTimeoutDescription": "Λήξη χρονικού ορίου προσπάθειας ταυτοποίησης. Παρακαλώ δοκιμάστε ξανά.", - "sshProvideCredentialsDescription": "Παρακαλώ δώστε τα SSH διαπιστευτήρια σας για να συνδεθείτε σε αυτόν το διακομιστή.", - "sshPasswordDescription": "Εισάγετε τον κωδικό πρόσβασης για αυτή τη σύνδεση SSH.", - "sshKeyPasswordDescription": "Αν το SSH κλειδί σας είναι κρυπτογραφημένο, εισάγετε εδώ τη φράση πρόσβασης.", - "passphraseRequired": "Απαιτείται Συνθηματική φράση", - "passphraseRequiredDescription": "Το SSH κλειδί είναι κρυπτογραφημένο. Παρακαλώ εισάγετε τη φράση πρόσβασης για να το ξεκλειδώσετε.", - "back": "Πίσω", - "firstUser": "Πρώτος Χρήστης", - "firstUserMessage": "Είστε ο πρώτος χρήστης και θα γίνει ένας διαχειριστής. Μπορείτε να δείτε τις ρυθμίσεις διαχειριστή στο αναπτυσσόμενο μενού του χρήστη πλαϊνής μπάρας. Αν νομίζετε ότι αυτό είναι λάθος, ελέγξτε τα αρχεία καταγραφής docker ή δημιουργήστε ένα ζήτημα GitHub.", - "external": "Εξωτερικό", - "loginWithExternal": "Συνδεθείτε με τον εξωτερικό πάροχο", - "loginWithExternalDesc": "Συνδεθείτε χρησιμοποιώντας τον καθορισμένο εξωτερικό πάροχο ταυτότητας σας", - "externalNotSupportedInElectron": "Ο εξωτερικός έλεγχος ταυτότητας δεν υποστηρίζεται ακόμα στην εφαρμογή Electron. Παρακαλούμε χρησιμοποιήστε την έκδοση web για σύνδεση OIDC.", - "resetPasswordButton": "Επαναφορά Κωδικού Πρόσβασης", - "sendResetCode": "Αποστολή Κωδικού Επαναφοράς", - "resetCodeDesc": "Εισάγετε το όνομα χρήστη σας για να λάβετε έναν κωδικό επαναφοράς κωδικού. Ο κωδικός θα συνδεθεί στα αρχεία καταγραφής docker container.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Επαλήθευση Κωδικού", - "enterResetCode": "Εισάγετε τον 6ψήφιο κωδικό από τα αρχεία καταγραφής docker container για τον χρήστη:", - "newPassword": "Νέος Κωδικός Πρόσβασης", - "confirmNewPassword": "Επιβεβαίωση Κωδικού Πρόσβασης", - "enterNewPassword": "Εισάγετε τον νέο κωδικό πρόσβασης για το χρήστη:", - "signUp": "Εγγραφή", - "desktopApp": "Εφαρμογή Επιφάνειας Εργασίας", - "loggingInToDesktopApp": "Σύνδεση στην εφαρμογή επιφάνειας εργασίας", - "loadingServer": "Φόρτωση διακομιστή...", - "dataLossWarning": "Η επαναφορά του κωδικού πρόσβασής σας με αυτόν τον τρόπο θα διαγράψει όλα τα αποθηκευμένα SSH hosts, διαπιστευτήρια και άλλα κρυπτογραφημένα δεδομένα. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Χρησιμοποιήστε την μόνο αν έχετε ξεχάσει τον κωδικό σας και δεν είστε συνδεδεμένοι.", - "authenticationDisabled": "Απενεργοποίηση Ταυτοποίησης", - "authenticationDisabledDesc": "Όλες οι μέθοδοι ταυτοποίησης είναι απενεργοποιημένες. Παρακαλώ επικοινωνήστε με το διαχειριστή.", - "attemptsRemaining": "{{count}} προσπάθειες απομένουν" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Επαλήθευση Κλειδιού Διακομιστή SSH", - "keyChangedWarning": "Το Κλειδί Υπολογιστή SSH Άλλαξε", - "firstConnectionTitle": "Πρώτη σύνδεση σε αυτόν τον υπολογιστή", - "firstConnectionDescription": "Η αυθεντικότητα αυτού του κεντρικού υπολογιστή δεν μπορεί να καθοριστεί. Βεβαιωθείτε ότι το δακτυλικό αποτύπωμα ταιριάζει με αυτό που περιμένετε.", - "keyChangedDescription": "Το κλειδί φιλοξενίας αυτού του διακομιστή έχει αλλάξει από την τελευταία σας σύνδεση. Αυτό μπορεί να υποδηλώνει ένα πρόβλημα ασφάλειας.", - "previousKey": "Προηγούμενο Κλειδί", - "newFingerprint": "Νέο Δακτυλικό Αποτύπωμα", - "fingerprint": "Αποτύπωμα", - "verifyInstructions": "Αν εμπιστεύεστε αυτόν τον οικοδεσπότη, κάντε κλικ για να συνεχίσετε και να αποθηκεύσετε αυτό το δακτυλικό αποτύπωμα για μελλοντικές συνδέσεις.", - "securityWarning": "Προειδοποίηση Ασφαλείας", - "acceptAndContinue": "Αποδοχή & Συνέχεια", - "acceptNewKey": "Αποδοχή Νέου Κλειδιού & Συνέχεια" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Δεν ήταν δυνατή η σύνδεση με τη βάση δεδομένων", - "unknownError": "Άγνωστο σφάλμα", - "loginFailed": "Αποτυχία σύνδεσης", - "failedPasswordReset": "Αποτυχία έναρξης επαναφοράς κωδικού πρόσβασης", - "failedVerifyCode": "Αποτυχία επαλήθευσης κωδικού επαναφοράς", - "failedCompleteReset": "Αποτυχία ολοκλήρωσης της επαναφοράς κωδικού πρόσβασης", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Αποτυχία έναρξης σύνδεσης OIDC", - "silentSigninOidcUnavailable": "Ζητήθηκε σιωπηλή σύνδεση, αλλά η σύνδεση OIDC δεν είναι διαθέσιμη.", - "failedUserInfo": "Αποτυχία λήψης πληροφοριών χρήστη μετά τη σύνδεση", - "oidcAuthFailed": "Αποτυχία ταυτοποίησης OIDC", - "invalidAuthUrl": "Μη έγκυρη έγκριση URL λήφθηκε από το σύστημα υποστήριξης", - "requiredField": "Αυτό το πεδίο είναι υποχρεωτικό", - "minLength": "Το ελάχιστο μήκος είναι {{min}}", - "passwordMismatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν", - "passwordLoginDisabled": "Το όνομα χρήστη/κωδικός πρόσβασης είναι απενεργοποιημένο", - "sessionExpired": "Η συνεδρία έληξε - παρακαλώ συνδεθείτε ξανά", - "totpRateLimited": "Περιορισμένος ρυθμός: Πάρα πολλές προσπάθειες επαλήθευσης TOTP. Παρακαλώ δοκιμάστε ξανά αργότερα.", - "totpRateLimitedWithTime": "Περιορισμένος ρυθμός: Πάρα πολλές προσπάθειες επαλήθευσης TOTP. Παρακαλώ περιμένετε {{time}} δευτερόλεπτα πριν προσπαθήσετε ξανά.", - "resetCodeRateLimited": "Περιορισμένη βαθμολογία: Πάρα πολλές προσπάθειες επαλήθευσης. Παρακαλώ δοκιμάστε ξανά αργότερα.", - "resetCodeRateLimitedWithTime": "Περιορισμένος ρυθμός: Πάρα πολλές προσπάθειες επαλήθευσης. Παρακαλώ περιμένετε {{time}} δευτερόλεπτα πριν προσπαθήσετε ξανά.", - "authTokenSaveFailed": "Αποτυχία αποθήκευσης διακριτικού ταυτοποίησης", - "failedToLoadServer": "Αποτυχία φόρτωσης διακομιστή" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Νέα εγγραφή λογαριασμού απενεργοποιείται από έναν διαχειριστή. Παρακαλούμε συνδεθείτε ή επικοινωνήστε με το διαχειριστή.", - "userNotAllowed": "Ο λογαριασμός σας δεν είναι εξουσιοδοτημένος να εγγραφεί. Επικοινωνήστε με το διαχειριστή.", - "databaseConnectionFailed": "Αποτυχία σύνδεσης με το διακομιστή βάσης δεδομένων", - "resetCodeSent": "Η επαναφορά κωδικού στάλθηκε στα αρχεία καταγραφής Docker", - "codeVerified": "Επιτυχής επαλήθευση κωδικού", - "passwordResetSuccess": "Επιτυχής επαναφορά κωδικού πρόσβασης", - "loginSuccess": "Επιτυχής σύνδεση", - "registrationSuccess": "Επιτυχής εγγραφή" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Τοπικές σήραγγες επιφάνειας εργασίας με στόχο ρυθμισμένους SSH υπολογιστές.", - "c2sTunnelPresets": "Προεπιλογές Tunnel Πελάτη", - "c2sTunnelPresetsDesc": "Αποθήκευση της τοπικής λίστας διοχέτευσης αυτού του υπολογιστή-πελάτη γραφείου ως μια καθορισμένη προεπιλογή διακομιστή ή φόρτωση μιας προκαθορισμένης ρύθμισης σε αυτόν τον υπολογιστή-πελάτη.", - "c2sTunnelPresetsUnavailable": "Οι προεπιλογές διοχέτευσης πελάτη είναι διαθέσιμες μόνο στην επιφάνεια εργασίας προγράμματος-πελάτη.", - "c2sPresetName": "Προκαθορισμένο Όνομα", - "c2sPresetNamePlaceholder": "Προκαθορισμένο όνομα πελάτη", - "c2sPresetToLoad": "Προκαθορισμένο Για Φόρτωση", - "c2sNoPresetSelected": "Δεν επιλέχθηκε προεπιλογή", - "c2sNoPresets": "Δεν αποθηκεύτηκαν προεπιλογές", - "c2sLoadPreset": "Φόρτωση", - "c2sCurrentLocalConfig": "{{count}} τοπική(ές) διοχέτευση(εις) πελάτη που έχουν ρυθμιστεί σε αυτήν την επιφάνεια εργασίας.", - "c2sPresetSyncNote": "Οι προεπιλογές είναι σαφείς στιγμιότυπα. Η φόρτωση μιας αντικαθιστά την τοπική λίστα tunnel πελάτη της επιφάνειας εργασίας.", - "c2sPresetSaved": "Η προκαθορισμένη ρύθμιση δρομολόγησης πελάτη αποθηκεύτηκε", - "c2sPresetLoaded": "Τοπικά φορτισμένη σήραγγα πελάτη", - "c2sPresetRenamed": "Η προκαθορισμένη ρύθμιση δρομολόγησης πελάτη μετονομάστηκε", - "c2sPresetDeleted": "Η προκαθορισμένη ρύθμιση δρομολόγησης πελάτη διαγράφηκε", - "c2sPresetLoadError": "Αποτυχία φόρτωσης προεπιλογών tunnel πελάτη" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Γλώσσα", - "keyPassword": "κωδικός πρόσβασης", - "pastePrivateKey": "Επικολλήστε το ιδιωτικό σας κλειδί εδώ...", - "localListenerHost": "127.0.0.1 (ακούστε τοπικά)", - "localTargetHost": "127.0.0.1 (στόχος σε αυτόν τον υπολογιστή)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Εισάγετε τον κωδικό σας", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Ταμπλό", - "loading": "Φόρτωση ταμπλό...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Υποστήριξη", + "support": "Support", "discord": "Discord", - "serverOverview": "Επισκόπηση Διακομιστή", - "version": "Έκδοση", - "upToDate": "Έως την ημερομηνία", - "updateAvailable": "Διαθέσιμη Ενημέρωση", - "beta": "Βήτα", - "uptime": "Χρόνος", - "database": "Βάση Δεδομένων", - "healthy": "Υγιείς", - "error": "Σφάλμα", - "totalHosts": "Σύνολο Υπολογιστών", - "totalTunnels": "Σύνολο Σηράγγων", - "totalCredentials": "Σύνολο Διαπιστευτηρίων", - "recentActivity": "Πρόσφατη Δραστηριότητα", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Φόρτωση πρόσφατης δραστηριότητας...", - "noRecentActivity": "Καμία πρόσφατη δραστηριότητα", - "quickActions": "Γρήγορες Ενέργειες", - "addHost": "Προσθήκη Διακομιστή", - "addCredential": "Προσθήκη Διαπιστευτηρίου", - "adminSettings": "Ρυθμίσεις Διαχειριστή", - "userProfile": "Προφίλ Χρήστη", - "serverStats": "Στατιστικά Διακομιστή", - "loadingServerStats": "Φόρτωση στατιστικών διακομιστή...", - "noServerData": "Δεν υπάρχουν διαθέσιμα δεδομένα διακομιστή", - "cpu": "Επεξεργαστής", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Προσαρμογή Πίνακα Ελέγχου", - "dashboardSettings": "Ρυθμίσεις Ταμπλό", - "enableDisableCards": "Ενεργοποίηση/Απενεργοποίηση Καρτών", - "resetLayout": "Επαναφορά προεπιλογών", - "serverOverviewCard": "Επισκόπηση Διακομιστή", - "recentActivityCard": "Πρόσφατη Δραστηριότητα", - "networkGraphCard": "Γράφημα Δικτύου", - "networkGraph": "Γράφημα Δικτύου", - "quickActionsCard": "Γρήγορες Ενέργειες", - "serverStatsCard": "Στατιστικά Διακομιστή", - "panelMain": "Κύρια", - "panelSide": "Πλευρά", - "justNow": "μόλις τώρα" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { "stable": "STABLE", - "hostsOnline": "Συνδεδεμένοι Υπολογιστές", - "activeTunnels": "Ενεργές Σηράξεις", - "registerNewServer": "Εγγραφή νέου διακομιστή", - "storeSshKeysOrPasswords": "Αποθήκευση SSH κλειδιών ή κωδικών πρόσβασης", - "manageUsersAndRoles": "Διαχείριση χρηστών και ρόλων", - "manageYourAccount": "Διαχείριση του λογαριασμού σας", - "hostStatus": "Κατάσταση Υπολογιστή", - "noHostsConfigured": "Δεν έχουν ρυθμιστεί υπολογιστές", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", - "onlineLower": "Συνδεδεμένος", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Προσθέστε:", - "commandPalette": "Παλέτα Εντολών", - "done": "Ολοκληρώθηκε", - "editModeInstructions": "Σύρετε τις κάρτες για να αναδιατάξετε · Σύρετε το διαχωριστικό στήλης για να αλλάξετε το μέγεθος των στηλών · Σύρετε το κάτω άκρο ενός φύλλου για να αλλάξετε το μέγεθος του · Απορρίμματα για να αφαιρέσετε", - "empty": "Κενό", - "clear": "Εκκαθάριση" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Προσθήκη Διακομιστή", - "addGroup": "Προσθήκη Ομάδας", - "addLink": "Προσθήκη Συνδέσμου", - "zoomIn": "Μεγέθυνση", - "zoomOut": "Σμίκρυνση", - "resetView": "Επαναφορά Προβολής", - "selectHost": "Επιλογή Διακομιστή", - "chooseHost": "Επιλογή κεντρικού υπολογιστή...", - "parentGroup": "Γονική Ομάδα", - "noGroup": "Καμία Ομάδα", - "groupName": "Όνομα Ομάδας", - "color": "Χρώμα", - "source": "Πηγή", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Μετακίνηση στην ομάδα", - "selectGroup": "Επιλογή ομάδας...", - "addConnection": "Προσθήκη Σύνδεσης", - "hostDetails": "Λεπτομέρειες Υπολογιστή", - "removeFromGroup": "Αφαίρεση από την ομάδα", - "addHostHere": "Προσθήκη Υπολογιστή Εδώ", - "editGroup": "Επεξεργασία Ομάδας", - "delete": "Διαγραφή", - "add": "Προσθήκη", - "create": "Δημιουργία", - "move": "Μετακίνηση", - "connect": "Σύνδεση", - "createGroup": "Δημιουργία Ομάδας", - "selectSourcePlaceholder": "Επιλογή Πηγής...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Μη Έγκυρο Αρχείο", - "hostAlreadyExists": "Ο οικοδεσπότης βρίσκεται ήδη στην τοπολογία", - "connectionExists": "Η σύνδεση υπάρχει ήδη", - "unknown": "Άγνωστο", - "name": "Όνομα", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Κατάσταση", - "failedToAddNode": "Αποτυχία προσθήκης κόμβου", - "sourceDifferentFromTarget": "Η πηγή και ο στόχος πρέπει να είναι διαφορετικοί", - "exportJSON": "Εξαγωγή JSON", - "importJSON": "Εισαγωγή JSON", - "terminal": "Τερματικό", - "fileManager": "Διαχειριστής Αρχείων", - "tunnel": "Σήραγγα", - "docker": "Προσάρτηση", - "serverStats": "Στατιστικά Διακομιστή", - "noNodes": "Δεν υπάρχουν κόμβοι ακόμα" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Το Docker δεν είναι ενεργοποιημένο για αυτόν τον υπολογιστή", - "validating": "Επικύρωση Docker...", - "connecting": "Σύνδεση...", - "error": "Σφάλμα", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Αποτυχία σύνδεσης στο Docker", - "containerStarted": "Το κοντέινερ {{name}} ξεκίνησε", - "failedToStartContainer": "Αποτυχία εκκίνησης κοντέινερ {{name}}", - "containerStopped": "Το κοντέινερ {{name}} σταμάτησε", - "failedToStopContainer": "Αποτυχία διακοπής του κοντέινερ {{name}}", - "containerRestarted": "Container {{name}} επανεκκινήθηκε", - "failedToRestartContainer": "Απέτυχε η επανεκκίνηση του container {{name}}", - "containerPaused": "Το κοντέινερ {{name}} τέθηκε σε παύση", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", "containerUnpaused": "Container {{name}} unpaused", - "failedToTogglePauseContainer": "Αποτυχία εναλλαγής κατάστασης παύσης για το κοντέινερ {{name}}", - "containerRemoved": "Το κοντέινερ {{name}} αφαιρέθηκε", - "failedToRemoveContainer": "Αποτυχία κατάργησης του κοντέινερ {{name}}", - "image": "Εικόνα", - "ports": "Θύρες", - "noPorts": "Δεν υπάρχουν θύρες", - "start": "Έναρξη", - "confirmRemoveContainer": "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε το δοχείο '{{name}}'? Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", - "runningContainerWarning": "Προειδοποίηση: Αυτό το δοχείο εκτελείται αυτή τη στιγμή. Η αφαίρεση του θα σταματήσει πρώτα τον περιέκτη.", - "loadingContainers": "Φόρτωση εμπορευματοκιβωτίων...", - "manager": "Διαχειριστής Προσάρτησης", - "autoRefresh": "Αυτόματη Ανανέωση", - "timestamps": "Χρονοσφραγίδες", - "lines": "Γραμμές", - "filterLogs": "Φιλτράρισμα αρχείων καταγραφής...", - "refresh": "Ανανέωση", - "download": "Λήψη", - "clear": "Εκκαθάριση", - "logsDownloaded": "Τα αρχεία καταγραφής κατέβηκαν επιτυχώς", - "last50": "Τελευταίο 50", - "last100": "Τελευταία 100", - "last500": "Τελευταίο 500", - "last1000": "Τελευταίο 1000", - "allLogs": "Όλα Τα Αρχεία Καταγραφής", - "noLogsMatching": "Δεν υπάρχουν αρχεία καταγραφής που να ταιριάζουν \"{{query}}\"", - "noLogsAvailable": "Δεν υπάρχουν διαθέσιμα αρχεία καταγραφής", - "noContainersFound": "Δεν βρέθηκαν εμπορευματοκιβώτια", - "noContainersFoundHint": "Δεν υπάρχουν διαθέσιμα δοχεία Docker σε αυτόν τον υπολογιστή", - "searchPlaceholder": "Αναζήτηση εμπορευματοκιβωτίων...", - "allStatuses": "Όλες Οι Καταστάσεις", - "stateRunning": "Εκτελείται", - "statePaused": "Παύση", - "stateExited": "Έξοδος", - "stateRestarting": "Επανεκκίνηση", - "noContainersMatchFilters": "Κανένα δοχείο δεν ταιριάζει με τα φίλτρα σας", - "noContainersMatchFiltersHint": "Δοκιμάστε να προσαρμόσετε τα κριτήρια αναζήτησης ή φίλτρου", - "failedToFetchStats": "Αποτυχία λήψης στατιστικών στοιχείων εμπορευματοκιβωτίων", - "containerNotRunning": "Ο περιέκτης δεν εκτελείται", - "startContainerToViewStats": "Εκκίνηση του περιέκτη για προβολή στατιστικών", - "loadingStats": "Φόρτωση στατιστικών...", - "errorLoadingStats": "Σφάλμα φόρτωσης στατιστικών", - "noStatsAvailable": "Δεν υπάρχουν διαθέσιμα στατιστικά στοιχεία", - "cpuUsage": "Χρήση CPU", - "current": "Τρέχων", - "memoryUsage": "Χρήση Μνήμης", - "networkIo": "Δίκτυο I/O", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Έξοδος", - "blockIo": "Αποκλεισμός I/O", - "read": "Ανάγνωση", - "write": "Εγγραφή", - "pids": "PID", - "containerInformation": "Πληροφορίες Περιέκτη", - "name": "Όνομα", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Κατάσταση", - "containerMustBeRunning": "Ο περιέκτης πρέπει να εκτελείται για πρόσβαση στην κονσόλα", - "verificationCodePrompt": "Εισάγετε τον κωδικό επαλήθευσης", - "totpVerificationFailed": "Η επαλήθευση TOTP απέτυχε. Παρακαλώ προσπαθήστε ξανά.", - "warpgateVerificationFailed": "Η ταυτοποίηση Warpgate απέτυχε. Παρακαλώ προσπαθήστε ξανά.", - "connectedTo": "Συνδεδεμένο με {{containerName}}", - "disconnected": "Αποσυνδέθηκε", - "consoleError": "Σφάλμα κονσόλας", - "errorMessage": "Σφάλμα: {{message}}", - "failedToConnect": "Αποτυχία σύνδεσης με τον περιέκτη", - "console": "Κονσόλα", - "selectShell": "Επιλογή κελύφους", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "τέφρα", - "connect": "Σύνδεση", - "disconnect": "Αποσύνδεση", - "notConnected": "Δεν συνδέθηκε", - "clickToConnect": "Κάντε κλικ στη σύνδεση για να ξεκινήσετε μια συνεδρία κελύφους", - "connectingTo": "Σύνδεση με {{containerName}}...", - "containerNotFound": "Ο περιέκτης δεν βρέθηκε", - "backToList": "Πίσω στη Λίστα", - "logs": "Καταγραφή", - "stats": "Στατιστικά", - "consoleTab": "Κονσόλα", - "startContainerToAccess": "Εκκίνηση του περιέκτη για πρόσβαση στην κονσόλα" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Γενικά", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Χρήστες", - "sectionSessions": "Συνεδρίες", - "sectionRoles": "Ρόλοι", - "sectionDatabase": "Βάση Δεδομένων", - "sectionApiKeys": "Κλειδιά API", - "allowRegistration": "Επιτρέψτε Την Εγγραφή Χρήστη", - "allowRegistrationDesc": "Να επιτρέπεται στους νέους χρήστες η αυτο-εγγραφή", - "allowPasswordLogin": "Να Επιτρέπεται Η Σύνδεση Κωδικού Πρόσβασης", - "allowPasswordLoginDesc": "Όνομα χρήστη/κωδικός πρόσβασης", - "oidcAutoProvision": "Αυτόματη Παροχή OIDC", - "oidcAutoProvisionDesc": "Αυτόματη δημιουργία λογαριασμών για χρήστες OIDC ακόμη και όταν η εγγραφή είναι απενεργοποιημένη", - "allowPasswordReset": "Επιτρέπεται Επαναφορά Κωδικού Πρόσβασης", - "allowPasswordResetDesc": "Επαναφορά κώδικα μέσω αρχείων καταγραφής Docker", - "sessionTimeout": "Χρονικό Όριο Συνεδρίας", - "hours": "ώρες", - "sessionTimeoutRange": "Ελάχιστη 1h · Ανώτατο όριο 720h", - "monitoringDefaults": "Προεπιλογές Παρακολούθησης", - "statusCheck": "Έλεγχος Κατάστασης", - "metrics": "Μετρικές", - "sec": "δευτ", - "logLevel": "Επίπεδο Καταγραφής", - "enableGuacamole": "Ενεργοποίηση Guacamole", - "enableGuacamoleDesc": "Απομακρυσμένη επιφάνεια εργασίας RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Οι ρυθμίσεις σύνδεσης OpenID για SSO. Τα πεδία που σημειώνονται * είναι υποχρεωτικά.", - "oidcClientId": "Ταυτότητα Πελάτη", - "oidcClientSecret": "Μυστικό Πελάτη", - "oidcAuthUrl": "URL Εξουσιοδότησης", - "oidcIssuerUrl": "Url Εκδότη", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "Token URL", - "oidcUserIdentifier": "Διαδρομή Αναγνωριστικού Χρήστη", - "oidcDisplayName": "Εμφανιζόμενο Όνομα Διαδρομής", - "oidcScopes": "Εμβέλεια", - "oidcUserinfoUrl": "Παράκαμψη URL Πληροφορίας Χρήστη", - "oidcAllowedUsers": "Επιτρεπόμενοι Χρήστες", - "oidcAllowedUsersDesc": "Ένα email ανά γραμμή. Αφήστε κενό για να επιτρέψετε σε όλους.", - "removeOidc": "Αφαίρεση", - "usersCount": "{{count}} χρήστες", - "createUser": "Δημιουργία", - "newRole": "Νέος Ρόλος", - "roleName": "Όνομα", - "roleDisplayName": "Εμφανιζόμενο Όνομα", - "roleDescription": "Περιγραφή", - "rolesCount": "{{count}} ρόλοι", - "createRole": "Δημιουργία", - "creating": "Δημιουργία...", - "exportDatabase": "Εξαγωγή Βάσης Δεδομένων", - "exportDatabaseDesc": "Κατεβάστε ένα αντίγραφο ασφαλείας όλων των hosts, διαπιστευτήρια και ρυθμίσεις", - "export": "Εξαγωγή", - "exporting": "Εξαγωγή...", - "importDatabase": "Εισαγωγή Βάσης Δεδομένων", - "importDatabaseDesc": "Επαναφορά από ένα αρχείο αντιγράφου ασφαλείας .sqlite", - "importDatabaseSelected": "Επιλεγμένα: {{name}}", - "selectFile": "Επιλογή Αρχείου", - "changeFile": "Αλλαγή", - "import": "Εισαγωγή", - "importing": "Εισαγωγή...", - "apiKeysCount": "{{count}} πλήκτρα", - "newApiKey": "Νέο Κλειδί Api", - "apiKeyCreatedWarning": "Το κλειδί δημιουργήθηκε - αντιγράψτε το τώρα, δεν θα εμφανιστεί ξανά.", - "apiKeyName": "Όνομα", - "apiKeyUser": "Χρήστης", - "apiKeySelectUser": "Επιλέξτε έναν χρήστη...", - "apiKeyExpiresAt": "Λήγει Στις", - "createKey": "Δημιουργία Κλειδιού", - "apiKeyNoExpiry": "Καμία λήξη", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Διπλή Πιστοποίηση", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Τοπικό", - "adminStatusAdministrator": "Διαχειριστής", - "adminStatusRegularUser": "Κανονικός Χρήστης", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "ΤΕΛΩΝΕΙΟ", - "youBadge": "ΣΑΣ", - "sessionsActive": "{{count}} ενεργό", - "sessionActive": "Ενεργό: {{time}}", - "sessionExpires": "Λήξη: {{time}}", - "revokeAll": "Όλα", - "revokeAllSessionsSuccess": "Όλες οι συνεδρίες για τον χρήστη ανακλήθηκαν", - "revokeAllSessionsFailed": "Αποτυχία ανάκλησης συνεδριών", - "revokeSessionFailed": "Αποτυχία ανάκλησης συνεδρίας", - "addRole": "Προσθήκη ρόλου", - "noCustomRoles": "Δεν έχουν οριστεί προσαρμοσμένοι ρόλοι", - "removeRoleFailed": "Αποτυχία κατάργησης ρόλου", - "assignRoleFailed": "Αποτυχία αντιστοίχισης ρόλου", - "deleteRoleFailed": "Αποτυχία διαγραφής ρόλου", - "userAdminAccess": "Διαχειριστής", - "userAdminAccessDesc": "Πλήρης πρόσβαση σε όλες τις ρυθμίσεις διαχειριστή", - "userRoles": "Ρόλοι", - "revokeAllUserSessions": "Ανάκληση Όλων Των Συνεδριών", - "revokeAllUserSessionsDesc": "Αναγκαστική επανασύνδεση σε όλες τις συσκευές", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Η διαγραφή αυτού του χρήστη είναι μόνιμη.", - "deleteUser": "Διαγραφή {{username}}", - "deleting": "Διαγραφή...", - "deleteUserFailed": "Αποτυχία διαγραφής χρήστη", - "deleteUserSuccess": "Χρήστης \"{{username}}\" διαγράφηκε", - "deleteRoleSuccess": "Ο ρόλος \"{{name}}\" διαγράφηκε", - "revokeKeySuccess": "Το κλειδί \"{{name}}\" ανακλήθηκε", - "revokeKeyFailed": "Αποτυχία ανάκλησης κλειδιού", - "copiedToClipboard": "Αντιγράφηκε στο πρόχειρο", - "done": "Ολοκληρώθηκε", - "createUserTitle": "Δημιουργία Χρήστη", - "createUserDesc": "Δημιουργία νέου τοπικού λογαριασμού.", - "createUserUsername": "Όνομα Χρήστη", - "createUserPassword": "Κωδικός", - "createUserPasswordHint": "Τουλάχιστον 6 χαρακτήρες.", - "createUserEnterUsername": "Εισάγετε όνομα χρήστη", - "createUserEnterPassword": "Εισάγετε κωδικό πρόσβασης", - "createUserSubmit": "Δημιουργία Χρήστη", - "editUserTitle": "Διαχείριση χρήστη: {{username}}", - "editUserDesc": "Επεξεργασία ρόλων, κατάστασης διαχειριστή, συνεδριών και ρυθμίσεων λογαριασμού.", - "editUserUsername": "Όνομα Χρήστη", - "editUserAuthType": "Τύπος Πιστοποίησης", - "editUserAdminStatus": "Κατάσταση Διαχειριστή", - "editUserUserId": "Id Χρήστη", - "linkAccountTitle": "Σύνδεση OIDC σε λογαριασμό κωδικού πρόσβασης", - "linkAccountDesc": "Συγχώνευση του λογαριασμού OIDC {{username}} με έναν υπάρχοντα τοπικό λογαριασμό.", - "linkAccountWarningTitle": "Αυτό θα:", - "linkAccountEffect1": "Διαγραφή του λογαριασμού OIDC μόνο", - "linkAccountEffect2": "Προσθήκη σύνδεσης OIDC στον λογαριασμό προορισμού", - "linkAccountEffect3": "Επιτρέπεται η σύνδεση OIDC και κωδικού πρόσβασης", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Εισάγετε το όνομα τοπικού λογαριασμού για σύνδεση", - "linkAccounts": "Σύνδεση Λογαριασμών", - "linkAccountSuccess": "Λογαριασμός OIDC συνδεδεμένος με \"{{username}}\"", - "linkAccountFailed": "Αποτυχία σύνδεσης λογαριασμού OIDC", - "linkAccountInProgress": "Σύνδεση...", - "saving": "Αποθηκεύεται...", - "updateRegistrationFailed": "Αποτυχία ενημέρωσης της ρύθμισης εγγραφής", - "updatePasswordLoginFailed": "Αποτυχία ενημέρωσης της ρύθμισης σύνδεσης κωδικού πρόσβασης", - "updateOidcAutoProvisionFailed": "Αποτυχία ενημέρωσης της ρύθμισης OIDC αυτόματης παροχής", - "updatePasswordResetFailed": "Αποτυχία ενημέρωσης της ρύθμισης επαναφοράς κωδικού πρόσβασης", - "sessionTimeoutRange2": "Το χρονικό όριο συνεδρίας πρέπει να είναι μεταξύ 1 και 720 ωρών", - "sessionTimeoutSaved": "Χρονικό όριο συνεδρίας αποθηκεύτηκε", - "sessionTimeoutSaveFailed": "Αποτυχία αποθήκευσης χρονικού ορίου συνεδρίας", - "monitoringIntervalInvalid": "Μη έγκυρες τιμές διαστήματος", - "monitoringSaved": "Οι ρυθμίσεις παρακολούθησης αποθηκεύτηκαν", - "monitoringSaveFailed": "Αποτυχία αποθήκευσης των ρυθμίσεων παρακολούθησης", - "guacamoleSaved": "Οι ρυθμίσεις Guacamole αποθηκεύτηκαν", - "guacamoleSaveFailed": "Αποτυχία αποθήκευσης ρυθμίσεων Guacamole", - "guacamoleUpdateFailed": "Αποτυχία ενημέρωσης της ρύθμισης Guacamole", - "logLevelUpdateFailed": "Αποτυχία ενημέρωσης επιπέδου καταγραφής", - "oidcSaved": "Οι ρυθμίσεις OIDC αποθηκεύτηκαν", - "oidcSaveFailed": "Αποτυχία αποθήκευσης ρυθμίσεων OIDC", - "oidcRemoved": "Η διαμόρφωση OIDC αφαιρέθηκε", - "oidcRemoveFailed": "Αποτυχία κατάργησης ρυθμίσεων OIDC", - "createUserRequired": "Απαιτείται όνομα χρήστη και κωδικός πρόσβασης", - "createUserPasswordTooShort": "Ο κωδικός πρόσβασης πρέπει να περιέχει τουλάχιστον 6 χαρακτήρες", - "createUserSuccess": "Χρήστης \"{{username}}\" δημιουργήθηκε", - "createUserFailed": "Αποτυχία δημιουργίας χρήστη", - "updateAdminStatusFailed": "Αποτυχία ενημέρωσης κατάστασης διαχειριστή", - "allSessionsRevoked": "Όλες οι συνεδρίες ανακλήθηκαν", - "revokeSessionsFailed": "Αποτυχία ανάκλησης συνεδριών", - "createRoleRequired": "Όνομα και εμφανιζόμενο όνομα απαιτούνται", - "createRoleSuccess": "Ο ρόλος \"{{name}}\" δημιουργήθηκε", - "createRoleFailed": "Αποτυχία δημιουργίας ρόλου", - "apiKeyNameRequired": "Απαιτείται όνομα κλειδιού", - "apiKeyUserRequired": "Απαιτείται ID χρήστη", - "apiKeyCreatedSuccess": "API κλειδί \"{{name}}\" δημιουργήθηκε", - "apiKeyCreateFailed": "Αποτυχία δημιουργίας κλειδιού API", - "exportSuccess": "Η βάση δεδομένων εξήχθη επιτυχώς", - "exportFailed": "Η εξαγωγή βάσης δεδομένων απέτυχε", - "importSelectFile": "Παρακαλώ επιλέξτε πρώτα ένα αρχείο", - "importCompleted": "Η εισαγωγή ολοκληρώθηκε: {{total}} στοιχεία εισήχθησαν, {{skipped}} παραλείφθηκαν", - "importFailed": "Η εισαγωγή απέτυχε: {{error}}", - "importError": "Η εισαγωγή βάσης δεδομένων απέτυχε" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Διακομιστής", - "hostPlaceholder": "192.168.1.1 ή example.com", - "portLabel": "Θύρα", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Όνομα Χρήστη", - "usernamePlaceholder": "όνομα χρήστη", - "authLabel": "Πιστοποίηση", - "passwordLabel": "Κωδικός", - "passwordPlaceholder": "κωδικός", - "privateKeyLabel": "Ιδιωτικό Κλειδί", - "privateKeyPlaceholder": "Επικόλληση ιδιωτικού κλειδιού...", - "credentialLabel": "Διαπιστευτήριο", - "credentialPlaceholder": "Επιλέξτε ένα αποθηκευμένο διαπιστευτήριο", - "connectToTerminal": "Σύνδεση στο Τερματικό", - "connectToFiles": "Σύνδεση σε αρχεία" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Δεν επιλέχθηκε τερματικό", - "noTerminalSelectedHint": "Άνοιγμα μιας καρτέλας τερματικού SSH για προβολή του ιστορικού εντολών", - "searchPlaceholder": "Αναζήτηση ιστορικού...", - "clearAll": "Εκκαθάριση Όλων", - "noHistoryEntries": "Δεν υπάρχουν καταχωρήσεις ιστορικού", - "trackingDisabled": "Η παρακολούθηση ιστορικού είναι απενεργοποιημένη", - "trackingDisabledHint": "Ενεργοποιήστε το στις ρυθμίσεις προφίλ σας για την εγγραφή εντολών." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Εγγραφή Πλήκτρων", - "recordToTerminals": "Εγγραφή σε τερματικά", - "selectAll": "Όλα", - "selectNone": "Κανένα", - "noTerminalTabsOpen": "Δεν έχουν ανοίξει καρτέλες τερματικού", - "selectTerminalsAbove": "Επιλέξτε τερματικά πάνω", - "broadcastInputPlaceholder": "Πληκτρολογήστε εδώ για να μεταδώσετε τα πλήκτρα...", - "stopRecording": "Διακοπή Εγγραφής", - "startRecording": "Έναρξη Εγγραφής", - "settingsTitle": "Ρυθμίσεις", - "enableRightClickCopyPaste": "Ενεργοποίηση αντιγραφής/επικόλλησης με δεξί κλικ" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Διάταξη", - "selectLayoutAbove": "Επιλέξτε μια διάταξη παραπάνω", - "selectLayoutHint": "Επιλέξτε πόσα παράθυρα θα εμφανίζονται", - "panesTitle": "Πίνακες", - "openTabsTitle": "Άνοιγμα Καρτελών", - "dragTabsHint": "Σύρετε καρτέλες στα παραπάνω παράθυρα ή χρησιμοποιήστε τη Γρήγορη αντιστοίχιση", - "dropHere": "Ρίξτε εδώ", - "emptyPane": "Κενό", - "dashboard": "Ταμπλό", - "clearSplitScreen": "Καθαρισμός Διαίρεσης Οθόνης", - "quickAssign": "Γρήγορη ανάθεση", - "alreadyAssigned": "Παράθυρο {{index}}", - "splitTab": "Διαίρεση καρτέλας", - "addToSplit": "Προσθήκη στο Split", - "removeFromSplit": "Αφαίρεση από το Σπλιτ", - "assignToPane": "Ανάθεση σε παράθυρο" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Δημιουργία Δείγματος", - "createSnippetDescription": "Δημιουργήστε ένα νέο στέλεχος εντολών για γρήγορη εκτέλεση", - "nameLabel": "Όνομα", - "namePlaceholder": "π.χ. Επανεκκίνηση Nginx", - "descriptionLabel": "Περιγραφή", - "descriptionPlaceholder": "Προαιρετική περιγραφή", - "optional": "Προαιρετικό", - "folderLabel": "Φάκελος", - "noFolder": "Χωρίς φάκελο (Μη κατηγοριοποιημένο)", - "commandLabel": "Εντολή", - "commandPlaceholder": "π.χ. sudo systemctl restart nginx", - "cancel": "Ακύρωση", - "createSnippetButton": "Δημιουργία Δείγματος", - "createFolderTitle": "Δημιουργία Φακέλου", - "createFolderDescription": "Οργανώστε τα αποσπάσματα σας σε φακέλους", - "folderNameLabel": "Όνομα Φακέλου", - "folderNamePlaceholder": "π.χ., Εντολές Συστήματος, Σενάρια Docker", - "folderColorLabel": "Χρώμα Φακέλου", - "folderIconLabel": "Εικονίδιο Φακέλου", - "previewLabel": "Προεπισκόπηση", - "folderNameFallback": "Όνομα Φακέλου", - "createFolderButton": "Δημιουργία Φακέλου", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Όλα", - "selectNone": "Κανένα", - "noTerminalTabsOpen": "Δεν έχουν ανοίξει καρτέλες τερματικού", - "searchPlaceholder": "Αναζήτηση αποσπασμάτων ...", - "newSnippet": "Νέο Δείγμα", - "newFolder": "Νέος Φάκελος", - "run": "Εκτέλεση", - "noSnippetsInFolder": "Δεν υπάρχουν αποσπάσματα σε αυτόν το φάκελο", - "uncategorized": "Αταξινόμητο", - "editSnippetTitle": "Επεξεργασία Δείγματος", - "editSnippetDescription": "Ενημερώστε αυτό το τμήμα εντολών", - "saveSnippetButton": "Αποθήκευση Αλλαγών", - "createSuccess": "Δείγμα δημιουργήθηκε με επιτυχία", - "createFailed": "Αποτυχία δημιουργίας αποσπάσματος", - "updateSuccess": "Δείγμα ενημερώθηκε με επιτυχία", - "updateFailed": "Αποτυχία ενημέρωσης αποσπάσματος", - "deleteFailed": "Αποτυχία διαγραφής αποσπάσματος", - "folderCreateSuccess": "Ο φάκελος δημιουργήθηκε με επιτυχία", - "folderCreateFailed": "Αποτυχία δημιουργίας φακέλου", - "editFolderTitle": "Επεξεργασία Φακέλου", - "editFolderDescription": "Μετονομασία ή αλλαγή εμφάνισης αυτού του φακέλου", - "saveFolderButton": "Αποθήκευση Αλλαγών", - "editFolder": "Επεξεργασία φακέλου", - "deleteFolder": "Διαγραφή φακέλου", - "folderDeleteSuccess": "Φάκελος \"{{name}}\" διαγράφηκε", - "folderDeleteFailed": "Αποτυχία διαγραφής φακέλου", - "folderEditSuccess": "Ο φάκελος ενημερώθηκε επιτυχώς", - "folderEditFailed": "Αποτυχία ενημέρωσης φακέλου", - "confirmRunMessage": "Εκτέλεση \"{{name}}\";", - "confirmRunButton": "Τρέξιμο", - "runSuccess": "Ran \"{{name}}\" σε τερματικό(ά) {{count}}", - "copySuccess": "Αντιγράφηκε το \"{{name}}\" στο πρόχειρο", - "shareTitle": "Μοιραστείτε Δείγμα", - "shareUser": "Χρήστης", - "shareRole": "Ρόλος", - "selectUser": "Επιλέξτε έναν χρήστη...", - "selectRole": "Επιλέξτε ένα ρόλο...", - "shareSuccess": "Το Snippet κοινοποιήθηκε με επιτυχία", - "shareFailed": "Αποτυχία κοινοποίησης αποσπάσματος", - "revokeSuccess": "Η πρόσβαση ανακλήθηκε", - "revokeFailed": "Αποτυχία ανάκλησης πρόσβασης", - "currentAccess": "Τρέχουσα Πρόσβαση", - "shareLoadError": "Αποτυχία φόρτωσης δεδομένων κοινής χρήσης", - "loading": "Φόρτωση...", - "close": "Κλείσιμο", - "reorderFailed": "Η αποθήκευση της σειράς αποσπάσματος απέτυχε" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Λογαριασμός", - "sectionAppearance": "Εμφάνιση", - "sectionSecurity": "Ασφάλεια", - "sectionApiKeys": "Κλειδιά API", - "sectionC2sTunnels": "C2S Σήραγγες", - "usernameLabel": "Όνομα Χρήστη", - "roleLabel": "Ρόλος", - "roleAdministrator": "Διαχειριστής", - "authMethodLabel": "Μέθοδος Πιστοποίησης", - "authMethodLocal": "Τοπικό", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Ενεργό", - "twoFaOff": "Ανενεργό", - "versionLabel": "Έκδοση", - "deleteAccount": "Διαγραφή Λογαριασμού", - "deleteAccountDescription": "Μόνιμη διαγραφή λογαριασμού σας", - "deleteButton": "Διαγραφή", - "deleteAccountPermanent": "Αυτή η ενέργεια είναι μόνιμη και δεν μπορεί να αναιρεθεί.", - "deleteAccountWarning": "Όλες οι συνεδρίες, οι υπολογιστές, τα διαπιστευτήρια και οι ρυθμίσεις θα διαγραφούν οριστικά.", - "confirmPasswordDeletePlaceholder": "Εισάγετε τον κωδικό πρόσβασής σας για επιβεβαίωση", - "languageLabel": "Γλώσσα", - "themeLabel": "Θέμα", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Χρώμα Έμφασης", - "settingsTerminal": "Τερματικό", - "commandAutocomplete": "Αυτόματη Ολοκλήρωση Εντολής", - "commandAutocompleteDesc": "Εμφάνιση αυτόματης συμπλήρωσης κατά την πληκτρολόγηση", - "historyTracking": "Παρακολούθηση Ιστορικού", - "historyTrackingDesc": "Παρακολούθηση εντολών τερματικού", - "syntaxHighlighting": "Επισήμανση Σύνταξης", - "syntaxHighlightingDesc": "Επισήμανση εξόδου τερματικού", - "commandPalette": "Παλέτα Εντολών", - "commandPaletteDesc": "Ενεργοποίηση συντόμευσης πληκτρολογίου", - "reopenTabsOnLogin": "Επανάνοιγμα καρτελών κατά τη σύνδεση", - "reopenTabsOnLoginDesc": "Επαναφέρετε τις ανοιχτές καρτέλες σας όταν συνδέεστε ή ανανεώνετε τη σελίδα, ακόμα και από άλλη συσκευή", - "confirmTabClose": "Επιβεβαίωση Κλεισίματος Καρτέλας", - "confirmTabCloseDesc": "Ερώτηση πριν το κλείσιμο καρτελών τερματικού", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Εμφάνιση Ετικετών Υπολογιστή", - "showHostTagsDesc": "Εμφάνιση ετικετών στη λίστα κεντρικών υπολογιστών", - "hostTrayOnClick": "Κάντε κλικ για να αναπτύξετε τις Ενέργειες κεντρικού υπολογιστή", - "hostTrayOnClickDesc": "Να εμφανίζονται πάντα τα κουμπιά σύνδεσης. Κάντε κλικ για να αναπτύξετε τις επιλογές διαχείρισης αντί να τοποθετήσετε τον δείκτη του ποντικιού.", - "pinAppRail": "Καρφίτσωμα εφαρμογής Rail", - "pinAppRailDesc": "Διατηρήστε το αριστερό πλευρικό πλαίσιο εφαρμογής πάντα ανοιχτό αντί να επεκτείνεται κατά την τοποθέτηση του δείκτη του ποντικιού", - "settingsSnippets": "Δείγματα", - "foldersCollapsed": "Φάκελοι Που Απορρίφθηκαν", - "foldersCollapsedDesc": "Σύμπτυξη φακέλων από προεπιλογή", - "confirmExecution": "Επιβεβαίωση Εκτέλεσης", - "confirmExecutionDesc": "Επιβεβαιώστε πριν εκτελέσετε αποσπάσματα", - "settingsUpdates": "Ενημερώσεις", - "disableUpdateChecks": "Απενεργοποίηση Ελέγχου Ενημέρωσης", - "disableUpdateChecksDesc": "Διακοπή ελέγχου για ενημερώσεις", - "totpAuthenticator": "Πιστοποιητής TOTP", - "totpEnabled": "Το 2FA είναι ενεργοποιημένο", - "totpDisabled": "Προσθέστε επιπλέον ασφάλεια σύνδεσης", - "disable": "Απενεργοποίηση", - "enable": "Ενεργοποίηση", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Σαρώστε τον κωδικό QR ή εισάγετε το μυστικό στην εφαρμογή ελέγχου ταυτότητας και μετά εισάγετε τον 6-ψήφιο κωδικό", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Επαλήθευση", - "changePassword": "Αλλαγή Κωδικού Πρόσβασης", - "currentPasswordLabel": "Τρέχων Κωδικός Πρόσβασης", - "currentPasswordPlaceholder": "Τρέχων κωδικός πρόσβασης", - "newPasswordLabel": "Νέος Κωδικός Πρόσβασης", - "newPasswordPlaceholder": "Νέος κωδικός πρόσβασης", - "confirmPasswordLabel": "Επιβεβαίωση Νέου Κωδικού Πρόσβασης", - "confirmPasswordPlaceholder": "Επιβεβαίωση νέου κωδικού πρόσβασης", - "updatePassword": "Ενημέρωση Κωδικού Πρόσβασης", - "createApiKeyTitle": "Δημιουργία Κλειδιού Api", - "createApiKeyDescription": "Δημιουργήστε ένα νέο κλειδί API για την προγραμματική πρόσβαση.", - "apiKeyNameLabel": "Όνομα", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "προαιρετικό", - "cancel": "Ακύρωση", - "createKey": "Δημιουργία Κλειδιού", - "apiKeyCount": "{{count}} πλήκτρα", - "newKey": "Νέο Κλειδί", - "noApiKeys": "Δεν υπάρχουν ακόμα κλειδιά API.", - "apiKeyActive": "Ενεργό", - "apiKeyUsageHint": "Συμπεριλάβετε το κλειδί σας στο", - "apiKeyUsageHintHeader": "κεφαλίδα.", - "apiKeyPermissionsHint": "Τα κλειδιά κληρονομούν τα δικαιώματα του δημιουργού χρήστη.", - "roleUser": "Χρήστης", - "authMethodDual": "Διπλή Πιστοποίηση", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Αποτυχία έναρξης εγκατάστασης TOTP", - "totpEnter6Digits": "Εισάγετε έναν 6-ψήφιο κωδικό", - "totpEnabledSuccess": "Έλεγχος ταυτότητας δύο παραγόντων ενεργοποιήθηκε", - "totpInvalidCode": "Μη έγκυρος κωδικός, παρακαλώ προσπαθήστε ξανά", - "totpDisableInputRequired": "Εισάγετε τον κωδικό TOTP ή τον κωδικό πρόσβασης", - "totpDisabledSuccess": "Ο έλεγχος ταυτότητας δύο παραγόντων απενεργοποιήθηκε", - "totpDisableFailed": "Αποτυχία απενεργοποίησης του 2FA", - "totpDisableTitle": "Απενεργοποίηση 2FA", - "totpDisablePlaceholder": "Εισάγετε τον κωδικό TOTP ή τον κωδικό πρόσβασης", - "totpDisableConfirm": "Απενεργοποίηση 2FA", - "totpContinueVerify": "Συνεχίστε στην Επαλήθευση", - "totpVerifyTitle": "Επαλήθευση Κωδικού", - "totpBackupTitle": "Κωδικοί Αντιγράφων Ασφαλείας", - "totpDownloadBackup": "Λήψη Εφεδρικών Κωδικών", - "done": "Ολοκληρώθηκε", - "secretCopied": "Το μυστικό αντιγράφηκε στο πρόχειρο", - "apiKeyNameRequired": "Απαιτείται όνομα κλειδιού", - "apiKeyCreated": "API κλειδί \"{{name}}\" δημιουργήθηκε", - "apiKeyCreateFailed": "Αποτυχία δημιουργίας κλειδιού API", - "apiKeyUser": "Χρήστης", - "apiKeyExpires": "Λήγει", - "apiKeyRevoked": "API κλειδί \"{{name}}\" ανακλήθηκε", - "apiKeyRevokeFailed": "Αποτυχία ανάκλησης κλειδιού API", - "passwordFieldsRequired": "Απαιτούνται τρέχοντες και νέοι κωδικοί πρόσβασης", - "passwordMismatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν", - "passwordTooShort": "Ο κωδικός πρόσβασης πρέπει να περιέχει τουλάχιστον 6 χαρακτήρες", - "passwordUpdated": "Ο κωδικός πρόσβασης ενημερώθηκε επιτυχώς", - "passwordUpdateFailed": "Αποτυχία ενημέρωσης κωδικού πρόσβασης", - "deletePasswordRequired": "Απαιτείται κωδικός πρόσβασης για να διαγράψετε το λογαριασμό σας", - "deleteFailed": "Αποτυχία διαγραφής λογαριασμού", - "deleting": "Διαγραφή...", - "colorPickerTooltip": "Άνοιγμα επιλογέα χρώματος", - "themeSystem": "Σύστημα", - "themeLight": "Φωτεινό", - "themeDark": "Σκοτεινό", - "themeDracula": "Δράκουλας", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Ολοκληρωμένο", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Ένα Σκοτεινό", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/es_ES.json b/src/ui/locales/translated/es_ES.json index 9db0a1b4..9bc5af32 100644 --- a/src/ui/locales/translated/es_ES.json +++ b/src/ui/locales/translated/es_ES.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Carpetas", - "folder": "Carpeta", - "password": "Contraseña", - "key": "Clave", - "sshPrivateKey": "Clave privada SSH", - "upload": "Subir", - "keyPassword": "Contraseña de clave", - "sshKey": "Clave SSH", - "uploadPrivateKeyFile": "Subir archivo de clave privada", - "searchCredentials": "Buscar credenciales...", - "addCredential": "Añadir credencial", - "caCertificate": "Certificado CA (-cert.pub)", - "caCertificateDescription": "Opcional: Cargue o pegue el archivo de certificado firmado por CA (por ejemplo, id_ed25519-cert.pub). Requerido cuando su servidor SSH utiliza la autorización basada en certificados.", - "uploadCertFile": "Subir archivo -cert.pub", - "clearCert": "Claro", - "certLoaded": "Certificado cargado", - "certPublicKeyLabel": "Certificado CA", - "certTypeLabel": "Tipo de certificado", - "pasteOrUploadCert": "Pegar o subir un certificado -cert.pub...", - "hasCaCert": "Tiene Certificado CA", - "noCaCert": "No hay Certificado CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Error al cargar las alertas", - "failedToDismissAlert": "Error al descartar la alerta" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configuración del servidor", - "description": "Configure la URL del servidor Termix para conectarse a sus servicios de backend", - "serverUrl": "URL del servidor", - "enterServerUrl": "Por favor, introduzca una URL del servidor", - "saveFailed": "Error al guardar la configuración", - "saveError": "Error al guardar la configuración", - "saving": "Guardando...", - "saveConfig": "Guardar configuración", - "helpText": "Introduzca la URL en la que se ejecuta su servidor Termix (por ejemplo, http://localhost:30001 o https://your-server.com)", - "changeServer": "Cambiar servidor", - "mustIncludeProtocol": "La URL del servidor debe comenzar con http:// o https://", - "allowInvalidCertificate": "Permitir certificado no válido", - "allowInvalidCertificateDesc": "Utilizar únicamente en servidores autogestionados de confianza con certificados autofirmados o basados en direcciones IP.", - "useEmbedded": "Usar servidor local", - "embeddedDesc": "Ejecutar Termix con el servidor local integrado (no se necesita un servidor remoto)", - "embeddedConnecting": "Conectando con el servidor local...", - "embeddedNotReady": "El servidor local aún no está listo. Por favor, espere un momento y vuelva a intentarlo.", - "localServer": "Servidor local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Error de comprobación de versión", - "checkFailed": "Error al comprobar actualizaciones", - "upToDate": "La aplicación está actualizada", - "currentVersion": "Estás ejecutando la versión {{version}}", - "updateAvailable": "Actualización disponible", - "newVersionAvailable": "¡Una nueva versión está disponible! Estás usando {{current}}, pero {{latest}} está disponible.", - "betaVersion": "Versión beta", - "betaVersionDesc": "Está ejecutando {{current}}, que es más reciente que la última versión estable {{latest}}.", - "releasedOn": "Liberado en {{date}}", - "downloadUpdate": "Descargar actualización", - "checking": "Buscando actualizaciones...", - "checkUpdates": "Buscar actualizaciones", - "checkingUpdates": "Buscando actualizaciones...", - "updateRequired": "Actualización requerida" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Cerrar", + "close": "Close", "minimize": "Minimize", - "online": "En línea", - "offline": "Desconectado", - "continue": "Continuar", - "maintenance": "Mantenimiento", - "degraded": "Degrado", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", "error": "Error", - "warning": "Advertencia", - "unsavedChanges": "Cambios sin guardar", - "dismiss": "Descartar", - "loading": "Cargando...", - "optional": "Opcional", - "connect": "Conectar", - "copied": "Copiado", - "connecting": "Conectando...", - "updateAvailable": "Actualización disponible", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Abrir en nueva pestaña", - "noReleases": "No hay lanzamientos", - "updatesAndReleases": "Actualizaciones y Publicaciones", - "newVersionAvailable": "Una nueva versión ({{version}}) está disponible.", - "failedToFetchUpdateInfo": "Error al obtener información de actualización", - "preRelease": "Pre-lanzamiento", - "noReleasesFound": "No se encontraron lanzamientos.", - "cancel": "Cancelar", - "username": "Usuario", - "login": "Ingresar", - "register": "Registrarse", - "password": "Contraseña", - "confirmPassword": "Confirmar contraseña", - "back": "Atrás", - "save": "Guardar", - "saving": "Guardando...", - "delete": "Eliminar", - "rename": "Renombrar", - "edit": "Editar", - "add": "Añadir", - "confirm": "Confirmar", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", "no": "No", - "or": "O", - "next": "Siguiente", - "previous": "Anterior", - "refresh": "Recargar", - "language": "Idioma", - "checking": "Comprobando...", - "checkingDatabase": "Comprobando conexión a la base de datos...", - "checkingAuthentication": "Comprobando autenticación...", - "backendReconnected": "Conexión con el servidor restaurada", - "connectionDegraded": "Se perdió la conexión del servidor, recuperando…", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Eliminar", - "create": "Crear", - "update": "Actualizar", - "copy": "Copiar", - "copyFailed": "Error al copiar al portapapeles", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Restaurar", - "of": "de" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Inicio", + "home": "Home", "terminal": "Terminal", - "docker": "Doctor", - "tunnels": "Túneles", - "fileManager": "Gestor de archivos", - "serverStats": "Estadísticas del Servidor", - "admin": "Administrador", - "userProfile": "Perfil de usuario", - "splitScreen": "Dividir pantalla", - "confirmClose": "¿Cerrar esta sesión activa?", - "close": "Cerrar", - "cancel": "Cancelar", - "sshManager": "Gestor SSH", - "cannotSplitTab": "No se puede dividir esta pestaña", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copiar contraseña", - "copySudoPassword": "Copiar contraseña de Sudo", - "passwordCopied": "Contraseña copiada al portapapeles", - "noPasswordAvailable": "Contraseña no disponible", - "failedToCopyPassword": "Error al copiar la contraseña", - "refreshTab": "Actualizar conexión", - "openFileManager": "Abrir gestor de archivos", - "dashboard": "Tablero", - "networkGraph": "Gráfico de red", - "quickConnect": "Conexión rápida", - "sshTools": "Herramientas SSH", - "history": "Historial", - "hosts": "Anfitriones", - "snippets": "Fragmentos", - "hostManager": "Gestor de Anfitriones", - "credentials": "Credenciales", - "connections": "Conexiones", - "roleAdministrator": "Administrador", - "roleUser": "Usuario" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Anfitriones", - "noHosts": "No hay hosts SSH", - "retry": "Reintentar", - "refresh": "Refrescar", - "optional": "Opcional", - "downloadSample": "Descargar muestra", - "failedToDeleteHost": "Error al eliminar {{name}}", - "importSkipExisting": "Importar (saltar existente)", - "connectionDetails": "Detalles de la conexión", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Escritorio remoto", - "port": "Puerto", - "username": "Usuario", - "folder": "Carpeta", - "tags": "Etiquetas", - "pin": "Fijar", - "addHost": "Añadir Host", - "editHost": "Editar Host", - "cloneHost": "Clonar Host", - "enableTerminal": "Activar Terminal", - "enableTunnel": "Activar túnel", - "enableFileManager": "Habilitar gestor de archivos", - "enableDocker": "Activar Docker", - "defaultPath": "Ruta por defecto", - "connection": "Conexión", - "upload": "Subir", - "authentication": "Autenticación", - "password": "Contraseña", - "key": "Clave", - "credential": "Credencial", - "none": "Ninguna", - "sshPrivateKey": "Clave privada SSH", - "keyType": "Tipo de clave", - "uploadFile": "Subir archivo", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Túneles", - "tabDocker": "Doctor", - "tabFiles": "Archivos", - "tabStats": "Estadísticas", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Compartiendo", - "tabAuthentication": "Autenticación", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Túnel", - "fileManager": "Gestor de archivos", - "serverStats": "Estadísticas del Servidor", - "status": "Estado", - "folderRenamed": "Carpeta \"{{oldName}}\" renombrada a \"{{newName}}\" correctamente", - "failedToRenameFolder": "Error al renombrar la carpeta", - "movedToFolder": "Movido a \"{{folder}}\"", - "editHostTooltip": "Editar host", - "statusChecks": "Comprobaciones de estado", - "metricsCollection": "Colección de métricas", - "metricsInterval": "Intervalo de recolección de métricas", - "metricsIntervalDesc": "Con qué frecuencia recolectar estadísticas del servidor (5s - 1h)", - "behavior": "Comportamiento", - "themePreview": "Vista previa del tema", - "theme": "Tema", - "fontFamily": "Familia de fuente", - "fontSize": "Tamaño de fuente", - "letterSpacing": "Espacio de letras", - "lineHeight": "Altura de línea", - "cursorStyle": "Estilo del cursor", - "cursorBlink": "Bloque de cursor", - "scrollbackBuffer": "Búfer de desplazamiento", - "bellStyle": "Estilo de Campanilla", - "rightClickSelectsWord": "Haga clic derecho selecciona palabra", - "fastScrollModifier": "Modificador de Desplazamiento Rápido", - "fastScrollSensitivity": "Sensibilidad de desplazamiento rápido", - "sshAgentForwarding": "Desvío del agente SSH", - "backspaceMode": "Modo Backspace", - "startupSnippet": "Fragmento de inicio", - "selectSnippet": "Seleccionar fragmento", - "forceKeyboardInteractive": "Forzar interactivo del teclado", - "overrideCredentialUsername": "Reemplazar nombre de usuario de credenciales", - "overrideCredentialUsernameDesc": "Utilice el nombre de usuario especificado arriba en lugar del nombre de usuario de la credencial", - "jumpHostChain": "Cadena de Host de Salto", - "portKnocking": "Puesta de puerto", - "addKnock": "Añadir Puerto", - "addProxyNode": "Añadir Nodo", - "proxyNode": "Nodo proxy", - "proxyType": "Tipo de proxy", - "quickActions": "Acciones rápidas", - "sudoPasswordAutoFill": "Auto-relleno de contraseña de Sudo", - "sudoPassword": "Contraseña Sudo", - "keepaliveInterval": "Intervalo Keepalive (ms)", - "moshCommand": "MOSH comando", - "environmentVariables": "Variables de entorno", - "addVariable": "Añadir variable", - "docker": "Doctor", - "copyTerminalUrl": "Copiar URL de terminal", - "copyFileManagerUrl": "Copiar URL del gestor de archivos", - "copyRemoteDesktopUrl": "Copiar URL de escritorio remoto", - "failedToConnect": "Error al conectar a la consola", - "connect": "Conectar", - "disconnect": "Desconectar", - "start": "Comenzar", - "enableStatusCheck": "Activar verificación de estado", - "enableMetrics": "Activar métricas", - "bulkUpdateFailed": "Actualización masiva fallida", - "selectAll": "Seleccionar todo", - "deselectAll": "Deseleccionar todo", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Caparazón seguro", - "virtualNetwork": "Red virtual", - "unencryptedShell": "Shell sin cifrar", - "addressIp": "Dirección / IP", - "friendlyName": "Nombre amigable", - "folderAndAdvanced": "Carpeta & Avanzada", - "privateNotes": "Notas privadas", - "privateNotesPlaceholder": "Detalles sobre este servidor...", - "pinToTop": "Fijar arriba", - "pinToTopDesc": "Mostrar siempre este host en la parte superior de la lista", - "portKnockingSequence": "Secuencia de golpe de puerto", - "addKnockBtn": "Añadir Knock", - "noPortKnocking": "No se ha configurado ningún golpe de puerto.", - "knockPort": "Puerto Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Retardo Después de (ms)", - "useSocks5Proxy": "Usar proxy SOCKS5", - "useSocks5ProxyDesc": "Conexión de ruta a través de un servidor proxy", - "proxyHost": "Host del proxy", - "proxyPort": "Puerto proxy", - "proxyUsername": "Usuario del proxy", - "proxyPassword": "Contraseña del proxy", - "proxySingleMode": "Proxy único", - "proxyChainMode": "Cadena de proxy", - "you": "Tú", - "jumpHostChainLabel": "Cadena de Host de Salto", - "addJumpBtn": "Añadir Salto", - "noJumpHosts": "No hay hosts configurados.", - "selectAServer": "Seleccione un servidor...", - "sshPort": "Puerto SSH", - "authMethod": "Método Auth", - "storedCredential": "Credencial almacenada", - "selectACredential": "Seleccione una credencial...", - "keyTypeLabel": "Tipo de clave", - "keyTypeAuto": "Auto detectar", - "keyPasteTab": "Pegar", - "keyUploadTab": "Subir", - "keyFileLoaded": "Archivo de clave cargado", - "keyUploadClick": "Haga clic para subir .pem / .key / .ppk", - "clearKey": "Borrar tecla", - "keySaved": "Clave SSH guardada", - "keyReplaceNotice": "pegar una nueva clave abajo para reemplazarla", - "keyPassphraseSaved": "Contraseña guardada, escriba para cambiar", - "replaceKey": "Reemplazar clave", - "forceKeyboardInteractiveLabel": "Forzar interactiva del teclado", - "forceKeyboardInteractiveShortDesc": "Forzar entrada de contraseña manual incluso si las claves están presentes", - "terminalAppearance": "Apariencia Terminal", - "colorTheme": "Tema de color", - "fontFamilyLabel": "Familia de fuente", - "fontSizeLabel": "Tamaño de fuente", - "cursorStyleLabel": "Estilo del cursor", - "letterSpacingPx": "Espacio de letras (px)", - "lineHeightLabel": "Altura de línea", - "bellStyleLabel": "Estilo de Campanilla", - "backspaceModeLabel": "Modo Backspace", - "cursorBlinking": "Parpadeo de cursor", - "cursorBlinkingDesc": "Activar animación parpadeante para el cursor de la terminal", - "rightClickSelectsWordLabel": "Clic derecho selecciona palabra", - "rightClickSelectsWordShortDesc": "Seleccione la palabra bajo el cursor al hacer clic derecho", - "behaviorAndAdvanced": "Comportamiento & Avanzado", - "scrollbackBufferLabel": "Búfer de desplazamiento", - "scrollbackMaxLines": "Número máximo de líneas conservadas en el historial", - "sshAgentForwardingLabel": "Desvío del agente SSH", - "sshAgentForwardingShortDesc": "Pasa tus claves SSH locales a este host", - "enableAutoMosh": "Activar autoMosh", - "enableAutoMoshDesc": "Preferir Mosh sobre SSH si está disponible", - "enableAutoTmux": "Activar Auto-Tmux", - "enableAutoTmuxDesc": "Iniciar o adjuntar automáticamente a la sesión de tmux", - "sudoPasswordAutoFillLabel": "Auto-relleno de contraseña de Sudo", - "sudoPasswordAutoFillShortDesc": "Proporcionar automáticamente una contraseña sudo cuando se le solicite", - "sudoPasswordLabel": "Contraseña Sudo", - "environmentVariablesLabel": "Variables de entorno", - "addVariableBtn": "Añadir variable", - "noEnvVars": "No hay variables de entorno configuradas.", - "fastScrollModifierLabel": "Modificador de Desplazamiento Rápido", - "fastScrollSensitivityLabel": "Sensibilidad de desplazamiento rápido", - "moshCommandLabel": "Mosh Comando", - "startupSnippetLabel": "Fragmento de inicio", - "keepaliveIntervalLabel": "Intervalo de mantenimiento de conexión (segundos)", - "maxKeepaliveMisses": "Misiones máximas de Keepalive", - "tunnelSettings": "Configuración del túnel", - "enableTunneling": "Activar túnel", - "enableTunnelingDesc": "Habilitar la funcionalidad de túnel SSH para este host", - "serverTunnelsSection": "Túneles de servidor", - "addTunnelBtn": "Añadir túnel", - "noTunnelsConfigured": "No hay túneles configurados.", - "tunnelLabel": "Túnel {{number}}", - "tunnelType": "Tipo de túnel", - "tunnelModeLocalDesc": "Reenviar un puerto local a un puerto en el servidor remoto (o un host accesible desde él).", - "tunnelModeRemoteDesc": "Reenviar un puerto en el servidor remoto a un puerto local de su máquina.", - "tunnelModeDynamicDesc": "Crear un proxy SOCKS5 en un puerto local para reenvío de puertos dinámicos.", - "sameHost": "Este host (túnel directo)", - "endpointHost": "Host de punto final", - "endpointPort": "Puerto final", - "bindHost": "Anfitrión de enlace", - "sourcePort": "Puerto de origen", - "maxRetries": "Reintentos máximos", - "retryIntervalS": "Intervalo de Reintentar (s)", - "autoStartLabel": "Autoiniciar", - "autoStartDesc": "Conectar automáticamente este túnel cuando se carga el host", - "tunnelConnecting": "Túnel conectando...", - "tunnelDisconnected": "Túnel desconectado", - "failedToConnectTunnel": "Error al conectar", - "failedToDisconnectTunnel": "Error al desconectar", - "dockerIntegration": "Integración Docker", - "enableDockerMonitor": "Activar Docker", - "enableDockerMonitorDesc": "Controlar y administrar contenedores en este host a través de Docker", - "enableFileManagerMonitor": "Habilitar gestor de archivos", - "enableFileManagerMonitorDesc": "Navegar y administrar archivos en este host a través de SFTP", - "defaultPathLabel": "Ruta por defecto", - "fileManagerPathHint": "El directorio a abrir cuando el gestor de archivos se inicia para este host.", - "statusChecksLabel": "Comprobaciones de estado", - "enableStatusChecks": "Activar comprobaciones de estado", - "enableStatusChecksDesc": "Haciendo ping periódicamente a este host para verificar disponibilidad", - "useGlobalInterval": "Usar intervalo global", - "useGlobalIntervalDesc": "Anular con el intervalo de verificación de estado del servidor", - "checkIntervalS": "Comprobar intervalo (s)", - "checkIntervalDesc": "Segundos entre cada ping de conectividad", - "metricsCollectionLabel": "Colección de métricas", - "enableMetricsLabel": "Activar métricas", - "enableMetricsDesc": "Recoge CPU, RAM, disco y uso de red desde este host", - "useGlobalMetrics": "Usar intervalo global", - "useGlobalMetricsDesc": "Anular con el intervalo de métricas del servidor", - "metricsIntervalS": "Intervalo de métricas (s)", - "metricsIntervalDesc2": "Segundos entre instantáneas métricas", - "visibleWidgets": "Widgets visibles", - "cpuUsageLabel": "Uso de CPU", - "cpuUsageDesc": "Porcentaje de la CPU, promedios de carga, gráfica de esparkline", - "memoryLabel": "Uso de memoria", - "memoryDesc": "Uso de RAM, intercambio, caché", - "storageLabel": "Uso del disco", - "storageDesc": "Uso de disco por punto de montaje", - "networkLabel": "Interfaces de red", - "networkDesc": "Lista de interfaces y ancho de banda", - "uptimeLabel": "Actualización", - "uptimeDesc": "Tiempo de puesta del sistema y tiempo de arranque", - "systemInfoLabel": "Información del sistema", - "systemInfoDesc": "OS, núcleo, nombre de host, arquitectura", - "recentLoginsLabel": "Entradas recientes", - "recentLoginsDesc": "Eventos de inicio de sesión fallidos y exitosos", - "topProcessesLabel": "Procesos más altos", - "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Puertos de escucha", - "listeningPortsDesc": "Puertos abiertos con proceso y estado", - "firewallLabel": "Cortafuegos", - "firewallDesc": "Firewall, AppArmor, estado SELinux", - "quickActionsLabel": "Acciones rápidas", - "quickActionsToolbar": "Las acciones rápidas aparecen como botones en la barra de herramientas de Server Stats para la ejecución de comandos con un solo clic.", - "noQuickActions": "Aún no hay acciones rápidas.", - "buttonLabel": "Etiqueta del botón", - "selectSnippetPlaceholder": "Seleccionar fragmento...", - "addActionBtn": "Añadir Acción", - "hostSharedSuccessfully": "Servidor compartido correctamente", - "failedToShareHost": "Error al compartir host", - "accessRevoked": "Acceso revocado", - "failedToRevokeAccess": "Error al revocar el acceso", - "cancelBtn": "Cancelar", - "savingBtn": "Guardando...", - "addHostBtn": "Añadir Host", - "hostUpdated": "Host actualizado", - "hostCreated": "Host creado", - "failedToSave": "Error al guardar el host", - "credentialUpdated": "Credencial actualizado", - "credentialCreated": "Creación de credenciales", - "failedToSaveCredential": "Error al guardar la credencial", - "backToHosts": "Volver a hosts", - "backToCredentials": "Volver a las credenciales", - "pinned": "Fijado", - "noHostsFound": "No hay hosts", - "tryDifferentTerm": "Prueba un término diferente", - "addFirstHost": "Añade tu primer host para empezar", - "noCredentialsFound": "No se encontraron credenciales", - "addCredentialBtn": "Añadir credencial", - "updateCredentialBtn": "Actualizar credencial", - "features": "Características", - "noFolder": "(Sin carpeta)", - "deleteSelected": "Eliminar", - "exitSelection": "Salir de la selección", - "importSkip": "Importar (saltar existente)", - "importOverwrite": "Importar (sobrescribir)", - "collapseBtn": "Colapso", - "importExportBtn": "Importar / Exportar", - "hostStatusesRefreshed": "Estados de host actualizados", - "failedToRefreshHosts": "Error al actualizar hosts", - "movedHostTo": "Mover {{host}} a \"{{folder}}\"", - "failedToMoveHost": "Error al mover el host", - "folderRenamedTo": "Carpeta renombrada a \"{{name}}\"", - "deletedFolder": "Carpeta eliminada \"{{name}}\"", - "failedToDeleteFolder": "Error al eliminar la carpeta", - "deleteAllInFolder": "¿Eliminar todos los hosts de \"{{name}}\"? Esto no se puede deshacer.", - "deletedHost": "Eliminado {{name}}", - "copiedToClipboard": "Copiado al portapapeles", - "terminalUrlCopied": "URL de terminal copiada", - "fileManagerUrlCopied": "URL del gestor de archivos copiado", - "tunnelUrlCopied": "URL del túnel copiado", - "dockerUrlCopied": "URL Docker copiada", - "serverStatsUrlCopied": "URL de las estadísticas del servidor copiada", - "rdpUrlCopied": "URL RDP copiada", - "vncUrlCopied": "URL VNC copiada", - "telnetUrlCopied": "URL de Telnet copiada", - "remoteDesktopUrlCopied": "URL de escritorio remoto copiada", - "expandActions": "Ampliar acciones", - "collapseActions": "Acciones de colapso", - "wakeOnLanAction": "Activar la LAN", - "wakeOnLanSuccess": "Paquete mágico enviado a {{name}}", - "wakeOnLanError": "Error al enviar el paquete mágico", - "cloneHostAction": "Clonar Host", - "copyAddress": "Copiar dirección", - "copyLink": "Copiar enlace", - "copyTerminalUrlAction": "Copiar URL de terminal", - "copyFileManagerUrlAction": "Copiar URL del gestor de archivos", - "copyTunnelUrlAction": "Copiar URL del túnel", - "copyDockerUrlAction": "Copiar URL de Docker", - "copyServerStatsUrlAction": "Copiar URL de las estadísticas del servidor", - "copyRdpUrlAction": "Copiar URL RDP", - "copyVncUrlAction": "Copiar URL VNC", - "copyTelnetUrlAction": "Copiar URL de Telnet", - "copyRemoteDesktopUrlAction": "Copiar URL de escritorio remoto", - "deleteCredentialConfirm": "¿Eliminar credencial \"{{name}}\"?", - "deletedCredential": "Eliminado {{name}}", - "deploySSHKeyTitle": "Desplegar clave SSH", - "deployingBtn": "Desplegando...", - "deployBtn": "Desplegar", - "failedToDeployKey": "Error al desplegar la clave", - "deleteHostsConfirm": "¿Eliminar {{count}} host{{plural}}? Esto no se puede deshacer.", - "movedToRoot": "Mover a la raíz", - "failedToMoveHosts": "Error al mover hosts", - "enableTerminalFeature": "Activar Terminal", - "disableTerminalFeature": "Desactivar Terminal", - "enableFilesFeature": "Habilitar archivos", - "disableFilesFeature": "Desactivar Archivos", - "enableTunnelsFeature": "Activar túneles", - "disableTunnelsFeature": "Desactivar túneles", - "enableDockerFeature": "Activar Docker", - "disableDockerFeature": "Desactivar Docker", - "addTagsPlaceholder": "Añadir etiquetas...", - "authDetails": "Detalles de autenticación", - "credType": "Tipo", - "generateKeyPairDesc": "Generar un nuevo par de claves, tanto las claves privadas como las públicas se llenarán automáticamente.", - "generatingKey": "Generando...", - "generateLabel": "Generar {{label}}", - "uploadFileBtn": "Subir archivo", - "keyPassphraseOptional": "Contraseña de clave (opcional)", - "sshPublicKeyOptional": "Clave pública SSH (opcional)", - "publicKeyGenerated": "Clave pública generada", - "failedToGeneratePublicKey": "No se pudo derivar la clave pública", - "publicKeyCopied": "Clave pública copiada", - "keyPairGenerated": "Se generó un par de claves {{label}}", - "failedToGenerateKeyPair": "Error al generar el par de claves", - "searchHostsPlaceholder": "Buscar hosts, direcciones, etiquetas…", - "searchCredentialsPlaceholder": "Buscar credenciales…", - "refreshBtn": "Refrescar", - "addTag": "Añadir etiquetas...", - "deleteConfirmBtn": "Eliminar", - "tunnelRequirementsText": "El servidor SSH debe tener GatewayPorts sí, AllowTcpForwarding sí, y PermitRootLogin yes configurado en src/ssh/sshd_config.", - "deleteHostConfirm": "¿Eliminar \"{{name}}\"?", - "enableAtLeastOneProtocol": "Habilitar al menos un protocolo anterior para configurar la autenticación y la configuración de conexión.", - "keyPassphrase": "Contraseña de clave", - "connectBtn": "Conectar", - "disconnectBtn": "Desconectar", - "basicInformation": "Información básica", - "authDetailsSection": "Detalles de autenticación", - "credTypeLabel": "Tipo", - "hostsTab": "Anfitriones", - "credentialsTab": "Credenciales", - "selectMultiple": "Seleccionar múltiples", - "selectHosts": "Seleccionar hosts", - "connectionLabel": "Conexión", - "authenticationLabel": "Autenticación", - "generateKeyPairTitle": "Generar par de claves", - "generateKeyPairDescription": "Generar un nuevo par de claves, tanto las claves privadas como las públicas se llenarán automáticamente.", - "generateFromPrivateKey": "Generar desde Clave Privada", - "refreshBtn2": "Refrescar", - "exitSelectionTitle": "Salir de la selección", - "exportAll": "Exportar todo", - "addHostBtn2": "Añadir Host", - "addCredentialBtn2": "Añadir credencial", - "checkingHostStatuses": "Comprobando estados de host...", - "pinnedSection": "Fijado", - "hostsExported": "Hosts exportados con éxito", - "exportFailed": "Error al exportar los hosts.", - "sampleDownloaded": "Archivo de ejemplo descargado", - "failedToDeleteCredential2": "Error al eliminar la credencial", - "noFolderOption": "(Sin carpeta)", - "nSelected": "{{count}} seleccionado", - "featuresMenu": "Características", - "moveMenu": "Mover", - "cancelSelection": "Cancelar", - "deployDialogDesc": "Desplegar {{name}} a las llaves autorizadas de un host.", - "targetHostLabel": "Anfitrión objetivo", - "selectHostOption": "Seleccione un host...", - "keyDeployedSuccess": "Clave desplegada con éxito", - "failedToDeployKey2": "Error al desplegar la clave", - "deletedCount": "Equipos {{count}} eliminados", - "failedToDeleteCount": "Error al eliminar hosts {{count}}", - "duplicatedHost": "Duplicado \"{{name}}\"", - "failedToDuplicateHost": "Error al duplicar host", - "updatedCount": "Equipos {{count}} actualizados", - "friendlyNameLabel": "Nombre amigable", - "descriptionLabel": "Descripción", - "loadingHost": "Cargando host...", - "loadingHosts": "Cargando hosts...", - "loadingCredentials": "Cargando credenciales...", - "noHostsYet": "Aún no hay hosts", - "noHostsMatchSearch": "Ningún host coincide con tu búsqueda", - "hostNotFound": "Host no encontrado", - "searchHosts": "Buscar hosts...", - "sortHosts": "Ordenar hosts", - "sortDefault": "Orden predeterminada", - "sortNameAsc": "Nombre (A → Z)", - "sortNameDesc": "Nombre (Z → A)", - "sortIpAsc": "Dirección IP (Asc)", - "sortIpDesc": "Dirección IP (Descripción)", - "sortOnlineFirst": "Primero en línea", - "sortOfflineFirst": "Primero sin conexión", - "sortPinnedFirst": "Fijado primero", - "filterHosts": "Filtrar hosts", - "filterClearAll": "Borrar filtros", - "filterStatusGroup": "Estado", - "filterOnline": "En línea", - "filterOffline": "Desconectado", - "filterPinned": "Fijado", - "filterAuthGroup": "Tipo de autenticación", - "filterAuthPassword": "Contraseña", - "filterAuthKey": "Clave SSH", - "filterAuthCredential": "Credencial", - "filterAuthNone": "Ninguno", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protocolo", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Características", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Gestor de archivos", - "filterFeatureTunnel": "Túnel", - "filterFeatureDocker": "Estibador", - "filterTagsGroup": "Etiquetas", - "shareHost": "Compartir Host", - "shareHostTitle": "Compartir: {{name}}", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Este host debe utilizar una credencial para permitir compartir. Editar el host y asignar una credencial primero." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Conexión", - "authentication": "Autenticación", - "connectionSettings": "Configuración de conexión", - "displaySettings": "Mostrar ajustes", - "audioSettings": "Ajustes de audio", - "rdpPerformance": "Rendimiento RDP", - "deviceRedirection": "Redirección del dispositivo", - "session": "Sesión", - "gateway": "Pasarela", - "remoteApp": "Aplicación remota", - "clipboard": "Portapapeles", - "sessionRecording": "Grabación de sesión", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Ajustes de VNC", - "terminalSettings": "Ajustes de Terminal", - "rdpPort": "Puerto RDP", - "username": "Usuario", - "password": "Contraseña", - "domain": "Dominio", - "securityMode": "Modo de seguridad", - "colorDepth": "Profundidad de color", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Altura", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Redimensionar Método", - "clientName": "Nombre del cliente", - "initialProgram": "Programa inicial", - "serverLayout": "Diseño del servidor", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Puerto de pasarela", - "gatewayUsername": "Usuario de Gateway", - "gatewayPassword": "Contraseña del Gateway", - "gatewayDomain": "Dominio de Gateway", - "remoteAppProgram": "Programa RemoteApp", - "workingDirectory": "Directorio de trabajo", - "arguments": "Argumentos", - "normalizeLineEndings": "Normalizar terminaciones de línea", - "recordingPath": "Ruta de grabación", - "recordingName": "Nombre de grabación", - "macAddress": "Dirección MAC", - "broadcastAddress": "Dirección de Broadcast", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Tiempo de espera (s)", - "driveName": "Nombre de Unidad", - "drivePath": "Ruta de la Unidad", - "ignoreCertificate": "Ignorar certificado", - "ignoreCertificateDesc": "Permitir conexiones a hosts con certificados autofirmados", - "forceLossless": "Forzar sin pérdida", - "forceLosslessDesc": "Forzar la codificación de imágenes sin pérdida (mayor calidad, más ancho de banda)", - "disableAudio": "Desactivar audio", - "disableAudioDesc": "Silenciar todo el audio de la sesión remota", - "enableAudioInput": "Activar entrada de audio (micrófono)", - "enableAudioInputDesc": "Reenviar el micrófono local a la sesión remota", - "wallpaper": "Fondo de pantalla", - "wallpaperDesc": "Mostrar fondo de escritorio (desactivar mejora el rendimiento)", - "theming": "Tema", - "themingDesc": "Habilitar temas y estilos visuales", - "fontSmoothing": "Suavizado de fuente", - "fontSmoothingDesc": "Habilitar el renderizado de tipografía Clear Type", - "fullWindowDrag": "Arrastre de Ventana completa", - "fullWindowDragDesc": "Mostrar el contenido de la ventana mientras se arrastra", - "desktopComposition": "Composición de escritorio", - "desktopCompositionDesc": "Activar efectos de cristal Aero", - "menuAnimations": "Animaciones de menú", - "menuAnimationsDesc": "Habilitar animaciones de diapositivas y de fundido de menú", - "disableBitmapCaching": "Desactivar Caché de Bitmap", - "disableBitmapCachingDesc": "Desactivar caché de mapa de bits (puede ayudar con fallos)", - "disableOffscreenCaching": "Desactivar caché offscreen", - "disableOffscreenCachingDesc": "Desactivar caché offscreen", - "disableGlyphCaching": "Desactivar el caché de glifos", - "disableGlyphCachingDesc": "Desactivar caché de glifos", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Usar pipeline de gráficos remota", - "enablePrinting": "Habilitar impresión", - "enablePrintingDesc": "Redirigir impresoras locales a la sesión remota", - "enableDriveRedirection": "Habilitar redirección de Drive", - "enableDriveRedirectionDesc": "Mapear una carpeta local como una unidad en la sesión remota", - "createDrivePath": "Crear ruta de unidad", - "createDrivePathDesc": "Crear automáticamente la carpeta si no existe", - "disableDownload": "Desactivar descarga", - "disableDownloadDesc": "Evitar descargar archivos de la sesión remota", - "disableUpload": "Desactivar subida", - "disableUploadDesc": "Evitar la subida de archivos a la sesión remota", - "enableTouch": "Activar Toque", - "enableTouchDesc": "Activar reenvío de entrada táctil", - "consoleSession": "Sesión de consola", - "consoleSessionDesc": "Conectar a la consola (sesión 0) en lugar de una nueva sesión", - "sendWolPacket": "Enviar paquete WOL", - "sendWolPacketDesc": "Enviar un paquete mágico para despertar este host antes de conectar", - "disableCopy": "Desactivar copia", - "disableCopyDesc": "Evitar copiar texto de la sesión remota", - "disablePaste": "Desactivar Pegar", - "disablePasteDesc": "Evitar pegar texto en la sesión remota", - "createPathIfMissing": "Crear ruta si falta", - "createPathIfMissingDesc": "Crear automáticamente el directorio de grabación", - "excludeOutput": "Excluir salida", - "excludeOutputDesc": "No grabar salida de pantalla (sólo metadatos)", - "excludeMouse": "Excluir ratón", - "excludeMouseDesc": "No grabar movimientos del ratón", - "includeKeystrokes": "Incluye trazos de teclado", - "includeKeystrokesDesc": "Grabar pulsaciones de teclas crudas además de la salida de pantalla", - "vncPort": "Puerto VNC", - "vncPassword": "Contraseña VNC", - "vncUsernameOptional": "Nombre de usuario (opcional)", - "vncLeaveBlank": "Dejar en blanco si no es necesario", - "cursorMode": "Modo cursor", - "swapRedBlue": "Intercambiar rojo/azul", - "swapRedBlueDesc": "Intercambia los canales de color rojo y azul (corrige algunos problemas de color)", - "readOnly": "Sólo lectura", - "readOnlyDesc": "Ver la pantalla remota sin enviar ninguna entrada", - "telnetPort": "Puerto Telnet", - "terminalType": "Tipo de terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Esquema de color", - "backspaceKey": "Retroceso clave", - "saveHostFirst": "Guardar el host primero.", - "sharingOptionsAfterSave": "Las opciones de compartir están disponibles después de que el host haya sido guardado.", - "sharingLoadError": "Error al cargar los datos compartidos. Comprueba tu conexión e inténtalo de nuevo.", - "shareHostSection": "Compartir Host", - "shareWithUser": "Compartir con el usuario", - "shareWithRole": "Compartir con Rol", - "selectUser": "Seleccionar usuario", - "selectRole": "Seleccionar rol", - "selectUserOption": "Seleccione un usuario...", - "selectRoleOption": "Seleccione un rol...", - "permissionLevel": "Nivel de permisos", - "expiresInHours": "Caduca en (horas)", - "noExpiryPlaceholder": "Dejar vacío para no expirar", - "shareBtn": "Compartir", - "currentAccess": "Acceso actual", - "typeHeader": "Tipo", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Permiso", - "grantedByHeader": "Concedido por", - "expiresHeader": "Caduca", - "noAccessEntries": "Aún no hay entradas de acceso.", - "expiredLabel": "Caducó", - "neverLabel": "Nunca", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Cancelar", - "savingBtn": "Guardando...", - "updateHostBtn": "Actualizar Host", - "addHostBtn": "Añadir Host" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Buscar hosts, comandos o ajustes...", - "quickActions": "Acciones rápidas", - "hostManager": "Gestor de Anfitriones", - "hostManagerDesc": "Gestionar, añadir o editar hosts", - "addNewHost": "Añadir nuevo Host", - "addNewHostDesc": "Registrar un nuevo host", - "adminSettings": "Configuración de Admin", - "adminSettingsDesc": "Configurar preferencias del sistema y usuarios", - "userProfile": "Perfil de usuario", - "userProfileDesc": "Administrar tu cuenta y preferencias", - "addCredential": "Añadir credencial", - "addCredentialDesc": "Guardar claves o contraseñas SSH", - "recentActivity": "Actividad reciente", - "serversAndHosts": "Servidores y hosts", - "noHostsFound": "No se han encontrado hosts que coincidan con \"{{search}}\"", - "links": "Enlaces", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Seleccionar", - "toggleWith": "Cambiar con" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Ninguna pestaña asignada", - "focusedPane": "Panel activo" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Sin conexiones", - "noConnectionsDesc": "Abra una terminal, un administrador de archivos o un escritorio remoto para ver las conexiones aquí.", - "connectedFor": "Conectado para {{duration}}", - "connected": "Conectado", - "disconnected": "Desconectado", - "closeTab": "Cerrar pestaña", - "closeConnection": "Conexión estrecha", - "forgetTab": "Olvidar", - "removeBackground": "Eliminar", - "reconnect": "Reconectar", - "reopenTab": "Reabrir", - "sectionOpen": "Abierto", - "sectionBackground": "Fondo", - "backgroundDesc": "Las sesiones se mantienen activas durante 30 minutos después de la desconexión y se pueden volver a conectar.", - "persisted": "Persistió en segundo plano", - "expiresIn": "Caduca en {{duration}}", - "search": "Buscar conexiones...", - "noSearchResults": "No se encontraron conexiones que coincidan con su búsqueda." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Conectando a la sesión {{type}}...", - "connectionError": "Error de conexión", - "connectionFailed": "Conexión fallida", - "failedToConnect": "Error al obtener el token de conexión", - "hostNotFound": "Host no encontrado", - "noHostSelected": "Ningún host seleccionado", - "reconnect": "Volver a conectar", - "retry": "Reintentar", - "guacdUnavailable": "El servicio de escritorio remoto (guacd) no está disponible. Por favor, asegúrese de que guacd se está ejecutando y es accesible y configurado correctamente en la configuración del administrador.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Ganar + L (Pantalla de bloqueo)", - "winKey": "Clave de Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Cambio", - "win": "Gana", - "stickyActive": "{{key}} (agarrado - click para lanzar)", - "stickyInactive": "{{key}} (click para atajar)", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", "esc": "Escape", "tab": "Tab", - "home": "Inicio", - "end": "Fin", - "pageUp": "Subir página", - "pageDown": "Página abajo", - "arrowUp": "Flecha arriba", - "arrowDown": "Flecha abajo", - "arrowLeft": "Flecha izquierda", - "arrowRight": "Flecha derecha", - "fnToggle": "Teclas de función", - "reconnect": "Volver a conectar sesión", - "collapse": "Colapsar barra de herramientas", - "expand": "Expandir barra de herramientas", - "dragHandle": "Arrastre para reposicionar" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Conectar al Host", - "clear": "Claro", - "paste": "Pegar", - "reconnect": "Volver a conectar", - "connectionLost": "Conexión perdida", - "connected": "Conectado", - "clipboardWriteFailed": "No se pudo copiar al portapapeles. Asegúrese de que la página se sirve sobre HTTPS o localhost.", - "clipboardReadFailed": "Error al leer desde el portapapeles. Asegúrate de que los permisos del portapapeles estén garantizados.", - "clipboardHttpWarning": "Pegar requiere HTTPS. Usar Ctrl+Shift+V o servir Termix sobre HTTPS.", - "unknownError": "Ocurrió un error desconocido", - "websocketError": "Error de conexión WebSocket", - "connecting": "Conectando...", - "noHostSelected": "Ningún host seleccionado", - "reconnecting": "Reconectando... ({{attempt}}/{{max}})", - "reconnected": "Reconectado correctamente", - "tmuxSessionCreated": "sesión tmux creada: {{name}}", - "tmuxSessionAttached": "sesión tmux adjunta: {{name}}", - "tmuxUnavailable": "tmux no está instalado en el host remoto, volviendo a la shell estándar", - "tmuxSessionPickerTitle": "sesiones de tmux", - "tmuxSessionPickerDesc": "Sesiones de tmux existentes encontradas en este host. Seleccione una para reconstruir o crear una nueva sesión.", - "tmuxWindows": "Ventanas", - "tmuxWindowCount": "Ventana {{count}}", - "tmuxAttached": "Clientes adjuntos", - "tmuxAttachedCount": "{{count}} adjunto", - "tmuxLastActivity": "Última actividad", - "tmuxTimeJustNow": "justo ahora", - "tmuxTimeMinutes": "{{count}}hace m", - "tmuxTimeHours": "{{count}}hace h", - "tmuxTimeDays": "{{count}}hace d", - "tmuxCreateNew": "Iniciar nueva sesión", - "tmuxCopyHint": "Ajustar selección y presionar Enter para copiar al portapapeles", - "tmuxDetach": "Separar de la sesión de tmux", - "tmuxDetached": "Separado de la sesión de tmux", - "maxReconnectAttemptsReached": "Máximo de intentos de reconexión alcanzados", - "closeTab": "Cerrar", - "connectionTimeout": "Tiempo de conexión agotado", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Ejecutando {{command}} - {{host}}", - "totpRequired": "Autenticación de dos factores requerida", - "totpCodeLabel": "Código de verificación", - "totpVerify": "Verificar", - "warpgateAuthRequired": "Autenticación de Warpgate requerida", - "warpgateSecurityKey": "Clave de seguridad", - "warpgateAuthUrl": "URL de autenticación", - "warpgateOpenBrowser": "Abrir en el navegador", - "warpgateContinue": "He completado la autenticación", - "opksshAuthRequired": "Autenticación OPKSSH requerida", - "opksshAuthDescription": "Autenticación completa en su navegador para continuar. Esta sesión permanecerá válida durante 24 horas.", - "opksshOpenBrowser": "Abrir navegador para autenticar", - "opksshWaitingForAuth": "Esperando la autenticación en el navegador...", - "opksshAuthenticating": "Procesando autenticación...", - "opksshTimeout": "Se ha agotado el tiempo de autenticación. Por favor, inténtalo de nuevo.", - "opksshAuthFailed": "Error de autenticación. Por favor, comprueba tus credenciales e inténtalo de nuevo.", - "opksshSignInWith": "Iniciar sesión con {{provider}}", - "sudoPasswordPopupTitle": "¿Insertar contraseña?", - "websocketAbnormalClose": "Conexión cerrada inesperadamente. Esto puede deberse a un problema de configuración SSL o proxy inverso. Por favor, compruebe los registros del servidor.", - "connectionLogTitle": "Registro de conexión", - "connectionLogCopy": "Copiar registros al portapapeles", - "connectionLogEmpty": "Aún no hay registros de conexión", - "connectionLogWaiting": "Esperando los registros de conexión...", - "connectionLogCopied": "Registros de conexión copiados al portapapeles", - "connectionLogCopyFailed": "Error al copiar los registros al portapapeles", - "connectionRejected": "Conexión rechazada por el servidor. Por favor, compruebe su autenticación y configuración de red.", - "hostKeyRejected": "Verificación de clave del host SSH rechazada. Conexión cancelada.", - "sessionTakenOver": "La sesión fue abierta en otra pestaña. Volviendo a conectar..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Ningún host seleccionado", - "initializingEditor": "Inicializando editor...", - "file": "Fichero", - "folder": "Carpeta", - "uploadFile": "Subir archivo", - "downloadFile": "Descargar", - "extractArchive": "Extraer archivo", - "extractingArchive": "Extrayendo {{name}}...", - "archiveExtractedSuccessfully": "{{name}} extraído con éxito", - "extractFailed": "Error al extraer", - "compressFile": "Comprimir archivo", - "compressFiles": "Comprimir archivos", - "compressFilesDesc": "Comprimir elementos {{count}} en un archivo", - "archiveName": "Nombre de archivo", - "enterArchiveName": "Introduzca el nombre del archivo...", - "compressionFormat": "Formato de compresión", - "selectedFiles": "Archivos seleccionados", - "andMoreFiles": "y {{count}} más...", - "compress": "Comprimir", - "compressingFiles": "Comprimiendo objetos {{count}} en {{name}}...", - "filesCompressedSuccessfully": "{{name}} creado con éxito", - "compressFailed": "Compresión fallida", - "edit": "Editar", - "preview": "Vista previa", - "previous": "Anterior", - "next": "Siguiente", - "pageXOfY": "Página {{current}} de {{total}}", - "zoomOut": "Apagar", - "zoomIn": "Acercar en", - "newFile": "Nuevo archivo", - "newFolder": "Nueva carpeta", - "rename": "Renombrar", - "uploading": "Subiendo...", - "uploadingFile": "Subiendo {{name}}...", - "fileName": "Nombre del archivo", - "folderName": "Nombre de carpeta", - "fileUploadedSuccessfully": "Archivo \"{{name}}\" subido correctamente", - "failedToUploadFile": "Error al subir el archivo", - "fileDownloadedSuccessfully": "Archivo \"{{name}}\" descargado correctamente", - "failedToDownloadFile": "Error al descargar el archivo", - "fileCreatedSuccessfully": "Archivo \"{{name}}\" creado correctamente", - "folderCreatedSuccessfully": "Carpeta \"{{name}}\" creada correctamente", - "failedToCreateItem": "Error al crear el artículo", - "operationFailed": "Operación {{operation}} fallida para {{name}}: {{error}}", - "failedToResolveSymlink": "Error al resolver el enlace simbólico", - "itemsDeletedSuccessfully": "{{count}} elementos eliminados con éxito", - "failedToDeleteItems": "Error al eliminar elementos", - "sudoPasswordRequired": "Contraseña de administrador requerida", - "enterSudoPassword": "Introduzca la contraseña sudo para continuar con esta operación", - "sudoPassword": "Contraseña Sudo", - "sudoOperationFailed": "Error en la operación Sudo", - "sudoAuthFailed": "Falló la autenticación Sudo", - "dragFilesToUpload": "Borrar archivos aquí para subir", - "emptyFolder": "Esta carpeta está vacía", - "searchFiles": "Buscar archivos...", - "upload": "Subir", - "selectHostToStart": "Seleccione un host para iniciar la gestión de archivos", - "sshRequiredForFileManager": "El gestor de archivos requiere SSH. Este host no tiene SSH habilitado.", - "failedToConnect": "No se pudo conectar a SSH", - "failedToLoadDirectory": "Error al cargar el directorio", - "noSSHConnection": "No hay conexión SSH disponible", - "copy": "Copiar", - "cut": "Cortar", - "paste": "Pegar", - "copyPath": "Copiar ruta", - "copyPaths": "Copiar rutas", - "delete": "Eliminar", - "properties": "Propiedades", - "refresh": "Refrescar", - "downloadFiles": "Descargar archivos {{count}} al navegador", - "copyFiles": "Copiar elementos {{count}}", - "cutFiles": "Cortar objetos {{count}}", - "deleteFiles": "Eliminar elementos {{count}}", - "filesCopiedToClipboard": "{{count}} elementos copiados al portapapeles", - "filesCutToClipboard": "{{count}} elementos cortados al portapapeles", - "pathCopiedToClipboard": "Ruta copiada al portapapeles", - "pathsCopiedToClipboard": "{{count}} rutas copiadas al portapapeles", - "failedToCopyPath": "Error al copiar la ruta al portapapeles", - "movedItems": "Movidos {{count}} objetos", - "failedToDeleteItem": "Error al eliminar el elemento", - "itemRenamedSuccessfully": "{{type}} renombrado con éxito", - "failedToRenameItem": "Error al renombrar el elemento", - "download": "Descargar", - "permissions": "Permisos", - "size": "Tamaño", - "modified": "Modificado", - "path": "Ruta", - "confirmDelete": "¿Está seguro que desea eliminar {{name}}?", - "permissionDenied": "Permiso denegado", - "serverError": "Error del servidor", - "fileSavedSuccessfully": "Archivo guardado correctamente", - "failedToSaveFile": "Error al guardar el archivo", - "confirmDeleteSingleItem": "¿Está seguro que desea eliminar permanentemente \"{{name}}\"?", - "confirmDeleteMultipleItems": "¿Está seguro que desea eliminar permanentemente los elementos {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "¿Estás seguro de que quieres eliminar permanentemente los elementos {{count}} ? Esto incluye carpetas y su contenido.", - "confirmDeleteFolder": "¿Está seguro de que desea eliminar permanentemente la carpeta \"{{name}}\" y todo su contenido?", - "permanentDeleteWarning": "Esta acción no se puede deshacer. El elemento(s) se eliminará permanentemente del servidor.", - "recent": "Recientes", - "pinned": "Fijado", - "folderShortcuts": "Atajos de carpeta", - "failedToReconnectSSH": "Error al volver a conectar la sesión SSH", - "openTerminalHere": "Abrir Terminal Aquí", - "run": "Ejecutar", - "openTerminalInFolder": "Abrir terminal en esta carpeta", - "openTerminalInFileLocation": "Abrir terminal en la ubicación del archivo", - "runningFile": "Ejecutando - {{file}}", - "onlyRunExecutableFiles": "Sólo se pueden ejecutar archivos ejecutables", - "directories": "Directorios", - "removedFromRecentFiles": "Eliminado \"{{name}}\" de archivos recientes", - "removeFailed": "Error al eliminar", - "unpinnedSuccessfully": "No fijado con éxito \"{{name}}\"", - "unpinFailed": "Fallo al desfijar", - "removedShortcut": "Atajo eliminado \"{{name}}\"", - "removeShortcutFailed": "Error al eliminar acceso directo", - "clearedAllRecentFiles": "Limpiado todos los archivos recientes", - "clearFailed": "Error al limpiar", - "removeFromRecentFiles": "Eliminar de archivos recientes", - "clearAllRecentFiles": "Borrar todos los archivos recientes", - "unpinFile": "Desanclar archivo", - "removeShortcut": "Eliminar acceso directo", - "pinFile": "Fijar archivo", - "addToShortcuts": "Añadir a accesos directos", - "pasteFailed": "Pegado fallido", - "noUndoableActions": "No hay acciones deshacerables", - "undoCopySuccess": "Operación de copia inrealizada: Eliminados archivos copiados {{count}}", - "undoCopyFailedDelete": "Deshacer fallido: No se pudo eliminar ningún archivo copiado", - "undoCopyFailedNoInfo": "Deshacer fallido: no se pudo encontrar la información del archivo copiado", - "undoMoveSuccess": "Operación de movimiento inrealizado: Se han movido archivos {{count}} de vuelta a la ubicación original", - "undoMoveFailedMove": "Error al deshacer: no se pudo mover ningún archivo de vuelta", - "undoMoveFailedNoInfo": "Error al deshacer: no se pudo encontrar la información del archivo movido", - "undoDeleteNotSupported": "La operación de eliminación no se puede deshacer: los archivos se han eliminado permanentemente del servidor", - "undoTypeNotSupported": "Tipo de operación de deshacer no soportado", - "undoOperationFailed": "Operación de deshacer fallida", - "unknownError": "Error desconocido", - "confirm": "Confirmar", - "find": "Buscar...", - "replace": "Reemplazar", - "downloadInstead": "Descargar en su lugar", - "keyboardShortcuts": "Atajos de teclado", - "searchAndReplace": "Buscar y reemplazar", - "editing": "Editando", - "search": "Buscar", - "findNext": "Buscar Siguiente", - "findPrevious": "Buscar Anterior", - "save": "Guardar", - "selectAll": "Seleccionar todo", - "undo": "Deshacer", - "redo": "Rehacer", - "moveLineUp": "Mover línea arriba", - "moveLineDown": "Mover línea abajo", - "toggleComment": "Cambiar comentario", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Error al cargar la imagen", - "startTyping": "Comience a escribir...", - "unknownSize": "Tamaño desconocido", - "fileIsEmpty": "El archivo está vacío", - "largeFileWarning": "Advertencia de archivo grande", - "largeFileWarningDesc": "Este archivo tiene un tamaño {{size}} , que puede causar problemas de rendimiento cuando se abre como texto.", - "fileNotFoundAndRemoved": "No se ha encontrado el archivo \"{{name}}\" y se ha eliminado de archivos recientes o anclados", - "failedToLoadFile": "Error al cargar el archivo: {{error}}", - "serverErrorOccurred": "Se ha producido un error en el servidor. Inténtalo de nuevo más tarde.", - "autoSaveFailed": "Error al guardar automáticamente", - "fileAutoSaved": "Archivo guardado automáticamente", - "moveFileFailed": "Error al mover {{name}}", - "moveOperationFailed": "Operación de movimiento fallida", - "canOnlyCompareFiles": "Sólo se pueden comparar dos archivos", - "comparingFiles": "Comparando archivos: {{file1}} y {{file2}}", - "dragFailed": "Operación de arrastre fallida", - "filePinnedSuccessfully": "Archivo \"{{name}}\" anclado correctamente", - "pinFileFailed": "Error al anclar archivo", - "fileUnpinnedSuccessfully": "Archivo \"{{name}}\" sin fijar correctamente", - "unpinFileFailed": "Error al desfijar el archivo", - "shortcutAddedSuccessfully": "Acceso directo a la carpeta \"{{name}}\" añadido correctamente", - "addShortcutFailed": "Error al añadir acceso directo", - "operationCompletedSuccessfully": "{{operation}} {{count}} elementos con éxito", - "operationCompleted": "{{operation}} {{count}} objetos", - "downloadFileSuccess": "Archivo {{name}} descargado correctamente", - "downloadFileFailed": "Descarga fallida", - "moveTo": "Mover a {{name}}", - "diffCompareWith": "Comparación de diferencias con {{name}}", - "dragOutsideToDownload": "Arrastra la ventana exterior para descargar ( archivos{{count}})", - "newFolderDefault": "Carpeta nueva", - "newFileDefault": "Nuevo archivo.txt", - "successfullyMovedItems": "Movidos con éxito {{count}} elementos a {{target}}", - "move": "Mover", - "searchInFile": "Buscar en archivo (Ctrl+F)", - "showKeyboardShortcuts": "Mostrar atajos de teclado", - "startWritingMarkdown": "Empieza a escribir tu contenido de markdown...", - "loadingFileComparison": "Cargando comparación de archivos...", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Comparar", - "sideBySide": "Lado por lado", - "inline": "En línea", - "fileComparison": "Comparación de archivos: {{file1}} vs {{file2}}", - "fileTooLarge": "Archivo demasiado grande: {{error}}", - "sshConnectionFailed": "La conexión SSH ha fallado. Por favor, compruebe su conexión a {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Error al cargar el archivo: {{error}}", - "connecting": "Conectando...", - "connectedSuccessfully": "Conectado correctamente", - "totpVerificationFailed": "Falló la verificación TOTP", - "warpgateVerificationFailed": "Autenticación de Warpgate fallida", - "authenticationFailed": "Autenticación fallida", - "verificationCodePrompt": "Código de verificación:", - "changePermissions": "Cambiar permisos", - "currentPermissions": "Permisos actuales", - "owner": "Propietario", - "group": "Grupo", - "others": "Otros", - "read": "Leer", - "write": "Escribir", - "execute": "Ejecutar", - "permissionsChangedSuccessfully": "Permisos modificados con éxito", - "failedToChangePermissions": "Error al cambiar los permisos", - "name": "Nombre", - "sortByName": "Nombre", - "sortByDate": "Fecha Modificada", - "sortBySize": "Tamaño", - "ascending": "Ascendente", - "descending": "Descendente", - "root": "Raíz", - "new": "Nuevo", - "sortBy": "Ordenar por", - "items": "Elementos", - "selected": "Seleccionado", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", "octal": "Octal", - "storage": "Almacenamiento", - "disk": "Disco", - "used": "Usado", - "of": "de", - "toggleSidebar": "Cambiar barra lateral", - "cannotLoadPdf": "No se puede cargar PDF", - "pdfLoadError": "Se ha producido un error al cargar este archivo PDF.", - "loadingPdf": "Cargando PDF...", - "loadingPage": "Cargando página..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copiar al host…", - "moveToHost": "Mover al host…", - "copyItemsToHost": "Copia {{count}} elementos al host…", - "moveItemsToHost": "Mover {{count}} elementos al host…", - "noHostsConnected": "No hay otros servidores de gestores de archivos disponibles.", - "noHostsConnectedHint": "Agregue otro host SSH con el Administrador de archivos habilitado en el Administrador de hosts.", - "selectDestinationHost": "Seleccione el host de destino", - "destinationPath": "Ruta de destino", - "recentDestinations": "Destinos recientes", - "collapseRecentDestinations": "Colapsar destinos recientes", - "expandRecentDestinations": "Ampliar destinos recientes", - "browseFolders": "Explorar carpetas de destino", - "browseDestination": "Navegar o introducir ruta", - "confirmCopy": "Copiar", - "confirmMove": "Mover", - "transferring": "Transferencia…", - "compressing": "Comprimiendo…", - "extracting": "Extrayendo…", - "transferringItems": "Transferencia de {{current}} de {{total}} elementos…", - "transferSuccess": "Transferencia completada", - "transferError": "La transferencia falló.", - "transferPartial": "Transferencia completada con {{count}} errores", - "transferPartialHint": "No se pudo transferir: {{paths}}", - "itemsSummary": "{{count}} elementos", - "destMustBeDirectory": "El destino debe ser un directorio para transferencias de varios elementos.", - "selectThisFolder": "Seleccione esta carpeta", - "browsePathWillBeCreated": "Esta carpeta aún no existe. Se creará cuando comience la transferencia.", - "browsePathError": "No se pudo abrir esta ruta en el host de destino.", - "goUp": "Subir", - "copyFolderToHost": "Copiar carpeta al host…", - "moveFolderToHost": "Mover carpeta al host…", - "hostReady": "Listo", - "hostConnecting": "Conectando…", - "hostDisconnected": "No conectado", - "hostAuthRequired": "Se requiere autenticación; primero abra el Administrador de archivos en este host.", - "hostConnectionFailed": "Falló la conexión.", - "metricsTitle": "Plazos de transferencia", - "metricsPrepare": "Preparar destino: {{duration}}", - "metricsCompress": "Comprimir en la fuente: {{duration}}", - "metricsHopSourceRead": "Fuente → servidor: {{throughput}}", - "metricsHopDestSftpWrite": "Servidor → destino (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Servidor → destino (local): {{throughput}}", - "metricsTransfer": "De extremo a extremo: {{throughput}} ({{duration}})", - "metricsExtract": "Extraer en el destino: {{duration}}", - "metricsSourceDelete": "Eliminar de la fuente: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Comprimiendo en el host de origen…", - "progressExtracting": "Extrayendo en el destino…", - "progressTransferring": "Transferencia de datos…", - "progressReconnecting": "Reconectando…", - "parallelSegmentsLabel": "Carriles de transferencia paralelos", - "parallelSegmentsOption": "{{count}} carriles", - "parallelSegmentsHint": "Los archivos grandes se dividen en fragmentos de 256 MB. Varias vías utilizan conexiones separadas (como si se iniciaran varias transferencias) para obtener un mayor rendimiento total.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} carriles)", - "progressTransferringItems": "Transferencia de archivos ({{current}} de {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} archivos", - "sourceNotDeletedPartial": "Archivos de origen conservados (transferencia parcial)", - "jumpHostLimitation": "Ambos hosts deben ser accesibles desde el servidor Termix. No se admite el enrutamiento directo entre hosts.", - "cancel": "Cancelar", - "methodLabel": "Método de transferencia", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Archivo Tar", - "methodItemSftp": "SFTP por archivo", - "methodAutoHint": "Selecciona entre tar o SFTP por archivo según la cantidad, el tamaño y la compresibilidad de los archivos. Los archivos individuales siempre utilizan SFTP por transmisión.", - "methodTarHint": "Comprime en origen, transfiere un archivo y descomprímelo en destino. Requiere tar en ambos hosts Unix.", - "methodItemSftpHint": "Transfiere cada archivo individualmente mediante SFTP. Funciona en todos los sistemas operativos, incluido Windows.", - "methodPreviewLoading": "Calculando el método de transferencia…", - "methodPreviewError": "No se pudo previsualizar el método de transferencia. El servidor seleccionará un método al iniciar.", - "methodPreviewWillUseTar": "Se utilizará: archivo Tar", - "methodPreviewWillUseItemSftp": "Se utilizará: SFTP por archivo", - "methodPreviewScanSummary": "{{fileCount}} archivos, {{totalSize}} total (escaneados en el host de origen).", - "methodItemSftpLimitation": "Cada archivo utiliza el mismo flujo SFTP como una copia individual, una tras otra. El progreso se combina en todos los archivos, por lo que la barra avanza lentamente con archivos grandes.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Usted eligió SFTP por archivo.", - "user_tar": "Elegiste el archivo tar.", - "tar_unavailable": "Tar no está disponible en uno o ambos hosts; en su lugar, se utilizará SFTP por archivo.", - "windows_host": "Se utiliza un sistema operativo Windows; no se usa tar.", - "auto_multi_large": "Automático: varios archivos, incluido un archivo grande ({{largestSize}}) con datos comprimibles: paquetes tar en una sola transferencia.", - "auto_single_large_in_archive": "Automático: un archivo grande ({{largestSize}}) en este conjunto — SFTP por archivo.", - "auto_many_incompressible": "Automático: datos mayormente incompresibles — SFTP por archivo.", - "auto_many_files": "Automático: muchos archivos ({{fileCount}}) — tar reduce la sobrecarga por archivo.", - "auto_default": "Automático: SFTP por archivo para este conjunto." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Cancelar", - "progressCancelling": "Cancelando…", - "progressStalled": "Estancado", - "resumedHint": "Se ha restablecido la conexión con una transferencia activa iniciada en otra ventana.", - "transferCancelled": "Transferencia cancelada", - "transferCancelledCopyHint": "Es posible que queden archivos parciales en el destino.", - "transferCancelledMoveHint": "Es posible que queden archivos parciales en el destino. Los archivos de origen no se eliminaron.", - "cleanupDestFiles": "Limpiar el destino", - "cleanupDestFilesSuccess": "Se eliminaron archivos parciales del destino.", - "cleanupDestFilesPartial": "No se pudieron eliminar algunos archivos parciales.", - "cleanupDestFilesNothing": "No hay nada que limpiar en el destino.", - "cleanupDestFilesError": "La limpieza falló", - "retryTransfer": "Rever", - "retryTransferError": "Reintentar falló", - "transferFailedRetryHint": "Se conservaron datos parciales en el destino. El reintento se reanudará cuando se restablezca la conexión." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "No hay túneles SSH", - "createFirstTunnelMessage": "No has creado ningún túnel SSH todavía. Configura conexiones de túnel en el Administrador de Equipos para empezar.", - "connected": "Conectado", - "disconnected": "Desconectado", - "connecting": "Conectando...", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", "error": "Error", - "canceling": "Cancelando...", - "connect": "Conectar", - "disconnect": "Desconectar", - "cancel": "Cancelar", - "port": "Puerto", - "localPort": "Puerto local", - "remotePort": "Puerto remoto", - "currentHostPort": "Puerto actual de host", - "endpointPort": "Puerto final", - "bindIp": "IP local", - "endpointSshConfig": "Configuración SSSH de punto final", - "endpointSshHost": "Host SSH de punto final", - "endpointSshHostPlaceholder": "Seleccione un host configurado", - "endpointSshHostRequired": "Seleccione un endpoint SSH host para cada túnel de cliente.", - "attempt": "Intento {{current}} de {{max}}", - "nextRetryIn": "Siguiente reintento en {{seconds}} segundos", - "clientTunnels": "Túneles de cliente", - "clientTunnel": "Túnel de cliente", - "addClientTunnel": "Añadir túnel de cliente", - "noClientTunnels": "No hay túneles de cliente configurados en este ordenador.", - "tunnelName": "Nombre del túnel", - "remoteHost": "Host remoto", - "autoStart": "Auto inicio", - "clientAutoStartDesc": "Se inicia cuando este cliente de escritorio se abre y permanece conectado.", - "clientManualStartDesc": "Usar Inicio y Detener de esta fila. Termix no lo abrirá automáticamente.", - "clientRemoteServerNote": "El reenvío remoto puede requerir AllowTcpForwarding y GatewayPorts en el servidor SSH. El puerto remoto se cierra cuando este escritorio se desconecta.", - "clientTunnelStarted": "Túnel de cliente iniciado", - "clientTunnelStopped": "Túnel de cliente detenido", - "tunnelTestSucceeded": "Prueba de túnel exitosa", - "tunnelTestFailed": "Prueba de túnel fallida", - "localSaved": "Túneles de cliente guardados", - "localSaveError": "Error al guardar túneles de cliente locales", - "invalidBindIp": "IP local debe ser una dirección IPv4 válida.", - "invalidLocalTargetIp": "IP de destino local debe ser una dirección IPv4 válida.", - "invalidLocalPort": "El puerto local debe estar entre 1 y 65535.", - "invalidRemotePort": "Puerto remoto debe estar entre 1 y 65535.", - "invalidLocalTargetPort": "El puerto local destino debe estar entre 1 y 65535.", - "invalidEndpointPort": "El puerto de punto final debe estar entre 1 y 65535.", - "duplicateAutoStartBind": "Sólo un túnel de cliente de autoarranque puede usar {{bind}}.", - "manualControlError": "Error al actualizar el estado del túnel.", - "active": "Activo", - "start": "Comenzar", - "stop": "Parar", - "test": "Prueba", - "type": "Tipo de túnel", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Remoto (-R)", - "typeDynamic": "Dinámica (-D)", - "typeServerLocalDesc": "Anfitrión actual al final.", - "typeServerRemoteDesc": "Volver al punto final al host actual.", - "typeClientLocalDesc": "Ordenador local al final.", - "typeClientRemoteDesc": "Vuelve al punto final al ordenador local.", - "typeClientDynamicDesc": "SOCKS en el ordenador local.", - "typeDynamicDesc": "Reenviar tráfico CONNECT SOCKS5 a través de SSH", - "forwardDescriptionServerLocal": "Hosting actual {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host actual {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS en el host actual {{sourcePort}}.", - "forwardDescriptionClientLocal": "Local {{sourcePort}} → remoto {{endpointPort}}.", - "forwardDescriptionClientRemote": "{{sourcePort}} remoto → {{endpointPort}} local .", - "forwardDescriptionClientDynamic": "SOCKS en el puerto local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS vía {{endpoint}}", - "autoNameClientLocal": "{{localPort}} local → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → {{localPort}} local", - "autoNameClientDynamic": "SOCKS {{localPort}} vía {{endpoint}}", - "route": "Ruta:", - "lastStarted": "Último inicio", - "lastTested": "Última prueba", - "lastError": "Último error", - "maxRetries": "Reintentos máximos", - "maxRetriesDescription": "Cantidad máxima de intentos de reintento.", - "retryInterval": "Intervalo de reintento (segundos)", - "retryIntervalDescription": "Tiempo de espera entre intentos de reintento.", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", "local": "Local", - "remote": "Remoto", - "destination": "Destino", - "host": "Anfitrión", - "mode": "Modo", - "noHostSelected": "Ningún host seleccionado", - "working": "Trabajando..." + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Memoria", - "disk": "Disco", - "network": "Red", - "uptime": "Actualización", - "processes": "Procesos", - "available": "Disponible", - "free": "Gratis", - "connecting": "Conectando...", - "connectionFailed": "Error al conectar al servidor", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "Núcleo {{count}}", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Uso de CPU", - "memoryUsage": "Uso de memoria", - "diskUsage": "Uso del disco", - "failedToFetchHostConfig": "Error al recuperar la configuración del host", - "serverOffline": "Servidor fuera de línea", - "cannotFetchMetrics": "No se pueden obtener métricas del servidor sin conexión", - "totpFailed": "Falló la verificación TOTP", - "noneAuthNotSupported": "Las estadísticas del servidor no son compatibles con el tipo de autenticación 'ninguno'.", - "load": "Cargar", - "systemInfo": "Información del sistema", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Sistema operativo", - "kernel": "Kerel", - "seconds": "segundos", - "networkInterfaces": "Interfaces de red", - "noInterfacesFound": "No se encontraron interfaces de red", - "noProcessesFound": "No se encontraron procesos", - "loginStats": "Estadísticas de inicio de sesión SSH", - "noRecentLoginData": "No hay datos de acceso recientes", - "executingQuickAction": "Ejecutando {{name}}...", - "quickActionSuccess": "{{name}} completado con éxito", - "quickActionFailed": "{{name}} falló", - "quickActionError": "Error al ejecutar {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Puertos de escucha", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Puerto", - "address": "Dirección", - "process": "Proceso", - "noData": "No hay datos de puertos en escucha" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Cortafuegos", - "inactive": "Inactivo", - "policy": "Política", - "rules": "reglas", - "noData": "No hay datos firewall disponibles", - "action": "Accin", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Puerto", - "source": "Fuente", - "anywhere": "En cualquier lugar" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Cargar Media", - "swap": "Intercambiar", - "architecture": "Arquitectura", - "refresh": "Refrescar", - "retry": "Reintentar" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "SSH autoalojado y administración remota de escritorio", - "loginTitle": "Iniciar sesión en Termix", - "registerTitle": "Crear cuenta", - "forgotPassword": "¿Olvidó la contraseña?", - "rememberMe": "Recordar dispositivo durante 30 días (incluye TOTP)", - "noAccount": "¿No tienes una cuenta?", - "hasAccount": "¿Ya tienes una cuenta?", - "twoFactorAuth": "Autenticación de dos factores", - "enterCode": "Introduzca código de verificación", - "backupCode": "O usar código de copia de seguridad", - "verifyCode": "Verificar código", - "redirectingToApp": "Redirigiendo a la aplicación...", - "sshAuthenticationRequired": "Autenticación SSH requerida", - "sshNoKeyboardInteractive": "Autenticación interactiva del teclado no disponible", - "sshAuthenticationFailed": "Autenticación fallida", - "sshAuthenticationTimeout": "Tiempo de espera de autenticación", - "sshNoKeyboardInteractiveDescription": "El servidor no soporta autenticación interactiva de teclado. Por favor, introduzca su contraseña o clave SSH.", - "sshAuthFailedDescription": "Las credenciales proporcionadas eran incorrectas. Por favor, inténtelo de nuevo con credenciales válidas.", - "sshTimeoutDescription": "Se ha agotado el tiempo de espera de autenticación. Inténtalo de nuevo.", - "sshProvideCredentialsDescription": "Por favor, proporcione sus credenciales SSH para conectarse a este servidor.", - "sshPasswordDescription": "Introduzca la contraseña para esta conexión SSH.", - "sshKeyPasswordDescription": "Si su clave SSH está cifrada, introduzca la contraseña aquí.", - "passphraseRequired": "Contraseña requerida", - "passphraseRequiredDescription": "La clave SSH está cifrada. Por favor, introduzca la contraseña para desbloquearla.", - "back": "Atrás", - "firstUser": "Primer usuario", - "firstUserMessage": "Eres el primer usuario y se hará un administrador. Puedes ver la configuración de administración en la barra lateral del menú desplegable. Si cree que esto es un error, compruebe los registros de docker, o cree un problema de GitHub.", - "external": "Externo", - "loginWithExternal": "Iniciar sesión con proveedor externo", - "loginWithExternalDesc": "Iniciar sesión usando su proveedor de identidad externo configurado", - "externalNotSupportedInElectron": "La autenticación externa no está soportada en la aplicación Electron todavía. Por favor, utilice la versión web para iniciar sesión OIDC.", - "resetPasswordButton": "Restablecer contraseña", - "sendResetCode": "Enviar código de reinicio", - "resetCodeDesc": "Introduzca su nombre de usuario para recibir un código de restablecimiento de contraseña. El código se registrará en los registros del contenedor docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verificar código", - "enterResetCode": "Introduzca el código de 6 dígitos del contenedor docker para el usuario:", - "newPassword": "Nueva contraseña", - "confirmNewPassword": "Confirmar contraseña", - "enterNewPassword": "Introduzca su nueva contraseña para el usuario:", - "signUp": "Regístrate", - "desktopApp": "App de escritorio", - "loggingInToDesktopApp": "Iniciando sesión en la aplicación de escritorio", - "loadingServer": "Cargando servidor...", - "dataLossWarning": "Restablecer su contraseña de esta manera eliminará todos sus hosts SSH guardados, credenciales y otros datos cifrados. Esta acción no se puede deshacer. Utilice sólo si ha olvidado su contraseña y no ha iniciado sesión.", - "authenticationDisabled": "Autenticación desactivada", - "authenticationDisabledDesc": "Todos los métodos de autenticación están actualmente deshabilitados. Por favor, póngase en contacto con su administrador.", - "attemptsRemaining": "{{count}} intentos restantes" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verificar clave SSH Host", - "keyChangedWarning": "SSH Host Key cambiada", - "firstConnectionTitle": "Primera conexión a este host", - "firstConnectionDescription": "La autenticidad de este host no se puede establecer. Verifique que la huella digital coincide con lo que espera.", - "keyChangedDescription": "La clave de host para este servidor ha cambiado desde su última conexión. Esto podría indicar un problema de seguridad.", - "previousKey": "Clave anterior", - "newFingerprint": "Nueva huella dactilar", - "fingerprint": "Huella dactilar", - "verifyInstructions": "Si confía en este host, haga clic en Aceptar para continuar y guardar esta huella dactilar para conexiones futuras.", - "securityWarning": "Advertencia de seguridad", - "acceptAndContinue": "Aceptar y continuar", - "acceptNewKey": "Aceptar nueva clave y continuar" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "No se pudo conectar a la base de datos", - "unknownError": "Error desconocido", - "loginFailed": "Error al iniciar sesión", - "failedPasswordReset": "Error al iniciar el restablecimiento de contraseña", - "failedVerifyCode": "Error al verificar el código de restablecimiento", - "failedCompleteReset": "Error al restablecer la contraseña", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Error al iniciar inicio de sesión OIDC", - "silentSigninOidcUnavailable": "Se solicitó el inicio de sesión silencioso, pero el inicio de sesión de OIDC no está disponible.", - "failedUserInfo": "Error al obtener la información del usuario después de iniciar sesión", - "oidcAuthFailed": "Autenticación OIDC fallida", - "invalidAuthUrl": "URL de autorización no válida recibida del backend", - "requiredField": "Este campo es obligatorio", - "minLength": "La longitud mínima es {{min}}", - "passwordMismatch": "Las contraseñas no coinciden", - "passwordLoginDisabled": "Nombre de usuario/contraseña está actualmente desactivado", - "sessionExpired": "Sesión caducada - por favor inicia sesión de nuevo", - "totpRateLimited": "Tasa limitada: Demasiados intentos de verificación TOTP. Inténtalo de nuevo más tarde.", - "totpRateLimitedWithTime": "Tasa limitada: Demasiados intentos de verificación TOTP. Por favor, espera {{time}} segundos antes de intentarlo de nuevo.", - "resetCodeRateLimited": "Tasa limitada: Demasiados intentos de verificación. Inténtalo de nuevo más tarde.", - "resetCodeRateLimitedWithTime": "Tasa limitada: Demasiados intentos de verificación. Por favor, espere {{time}} segundos antes de intentarlo de nuevo.", - "authTokenSaveFailed": "Error al guardar el token de autenticación", - "failedToLoadServer": "Error al cargar el servidor" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "El registro de nueva cuenta está actualmente desactivado por un administrador. Por favor, inicie sesión o póngase en contacto con un administrador.", - "userNotAllowed": "Tu cuenta no está autorizada a registrarse. Por favor, contacta con un administrador.", - "databaseConnectionFailed": "Error al conectar al servidor de base de datos", - "resetCodeSent": "Restablecer código enviado a los registros de Docker", - "codeVerified": "Código verificado con éxito", - "passwordResetSuccess": "Contraseña restablecida correctamente", - "loginSuccess": "Inicio de sesión correcto", - "registrationSuccess": "Registro exitoso" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Túneles de escritorio locales dirigidos a hosts SSH configurados.", - "c2sTunnelPresets": "Ajustes de túnel de cliente", - "c2sTunnelPresetsDesc": "Guarde la lista de túneles locales de este cliente de escritorio como un preset de servidor nombrado, o cargue un preset de nuevo en este cliente.", - "c2sTunnelPresetsUnavailable": "Los ajustes predeterminados del túnel de cliente sólo están disponibles en el cliente de escritorio.", - "c2sPresetName": "Nombre predefinido", - "c2sPresetNamePlaceholder": "Nombre predefinido del cliente", - "c2sPresetToLoad": "Preseleccionar para cargar", - "c2sNoPresetSelected": "Ningún preset seleccionado", - "c2sNoPresets": "No hay preajustes guardados", - "c2sLoadPreset": "Cargar", - "c2sCurrentLocalConfig": "{{count}} túnel(s) de cliente local configurado en este escritorio.", - "c2sPresetSyncNote": "Los ajustes predeterminados son instantáneas explícitas; la carga de uno reemplaza la lista de túneles de clientes locales de este cliente de escritorio.", - "c2sPresetSaved": "Preajuste de túnel de cliente guardado", - "c2sPresetLoaded": "Túnel de cliente preestablecido cargado localmente", - "c2sPresetRenamed": "Preajuste de túnel de cliente renombrado", - "c2sPresetDeleted": "Preajuste de túnel de cliente eliminado", - "c2sPresetLoadError": "Error al cargar los ajustes predeterminados del túnel del cliente" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Idioma", - "keyPassword": "clave de contraseña", - "pastePrivateKey": "Pegue su clave privada aquí...", - "localListenerHost": "127.0.0.1 (escuchar localmente)", - "localTargetHost": "127.0.0.1 (objetivo en este ordenador)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Introduzca su contraseña", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Tablero", - "loading": "Cargando panel...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Soporte", - "discord": "Discordia", - "serverOverview": "Vista general del servidor", - "version": "Versión", - "upToDate": "Hasta la fecha", - "updateAvailable": "Actualización disponible", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Actualización", - "database": "Base de datos", - "healthy": "Saludable", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", "error": "Error", - "totalHosts": "Total de hosts", - "totalTunnels": "Total de túneles", - "totalCredentials": "Total de credenciales", - "recentActivity": "Actividad reciente", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Cargando actividad reciente...", - "noRecentActivity": "No hay actividad reciente", - "quickActions": "Acciones rápidas", - "addHost": "Añadir Host", - "addCredential": "Añadir credencial", - "adminSettings": "Configuración de Admin", - "userProfile": "Perfil de usuario", - "serverStats": "Estadísticas del Servidor", - "loadingServerStats": "Cargando estadísticas del servidor...", - "noServerData": "No hay datos del servidor disponibles", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizar panel", - "dashboardSettings": "Ajustes del panel", - "enableDisableCards": "Habilitar/deshabilitar tarjetas", - "resetLayout": "Restablecer por defecto", - "serverOverviewCard": "Vista general del servidor", - "recentActivityCard": "Actividad reciente", - "networkGraphCard": "Gráfico de red", - "networkGraph": "Gráfico de red", - "quickActionsCard": "Acciones rápidas", - "serverStatsCard": "Estadísticas del Servidor", - "panelMain": "Principal", - "panelSide": "Lado", - "justNow": "justo ahora" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ESTABLE", - "hostsOnline": "Hosts en línea", - "activeTunnels": "Túneles activos", - "registerNewServer": "Registrar un nuevo servidor", - "storeSshKeysOrPasswords": "Guardar claves o contraseñas SSH", - "manageUsersAndRoles": "Administrar usuarios y roles", - "manageYourAccount": "Administra tu cuenta", - "hostStatus": "Estado del host", - "noHostsConfigured": "No hay hosts configurados", - "online": "EN LÍNEA", - "offline": "DESARROLLO", - "onlineLower": "En línea", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Añadir:", - "commandPalette": "Paleta de comandos", - "done": "Hecho", - "editModeInstructions": "Arrastra tarjetas para reordenar · Arrastra el divisor de columnas para redimensionar columnas · Arrastra el borde inferior de una tarjeta para redimensionar su altura · Basura para remover", - "empty": "Vaciar", - "clear": "Claro" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Añadir Host", - "addGroup": "Añadir grupo", - "addLink": "Añadir enlace", - "zoomIn": "Acercar en", - "zoomOut": "Apagar", - "resetView": "Reiniciar vista", - "selectHost": "Seleccionar Host", - "chooseHost": "Seleccione un host...", - "parentGroup": "Grupo padre", - "noGroup": "Sin grupo", - "groupName": "Nombre del grupo", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", "color": "Color", - "source": "Fuente", + "source": "Source", "target": "Target", - "moveToGroup": "Mover al grupo", - "selectGroup": "Seleccionar grupo...", - "addConnection": "Añadir conexión", - "hostDetails": "Detalles del host", - "removeFromGroup": "Eliminar del grupo", - "addHostHere": "Añadir host aquí", - "editGroup": "Editar grupo", - "delete": "Eliminar", - "add": "Añadir", - "create": "Crear", - "move": "Mover", - "connect": "Conectar", - "createGroup": "Crear grupo", - "selectSourcePlaceholder": "Seleccionar origen...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Archivo inválido", - "hostAlreadyExists": "El anfitrión ya está en la topología", - "connectionExists": "La conexión ya existe", - "unknown": "Desconocido", - "name": "Nombre", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Estado", - "failedToAddNode": "Error al añadir el nodo", - "sourceDifferentFromTarget": "La fuente y el objetivo deben ser diferentes", - "exportJSON": "Exportar JSON", - "importJSON": "Importar JSON", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Gestor de archivos", - "tunnel": "Túnel", - "docker": "Doctor", - "serverStats": "Estadísticas del Servidor", - "noNodes": "Aún no hay nodos" + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker no está habilitado para este host", - "validating": "Validando Docker...", - "connecting": "Conectando...", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Error al conectar con Docker", - "containerStarted": "Contenedor {{name}} iniciado", - "failedToStartContainer": "Error al iniciar el contenedor {{name}}", - "containerStopped": "Contenedor {{name}} detenido", - "failedToStopContainer": "No se pudo detener el contenedor {{name}}", - "containerRestarted": "Contenedor {{name}} reiniciado", - "failedToRestartContainer": "Error al reiniciar el contenedor {{name}}", - "containerPaused": "Contenedor {{name}} en pausa", - "containerUnpaused": "Contenedor {{name}} despausado", - "failedToTogglePauseContainer": "Error al cambiar el estado de pausa para el contenedor {{name}}", - "containerRemoved": "Contenedor {{name}} eliminado", - "failedToRemoveContainer": "Error al eliminar el contenedor {{name}}", - "image": "Imagen", - "ports": "Puertos", - "noPorts": "Sin puertos", - "start": "Comenzar", - "confirmRemoveContainer": "¿Está seguro de que desea eliminar el contenedor '{{name}}'? Esta acción no se puede deshacer.", - "runningContainerWarning": "Advertencia: Este contenedor se está ejecutando. Eliminarlo detendrá el contenedor primero.", - "loadingContainers": "Cargando contenedores...", - "manager": "Administrador Docker", - "autoRefresh": "Actualización automática", - "timestamps": "Marcas de tiempo", - "lines": "Líneas", - "filterLogs": "Filtrar registros...", - "refresh": "Refrescar", - "download": "Descargar", - "clear": "Claro", - "logsDownloaded": "Registros descargados con éxito", - "last50": "Últimos 50", - "last100": "Últimos 100", - "last500": "Últimos 500", - "last1000": "Últimos 1000", - "allLogs": "Todos los registros", - "noLogsMatching": "No hay registros que coincidan con \"{{query}}\"", - "noLogsAvailable": "No hay registros disponibles", - "noContainersFound": "No hay contenedores", - "noContainersFoundHint": "No hay contenedores Docker disponibles en este host", - "searchPlaceholder": "Buscar contenedores...", - "allStatuses": "Todos los estados", - "stateRunning": "Ejecutando", - "statePaused": "Pausado", - "stateExited": "Salido", - "stateRestarting": "Reiniciando", - "noContainersMatchFilters": "Ningún contenedor coincide con sus filtros", - "noContainersMatchFiltersHint": "Intente ajustar su criterio de búsqueda o filtro", - "failedToFetchStats": "Error al obtener las estadísticas del contenedor", - "containerNotRunning": "Contenedor no ejecutándose", - "startContainerToViewStats": "Iniciar el contenedor para ver las estadísticas", - "loadingStats": "Cargando estadísticas...", - "errorLoadingStats": "Error al cargar estadísticas", - "noStatsAvailable": "No hay estadísticas disponibles", - "cpuUsage": "Uso de CPU", - "current": "Actual", - "memoryUsage": "Uso de memoria", - "networkIo": "E/S de red", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Salida", - "blockIo": "Bloquear E/S", - "read": "Leer", - "write": "Escribir", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "Información del contenedor", - "name": "Nombre", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Estado", - "containerMustBeRunning": "El contenedor debe estar ejecutándose para acceder a la consola", - "verificationCodePrompt": "Introduzca código de verificación", - "totpVerificationFailed": "Falló la verificación del TOTP. Por favor, inténtelo de nuevo.", - "warpgateVerificationFailed": "La autenticación de Warpgate falló. Por favor, inténtelo de nuevo.", - "connectedTo": "Conectado a {{containerName}}", - "disconnected": "Desconectado", - "consoleError": "Error de consola", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", "errorMessage": "Error: {{message}}", - "failedToConnect": "No se pudo conectar al contenedor", - "console": "Consola", - "selectShell": "Seleccionar shell", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "m", - "ash": "ceniza", - "connect": "Conectar", - "disconnect": "Desconectar", - "notConnected": "No conectado", - "clickToConnect": "Haga clic en conectar para iniciar una sesión de shell", - "connectingTo": "Conectando a {{containerName}}...", - "containerNotFound": "Contenedor no encontrado", - "backToList": "Volver a la lista", - "logs": "Registros", - "stats": "Estadísticas", - "consoleTab": "Consola", - "startContainerToAccess": "Iniciar el contenedor para acceder a la consola" + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Usuarios", - "sectionSessions": "Sesiones", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", "sectionRoles": "Roles", - "sectionDatabase": "Base de datos", - "sectionApiKeys": "Claves API", - "allowRegistration": "Permitir registro de usuario", - "allowRegistrationDesc": "Permitir auto-registro de nuevos usuarios", - "allowPasswordLogin": "Permitir inicio de sesión de contraseña", - "allowPasswordLoginDesc": "Nombre de usuario / contraseña", - "oidcAutoProvision": "Auto-Provisión OIDC", - "oidcAutoProvisionDesc": "Auto-crear cuentas para usuarios OIDC incluso cuando el registro está desactivado", - "allowPasswordReset": "Permitir restablecer contraseña", - "allowPasswordResetDesc": "Restablecer código a través de registros Docker", - "sessionTimeout": "Tiempo de espera agotado", - "hours": "horas", - "sessionTimeoutRange": "Mínimo 1h · Max 720h", - "monitoringDefaults": "Monitoreo por defecto", - "statusCheck": "Comprobación de estado", - "metrics": "Métricas", - "sec": "seg", - "logLevel": "Nivel de Log", - "enableGuacamole": "Activar Guacamole", - "enableGuacamoleDesc": "Escritorio remoto RDP/VNC", - "guacdUrl": "URL de guacd", - "oidcDescription": "Configure OpenID Connect para SSO. Los campos marcados * son obligatorios.", - "oidcClientId": "ID de cliente", - "oidcClientSecret": "Cliente secreto", - "oidcAuthUrl": "URL de autorización", - "oidcIssuerUrl": "URL del emisor", - "oidcTokenUrl": "URL del token", - "oidcUserIdentifier": "Ruta del identificador de usuario", - "oidcDisplayName": "Mostrar ruta de nombre", - "oidcScopes": "Ámbitos", - "oidcUserinfoUrl": "Reemplazar URL de Userinfo", - "oidcAllowedUsers": "Usuarios Permitidos", - "oidcAllowedUsersDesc": "Un correo electrónico por línea. Dejar en blanco para permitir a todos.", - "removeOidc": "Eliminar", - "usersCount": "{{count}} usuarios", - "createUser": "Crear", - "newRole": "Nuevo rol", - "roleName": "Nombre", - "roleDisplayName": "Mostrar nombre", - "roleDescription": "Descripción", - "rolesCount": "Rol {{count}}", - "createRole": "Crear", - "creating": "Creando...", - "exportDatabase": "Exportar base de datos", - "exportDatabaseDesc": "Descargar una copia de seguridad de todos los hosts, credenciales y ajustes", - "export": "Exportar", - "exporting": "Exportando...", - "importDatabase": "Importar base de datos", - "importDatabaseDesc": "Restaurar desde un archivo de copia de seguridad .sqlite", - "importDatabaseSelected": "Seleccionado: {{name}}", - "selectFile": "Seleccionar archivo", - "changeFile": "Cambiar", - "import": "Importar", - "importing": "Importando...", - "apiKeysCount": "Claves {{count}}", - "newApiKey": "Nueva Clave API", - "apiKeyCreatedWarning": "Clave creada - cópiela ahora, no se mostrará de nuevo.", - "apiKeyName": "Nombre", - "apiKeyUser": "Usuario", - "apiKeySelectUser": "Seleccione un usuario...", - "apiKeyExpiresAt": "Caduca el", - "createKey": "Crear Clave", - "apiKeyNoExpiry": "No expirar", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Doble Auth", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", "authTypeLocal": "Local", - "adminStatusAdministrator": "Administrador", - "adminStatusRegularUser": "Usuario regular", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "SI", - "customBadge": "CLIENTE", - "youBadge": "TI", - "sessionsActive": "{{count}} activo", - "sessionActive": "Activo: {{time}}", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Todos", - "revokeAllSessionsSuccess": "Todas las sesiones para el usuario revocadas", - "revokeAllSessionsFailed": "Error al revocar las sesiones", - "revokeSessionFailed": "Error al revocar la sesión", - "addRole": "Añadir rol", - "noCustomRoles": "Sin roles personalizados definidos", - "removeRoleFailed": "Error al eliminar el rol", - "assignRoleFailed": "No se pudo asignar el rol", - "deleteRoleFailed": "Error al eliminar el rol", - "userAdminAccess": "Administrador", - "userAdminAccessDesc": "Acceso total a todos los ajustes de administración", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", "userRoles": "Roles", - "revokeAllUserSessions": "Revocar todas las sesiones", - "revokeAllUserSessionsDesc": "Forzar reinicio de sesión en todos los dispositivos", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Eliminar este usuario es permanente.", - "deleteUser": "Eliminar {{username}}", - "deleting": "Eliminando...", - "deleteUserFailed": "Error al eliminar el usuario", - "deleteUserSuccess": "Usuario \"{{username}}\" eliminado", - "deleteRoleSuccess": "Rol \"{{name}}\" eliminado", - "revokeKeySuccess": "Llave \"{{name}}\" revocada", - "revokeKeyFailed": "Error al revocar la clave", - "copiedToClipboard": "Copiado al portapapeles", - "done": "Hecho", - "createUserTitle": "Crear usuario", - "createUserDesc": "Crear una nueva cuenta local.", - "createUserUsername": "Usuario", - "createUserPassword": "Contraseña", - "createUserPasswordHint": "Mínimo 6 caracteres.", - "createUserEnterUsername": "Introduzca nombre de usuario", - "createUserEnterPassword": "Introduzca contraseña", - "createUserSubmit": "Crear usuario", - "editUserTitle": "Administrar usuario: {{username}}", - "editUserDesc": "Editar roles, estado de administración, sesiones y configuración de la cuenta.", - "editUserUsername": "Usuario", - "editUserAuthType": "Tipo de Auth", - "editUserAdminStatus": "Estado del Admin", - "editUserUserId": "ID Usuario", - "linkAccountTitle": "Enlazar OIDC a la cuenta de contraseña", - "linkAccountDesc": "Fusionar la cuenta OIDC {{username}} con una cuenta local existente.", - "linkAccountWarningTitle": "Esto hará:", - "linkAccountEffect1": "Eliminar la cuenta sólo OIDC", - "linkAccountEffect2": "Añadir inicio de sesión OIDC a la cuenta de destino", - "linkAccountEffect3": "Permitir tanto OIDC como login de contraseña", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Introduzca el nombre de usuario de cuenta local al que enlazar", - "linkAccounts": "Enlazar cuentas", - "linkAccountSuccess": "Cuenta OIDC vinculada a \"{{username}}\"", - "linkAccountFailed": "No se pudo vincular la cuenta OIDC.", - "linkAccountInProgress": "Enlace...", - "saving": "Guardando...", - "updateRegistrationFailed": "Error al actualizar la configuración de registro", - "updatePasswordLoginFailed": "Error al actualizar el inicio de sesión de contraseña", - "updateOidcAutoProvisionFailed": "Error al actualizar la configuración de autoprovisión OIDC", - "updatePasswordResetFailed": "Error al actualizar el restablecimiento de contraseña", - "sessionTimeoutRange2": "El tiempo de espera de la sesión debe estar entre 1 y 720 horas", - "sessionTimeoutSaved": "Tiempo de espera de sesión guardado", - "sessionTimeoutSaveFailed": "Error al guardar el tiempo de espera de sesión", - "monitoringIntervalInvalid": "Valores de intervalo inválidos", - "monitoringSaved": "Ajustes de monitorización guardados", - "monitoringSaveFailed": "Error al guardar la configuración de monitoreo", - "guacamoleSaved": "Ajustes de Guacamole guardados", - "guacamoleSaveFailed": "Error al guardar la configuración de Guacamole", - "guacamoleUpdateFailed": "Error al actualizar la configuración de Guacamole", - "logLevelUpdateFailed": "Error al actualizar el nivel de registro", - "oidcSaved": "Configuración OIDC guardada", - "oidcSaveFailed": "Error al guardar la configuración OIDC", - "oidcRemoved": "Configuración OIDC eliminada", - "oidcRemoveFailed": "Error al eliminar la configuración OIDC", - "createUserRequired": "Nombre de usuario y contraseña son requeridos", - "createUserPasswordTooShort": "La contraseña debe tener al menos 6 caracteres", - "createUserSuccess": "Usuario \"{{username}}\" creado", - "createUserFailed": "Error al crear el usuario", - "updateAdminStatusFailed": "Error al actualizar el estado del administrador", - "allSessionsRevoked": "Todas las sesiones revocadas", - "revokeSessionsFailed": "Error al revocar las sesiones", - "createRoleRequired": "Nombre y nombre para mostrar son requeridos", - "createRoleSuccess": "Rol \"{{name}}\" creado", - "createRoleFailed": "Error al crear el rol", - "apiKeyNameRequired": "Se requiere nombre de clave", - "apiKeyUserRequired": "Se requiere ID de usuario", - "apiKeyCreatedSuccess": "Clave API \"{{name}}\" creada", - "apiKeyCreateFailed": "Error al crear la clave API", - "exportSuccess": "Base de datos exportada con éxito", - "exportFailed": "Exportación de base de datos fallida", - "importSelectFile": "Por favor, seleccione un archivo primero", - "importCompleted": "Importación completada: {{total}} elementos importados, {{skipped}} omitidos", - "importFailed": "Importación fallida: {{error}}", - "importError": "Error al importar la base de datos" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Anfitrión", - "hostPlaceholder": "192.168.1.1 o ejemplo.com", - "portLabel": "Puerto", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Usuario", - "usernamePlaceholder": "nombre de usuario", - "authLabel": "Aut", - "passwordLabel": "Contraseña", - "passwordPlaceholder": "contraseña", - "privateKeyLabel": "Clave Privada", - "privateKeyPlaceholder": "Pegar clave privada...", - "credentialLabel": "Credencial", - "credentialPlaceholder": "Seleccione una credencial guardada", - "connectToTerminal": "Conectar a la terminal", - "connectToFiles": "Conectar a archivos" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "No hay terminal seleccionado", - "noTerminalSelectedHint": "Abrir una pestaña de terminal SSH para ver su historial de comandos", - "searchPlaceholder": "Buscar historial...", - "clearAll": "Limpiar todo", - "noHistoryEntries": "No hay entradas del historial", - "trackingDisabled": "El seguimiento del historial está desactivado", - "trackingDisabledHint": "Actívalo en la configuración de tu perfil para grabar comandos." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Grabación de clave", - "recordToTerminals": "Grabar en terminales", - "selectAll": "Todos", - "selectNone": "Ninguna", - "noTerminalTabsOpen": "No hay pestañas de terminal abiertas", - "selectTerminalsAbove": "Seleccionar terminales arriba", - "broadcastInputPlaceholder": "Escriba aquí para emitir pulsaciones de teclado...", - "stopRecording": "Detener grabación", - "startRecording": "Iniciar grabación", - "settingsTitle": "Ajustes", - "enableRightClickCopyPaste": "Habilitar copiar/pegar con clic derecho" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Diseño", - "selectLayoutAbove": "Seleccione un diseño arriba", - "selectLayoutHint": "Elegir cuántos paneles mostrar", - "panesTitle": "Paneles", - "openTabsTitle": "Abrir Pestañas", - "dragTabsHint": "Arrastra las pestañas a los paneles superiores o usa la función Asignación rápida.", - "dropHere": "Soltar aquí", - "emptyPane": "Vaciar", - "dashboard": "Tablero", - "clearSplitScreen": "Limpiar pantalla dividida", - "quickAssign": "Asignación rápida", - "alreadyAssigned": "Panel {{index}}", - "splitTab": "Pestaña dividida", - "addToSplit": "Agregar a Dividir", - "removeFromSplit": "Eliminar de Split", - "assignToPane": "Asignar al panel" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Crear Fragmento", - "createSnippetDescription": "Crear un nuevo fragmento de comandos para ejecución rápida", - "nameLabel": "Nombre", - "namePlaceholder": "ej. Reiniciar Nginx", - "descriptionLabel": "Descripción", - "descriptionPlaceholder": "Descripción opcional", - "optional": "Opcional", - "folderLabel": "Carpeta", - "noFolder": "No hay carpeta (sin categorizar)", - "commandLabel": "Comando", - "commandPlaceholder": "por ejemplo, el sistema sudo reiniciar nginx", - "cancel": "Cancelar", - "createSnippetButton": "Crear Fragmento", - "createFolderTitle": "Crear carpeta", - "createFolderDescription": "Organiza tus fragmentos en carpetas", - "folderNameLabel": "Nombre de carpeta", - "folderNamePlaceholder": "ej., Comandos del Sistema, Docker Scripts", - "folderColorLabel": "Color de carpeta", - "folderIconLabel": "Icono de carpeta", - "previewLabel": "Vista previa", - "folderNameFallback": "Nombre de carpeta", - "createFolderButton": "Crear carpeta", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Todos", - "selectNone": "Ninguna", - "noTerminalTabsOpen": "No hay pestañas de terminal abiertas", - "searchPlaceholder": "Buscar fragmentos...", - "newSnippet": "Nuevo Fragmento", - "newFolder": "Nueva carpeta", - "run": "Ejecutar", - "noSnippetsInFolder": "No hay fragmentos en esta carpeta", - "uncategorized": "Sin categorizar", - "editSnippetTitle": "Editar Snippet", - "editSnippetDescription": "Actualizar este fragmento de comandos", - "saveSnippetButton": "Guardar Cambios", - "createSuccess": "Fragmento creado correctamente", - "createFailed": "Error al crear el fragmento", - "updateSuccess": "Fragmento actualizado correctamente", - "updateFailed": "Error al actualizar el snippet", - "deleteFailed": "Error al eliminar el snippet", - "folderCreateSuccess": "Carpeta creada correctamente", - "folderCreateFailed": "Error al crear la carpeta", - "editFolderTitle": "Editar carpeta", - "editFolderDescription": "Renombrar o cambiar la apariencia de esta carpeta", - "saveFolderButton": "Guardar Cambios", - "editFolder": "Editar carpeta", - "deleteFolder": "Eliminar carpeta", - "folderDeleteSuccess": "Carpeta \"{{name}}\" eliminada", - "folderDeleteFailed": "Error al eliminar la carpeta", - "folderEditSuccess": "Carpeta actualizada correctamente", - "folderEditFailed": "Error al actualizar la carpeta", - "confirmRunMessage": "Ejecutar \"{{name}}\"?", - "confirmRunButton": "Correr", - "runSuccess": "Ran \"{{name}}\" en terminales {{count}}", - "copySuccess": "Copiado \"{{name}}\" al portapapeles", - "shareTitle": "Compartir Snippet", - "shareUser": "Usuario", - "shareRole": "Rol", - "selectUser": "Seleccione un usuario...", - "selectRole": "Seleccione un rol...", - "shareSuccess": "Fragmento compartido correctamente", - "shareFailed": "Error al compartir el snippet", - "revokeSuccess": "Acceso revocado", - "revokeFailed": "Error al revocar el acceso", - "currentAccess": "Acceso actual", - "shareLoadError": "Error al cargar los datos compartidos", - "loading": "Cargando...", - "close": "Cerrar", - "reorderFailed": "No se pudo guardar el orden del fragmento." + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Cuenta", - "sectionAppearance": "Apariencia", - "sectionSecurity": "Seguridad", - "sectionApiKeys": "Claves API", - "sectionC2sTunnels": "Túneles C2S", - "usernameLabel": "Usuario", - "roleLabel": "Rol", - "roleAdministrator": "Administrador", - "authMethodLabel": "Método Auth", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "En", - "twoFaOff": "Apagado", - "versionLabel": "Versión", - "deleteAccount": "Eliminar cuenta", - "deleteAccountDescription": "Elimina permanentemente tu cuenta", - "deleteButton": "Eliminar", - "deleteAccountPermanent": "Esta acción es permanente y no se puede deshacer.", - "deleteAccountWarning": "Todas las sesiones, hosts, credenciales y ajustes se eliminarán permanentemente.", - "confirmPasswordDeletePlaceholder": "Introduzca su contraseña para confirmar", - "languageLabel": "Idioma", - "themeLabel": "Tema", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Color de acento", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Comando autocompletado", - "commandAutocompleteDesc": "Mostrar autocompletado al escribir", - "historyTracking": "Historial de seguimiento", - "historyTrackingDesc": "Rastrear comandos de terminal", - "syntaxHighlighting": "Resaltado sintáctico", - "syntaxHighlightingDesc": "Resaltar salida de terminal", - "commandPalette": "Paleta de comandos", - "commandPaletteDesc": "Activar atajo de teclado", - "reopenTabsOnLogin": "Reabrir pestañas al iniciar sesión", - "reopenTabsOnLoginDesc": "Restaura tus pestañas abiertas cuando inicies sesión o actualices la página, incluso desde otro dispositivo.", - "confirmTabClose": "Confirmar cierre de pestaña", - "confirmTabCloseDesc": "Preguntar antes de cerrar pestañas de terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Mostrar etiquetas de host", - "showHostTagsDesc": "Mostrar etiquetas en la lista de hosts", - "hostTrayOnClick": "Haz clic para expandir las acciones del anfitrión.", - "hostTrayOnClickDesc": "Mostrar siempre los botones de conexión; hacer clic para expandir las opciones de administración en lugar de pasar el cursor por encima.", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Mantener siempre expandida la barra lateral izquierda en lugar de expandirla al pasar el cursor sobre ella.", - "settingsSnippets": "Fragmentos", - "foldersCollapsed": "Carpetas colapsadas", - "foldersCollapsedDesc": "Colapsar carpetas por defecto", - "confirmExecution": "Confirmar ejecución", - "confirmExecutionDesc": "Confirmar antes de ejecutar fragmentos", - "settingsUpdates": "Actualizaciones", - "disableUpdateChecks": "Desactivar comprobaciones de actualización", - "disableUpdateChecksDesc": "Dejar de buscar actualizaciones", - "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "2FA está habilitado", - "totpDisabled": "Añadir seguridad de acceso adicional", - "disable": "Desactivar", - "enable": "Activar", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Escanea el código QR o introduce el secreto en tu aplicación de autenticador, luego introduce el código de 6 dígitos", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verificar", - "changePassword": "Cambiar contraseña", - "currentPasswordLabel": "Contraseña actual", - "currentPasswordPlaceholder": "Contraseña actual", - "newPasswordLabel": "Nueva contraseña", - "newPasswordPlaceholder": "Nueva contraseña", - "confirmPasswordLabel": "Confirmar nueva contraseña", - "confirmPasswordPlaceholder": "Confirmar nueva contraseña", - "updatePassword": "Actualizar contraseña", - "createApiKeyTitle": "Crear Clave API", - "createApiKeyDescription": "Generar una nueva clave API para el acceso programático.", - "apiKeyNameLabel": "Nombre", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "opcional", - "cancel": "Cancelar", - "createKey": "Crear Clave", - "apiKeyCount": "Claves {{count}}", - "newKey": "Nueva Clave", - "noApiKeys": "Aún no hay claves API.", - "apiKeyActive": "Activo", - "apiKeyUsageHint": "Incluye tu clave en el", - "apiKeyUsageHintHeader": "cabecera.", - "apiKeyPermissionsHint": "Las claves heredan los permisos del usuario creador.", - "roleUser": "Usuario", - "authMethodDual": "Doble Auth", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Error al iniciar la configuración del TOTP", - "totpEnter6Digits": "Introduzca un código de 6 dígitos", - "totpEnabledSuccess": "Autenticación de doble factor habilitada", - "totpInvalidCode": "Código inválido, por favor inténtelo de nuevo", - "totpDisableInputRequired": "Introduzca su código TOTP o contraseña", - "totpDisabledSuccess": "Autenticación de doble factor desactivada", - "totpDisableFailed": "Error al desactivar 2FA", - "totpDisableTitle": "Desactivar 2FA", - "totpDisablePlaceholder": "Introduzca código TOTP o contraseña", - "totpDisableConfirm": "Desactivar 2FA", - "totpContinueVerify": "Continuar a verificar", - "totpVerifyTitle": "Verificar código", - "totpBackupTitle": "Copias de seguridad", - "totpDownloadBackup": "Descargar códigos de copia de seguridad", - "done": "Hecho", - "secretCopied": "secreto copiado al portapapeles", - "apiKeyNameRequired": "Se requiere nombre de clave", - "apiKeyCreated": "Clave API \"{{name}}\" creada", - "apiKeyCreateFailed": "Error al crear la clave API", - "apiKeyUser": "Usuario", - "apiKeyExpires": "Caduca", - "apiKeyRevoked": "Clave API \"{{name}}\" revocada", - "apiKeyRevokeFailed": "Error al revocar la clave API", - "passwordFieldsRequired": "Se requieren contraseñas actuales y nuevas", - "passwordMismatch": "Las contraseñas no coinciden", - "passwordTooShort": "La contraseña debe tener al menos 6 caracteres", - "passwordUpdated": "Contraseña actualizada correctamente", - "passwordUpdateFailed": "Error al actualizar la contraseña", - "deletePasswordRequired": "Se requiere contraseña para eliminar su cuenta", - "deleteFailed": "Error al eliminar la cuenta", - "deleting": "Eliminando...", - "colorPickerTooltip": "Abrir selector de color", - "themeSystem": "Sistema", - "themeLight": "Claro", - "themeDark": "Oscuro", - "themeDracula": "Drácula", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizado", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Una oscura", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/fi_FI.json b/src/ui/locales/translated/fi_FI.json index f9901c47..9bc5af32 100644 --- a/src/ui/locales/translated/fi_FI.json +++ b/src/ui/locales/translated/fi_FI.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Kansiot", - "folder": "Kansio", - "password": "Salasana", - "key": "Avain", - "sshPrivateKey": "Ssh Yksityinen Avain", - "upload": "Lähetä", - "keyPassword": "Avaimen Salasana", - "sshKey": "Ssh Avain", - "uploadPrivateKeyFile": "Lataa Yksityinen Avaintiedosto", - "searchCredentials": "Hae käyttäjätunnuksia...", - "addCredential": "Lisää Käyttöoikeustieto", - "caCertificate": "CA-todistus (-cert.pub)", - "caCertificateDescription": "Valinnainen: Lataa tai liitä CA-allekirjoitettu sertifikaattitiedosto (esim. id_ed25519-cert.pub). Vaaditaan kun SSH-palvelin käyttää sertifikaattipohjaista valtuutusta.", - "uploadCertFile": "Lataa -cert.pub-tiedosto", - "clearCert": "Tyhjennä", - "certLoaded": "Varmenne ladattu", - "certPublicKeyLabel": "Varmenne", - "certTypeLabel": "Varmenteen tyyppi", - "pasteOrUploadCert": "Liitä tai lataa -cert.pub sertifikaatti...", - "hasCaCert": "On CA-varmenne", - "noCaCert": "Ei CA-varmennetta" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Ilmoitusten lataaminen epäonnistui", - "failedToDismissAlert": "Hälytys epäonnistui" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Palvelimen Asetukset", - "description": "Määritä Termix-palvelimen URL-osoite yhdistääksesi taustaosan palveluihin", - "serverUrl": "Palvelimen URL", - "enterServerUrl": "Syötä palvelimen URL-osoite", - "saveFailed": "Konfiguraation tallennus epäonnistui", - "saveError": "Virhe tallennettaessa määritystä", - "saving": "Tallennetaan...", - "saveConfig": "Tallenna Asetukset", - "helpText": "Syötä URL, jossa Termix-palvelin on käynnissä (esim. http://localhost:30001 tai https://your-server.com)", - "changeServer": "Vaihda Palvelinta", - "mustIncludeProtocol": "Palvelimen URL-osoitteen on alettava http:// tai http://", - "allowInvalidCertificate": "Salli virheellinen varmenne", - "allowInvalidCertificateDesc": "Käytä vain luotettaville itse isännöidyille palvelimille, joissa on itse allekirjoitetut tai IP-osoitevarmenteet.", - "useEmbedded": "Käytä Paikallista Palvelinta", - "embeddedDesc": "Suorita Termix sisäänrakennetulla paikallisella palvelimella (etäpalvelinta ei tarvita)", - "embeddedConnecting": "Yhdistetään paikalliseen palvelimeen...", - "embeddedNotReady": "Paikallinen palvelin ei ole vielä valmis. Odota hetki ja yritä uudelleen.", - "localServer": "Paikallinen Palvelin" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Version Tarkistus Virhe", - "checkFailed": "Päivitysten tarkistus epäonnistui", - "upToDate": "Sovellus on ajan tasalla", - "currentVersion": "Käytössäsi on versio {{version}}", - "updateAvailable": "Päivitys Saatavilla", - "newVersionAvailable": "Uusi versio on saatavilla! Käytät {{current}}, mutta {{latest}} on saatavilla.", - "betaVersion": "Beta Versio", - "betaVersionDesc": "Käytössäsi on {{current}}, joka on uudempi kuin uusin vakaa julkaisu {{latest}}.", - "releasedOn": "Julkaistu {{date}}", - "downloadUpdate": "Lataa Päivitys", - "checking": "Tarkistetaan päivityksiä...", - "checkUpdates": "Tarkista päivitykset", - "checkingUpdates": "Tarkistetaan päivityksiä...", - "updateRequired": "Päivitys Vaaditaan" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Sulje", + "close": "Close", "minimize": "Minimize", - "online": "Paikalla", - "offline": "Offline-tilassa", - "continue": "Jatka", - "maintenance": "Huolto", - "degraded": "Hajotettu", - "error": "Virhe", - "warning": "Varoitus", - "unsavedChanges": "Tallentamattomat muutokset", - "dismiss": "Hylkää", - "loading": "Ladataan...", - "optional": "Valinnainen", - "connect": "Yhdistä", - "copied": "Kopioitu", - "connecting": "Yhdistetään...", - "updateAvailable": "Päivitys Saatavilla", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Avaa uudessa välilehdessä", - "noReleases": "Ei Julkaisuja", - "updatesAndReleases": "Päivitykset Ja Julkaisut", - "newVersionAvailable": "Uusi versio ({{version}}) on saatavilla.", - "failedToFetchUpdateInfo": "Päivitystietojen noutaminen epäonnistui", - "preRelease": "Esijulkaisu", - "noReleasesFound": "Julkaisuja ei löytynyt.", - "cancel": "Peruuta", - "username": "Käyttäjätunnus", - "login": "Kirjaudu", - "register": "Rekisteröidy", - "password": "Salasana", - "confirmPassword": "Vahvista Salasana", - "back": "Takaisin", - "save": "Tallenna", - "saving": "Tallennetaan...", - "delete": "Poista", - "rename": "Uudelleennimeä", - "edit": "Muokkaa", - "add": "Lisää", - "confirm": "Vahvista", - "no": "Ei", - "or": "TAI", - "next": "Seuraava", - "previous": "Edellinen", - "refresh": "Päivitä", - "language": "Kieli", - "checking": "Tarkistetaan...", - "checkingDatabase": "Tarkistetaan tietokantayhteyttä...", - "checkingAuthentication": "Tarkistetaan todennusta...", - "backendReconnected": "Palvelinyhteys palautettu", - "connectionDegraded": "Palvelinyhteys katkesi, palautetaan…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Poista", - "create": "Luo", - "update": "Päivitä", - "copy": "Kopioi", - "copyFailed": "Kopiointi leikepöydälle epäonnistui", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Palauta", - "of": "jostakin" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Koti", - "terminal": "Pääte", - "docker": "Telakoitsija", - "tunnels": "Tunnelit", - "fileManager": "Tiedostojen Hallinta", - "serverStats": "Palvelimen Tilastot", - "admin": "Ylläpitäjä", - "userProfile": "Käyttäjän Profiili", - "splitScreen": "Jaettu Näyttö", - "confirmClose": "Sulje tämä aktiivinen istunto?", - "close": "Sulje", - "cancel": "Peruuta", - "sshManager": "Ssh Hallinta", - "cannotSplitTab": "Välilehtiä ei voi jakaa", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopioi Salasana", - "copySudoPassword": "Kopioi Sudo Salasana", - "passwordCopied": "Salasana kopioitu leikepöydälle", - "noPasswordAvailable": "Salasanaa ei ole saatavilla", - "failedToCopyPassword": "Salasanan kopiointi epäonnistui", - "refreshTab": "Päivitä yhteys", - "openFileManager": "Avaa Tiedostonhallinta", - "dashboard": "Hallintapaneeli", - "networkGraph": "Verkkokaavio", - "quickConnect": "Nopea Yhdistäminen", - "sshTools": "SSH Työkalut", - "history": "Historia", - "hosts": "Isäntä", - "snippets": "Projekti", - "hostManager": "Isäntälaitteen Hallinta", - "credentials": "Käyttäjätunnukset", - "connections": "Yhteydet", - "roleAdministrator": "Ylläpitäjä", - "roleUser": "Käyttäjä" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Isäntä", - "noHosts": "Ei Ssh Isäntiä", - "retry": "Yritä Uudelleen", - "refresh": "Päivitä", - "optional": "Valinnainen", - "downloadSample": "Lataa Näyte", - "failedToDeleteHost": "{{name}} poisto epäonnistui", - "importSkipExisting": "Tuo (ohita olemassa)", - "connectionDetails": "Yhteyden Tiedot", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Etätyöpöytä", - "port": "Portti", - "username": "Käyttäjätunnus", - "folder": "Kansio", - "tags": "Tunnisteet", - "pin": "Kiinnitä", - "addHost": "Lisää Isäntä", - "editHost": "Muokkaa Palvelinta", - "cloneHost": "Kloonaa Isäntä", - "enableTerminal": "Ota Pääte Käyttöön", - "enableTunnel": "Ota Tunneli Käyttöön", - "enableFileManager": "Ota Tiedostonhallinta Käyttöön", - "enableDocker": "Ota Docker Käyttöön", - "defaultPath": "Oletus Polku", - "connection": "Yhteys", - "upload": "Lähetä", - "authentication": "Todennus", - "password": "Salasana", - "key": "Avain", - "credential": "Käyttöoikeustiedot", - "none": "Ei Mitään", - "sshPrivateKey": "Ssh Yksityinen Avain", - "keyType": "Avaimen Tyyppi", - "uploadFile": "Lataa Tiedosto", - "tabGeneral": "Yleiset", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunnelit", - "tabDocker": "Telakoitsija", - "tabFiles": "Tiedostot", - "tabStats": "Tilastot", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Jakaminen", - "tabAuthentication": "Todennus", - "terminal": "Pääte", - "tunnel": "Tunneli", - "fileManager": "Tiedostojen Hallinta", - "serverStats": "Palvelimen Tilastot", - "status": "Tila", - "folderRenamed": "Kansio \"{{oldName}}\" nimettiin uudelleen onnistuneesti muotoon \"{{newName}}\"", - "failedToRenameFolder": "Kansion uudelleennimeäminen epäonnistui", - "movedToFolder": "Siirretty kohteeseen \"{{folder}}\"", - "editHostTooltip": "Muokkaa isäntää", - "statusChecks": "Tilan Tarkastukset", - "metricsCollection": "Metriikan Kokoelma", - "metricsInterval": "Metrics Kokoelman Aikaväli", - "metricsIntervalDesc": "Kuinka usein palvelimia koskevia tilastoja kerätään (5s - 1h)", - "behavior": "Käyttäytyminen", - "themePreview": "Teeman Esikatselu", - "theme": "Teema", - "fontFamily": "Kirjasinperhe", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Kirje Väli", - "lineHeight": "Rivin Korkeus", - "cursorStyle": "Kohdistimen Tyyli", - "cursorBlink": "Kohdistimen Vilkutus", - "scrollbackBuffer": "Vierityspuskuri", - "bellStyle": "Äänimerkin Tyyli", - "rightClickSelectsWord": "Oikea Napsautus Valitsee Sanan", - "fastScrollModifier": "Nopea Vieritysmuokkain", - "fastScrollSensitivity": "Nopea Vieritä Herkkyys", - "sshAgentForwarding": "Ssh Agentti Eteenpäinvälitys", - "backspaceMode": "Askelpalautin Tila", - "startupSnippet": "Käynnistä Projekti", - "selectSnippet": "Valitse tiedosto", - "forceKeyboardInteractive": "Pakota Näppäimistön Interaktiivinen", - "overrideCredentialUsername": "Ohita Käyttöoikeustietojen Käyttäjätunnus", - "overrideCredentialUsernameDesc": "Käytä yllä määriteltyä käyttäjänimeä käyttäjätunnuksen sijaan", - "jumpHostChain": "Hyppää Isäntäketju", - "portKnocking": "Sataman Nukkuminen", - "addKnock": "Lisää Portti", - "addProxyNode": "Lisää Solmu", - "proxyNode": "Välityspalvelimen Solmu", - "proxyType": "Välityspalvelimen Tyyppi", - "quickActions": "Nopeat Toiminnot", - "sudoPasswordAutoFill": "Sudo Salasanan Automaattitäyttö", - "sudoPassword": "Sudo Salasana", - "keepaliveInterval": "Keepalive -väli (ms)", - "moshCommand": "MOSH Komento", - "environmentVariables": "Ympäristön Muuttujat", - "addVariable": "Lisää Muuttuja", - "docker": "Telakoitsija", - "copyTerminalUrl": "Kopioi Päätteen URL", - "copyFileManagerUrl": "Kopioi Tiedostonhallinnan URL", - "copyRemoteDesktopUrl": "Kopioi Työpöydän URL", - "failedToConnect": "Yhteyden muodostaminen konsoliin epäonnistui", - "connect": "Yhdistä", - "disconnect": "Katkaise", - "start": "Aloita", - "enableStatusCheck": "Ota Tilan Tarkistus Käyttöön", - "enableMetrics": "Ota Metrics Käyttöön", - "bulkUpdateFailed": "Massapäivitys epäonnistui", - "selectAll": "Valitse Kaikki", - "deselectAll": "Poista Kaikki Valinnat", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Turvallinen Tulkki", - "virtualNetwork": "Virtuaalinen Verkko", - "unencryptedShell": "Salaamaton tulkki", - "addressIp": "Osoite / IP", - "friendlyName": "Ystävällinen Nimi", - "folderAndAdvanced": "Kansio & Lisäasetukset", - "privateNotes": "Yksityiset Muistiinpanot", - "privateNotesPlaceholder": "Tämän palvelimen yksityiskohdat...", - "pinToTop": "Kiinnitä ylös", - "pinToTopDesc": "Näytä aina tämä isäntä listan yläreunassa", - "portKnockingSequence": "Portin Torkkujärjestys", - "addKnockBtn": "Lisää Torkku", - "noPortKnocking": "Portin koputtamista ei ole määritetty.", - "knockPort": "Knock Portti", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Viive Jälkeen (Ms)", - "useSocks5Proxy": "Käytä SOCKS5-välityspalvelinta", - "useSocks5ProxyDesc": "Reitin yhteys välityspalvelimen kautta", - "proxyHost": "Välityspalvelimen Isäntä", - "proxyPort": "Välityspalvelimen Portti", - "proxyUsername": "Välityspalvelimen Käyttäjänimi", - "proxyPassword": "Välityspalvelimen Salasana", - "proxySingleMode": "Yksi Välityspalvelin", - "proxyChainMode": "Välityspalvelimen Ketju", - "you": "Sinä", - "jumpHostChainLabel": "Hyppää Isäntäketju", - "addJumpBtn": "Lisää Hyppy", - "noJumpHosts": "Hyppypalvelimia ei ole määritetty.", - "selectAServer": "Valitse palvelin...", - "sshPort": "Ssh Portti", - "authMethod": "Todistusmenetelmä", - "storedCredential": "Tallennettu Käyttöoikeustieto", - "selectACredential": "Valitse käyttäjätunnus...", - "keyTypeLabel": "Avaimen Tyyppi", - "keyTypeAuto": "Tunnista Automaattisesti", - "keyPasteTab": "Liitä", - "keyUploadTab": "Lähetä", - "keyFileLoaded": "Avaintiedosto ladattu", - "keyUploadClick": "Klikkaa ladataksesi .pem / .key / .ppk", - "clearKey": "Tyhjennä avain", - "keySaved": "SSH avain tallennettu", - "keyReplaceNotice": "liitä uusi avain alla korvaamaan se", - "keyPassphraseSaved": "Tunnuslause tallennettu, kirjoita vaihtaaksesi", - "replaceKey": "Korvaa avain", - "forceKeyboardInteractiveLabel": "Pakota Näppäimistön Interaktiivinen", - "forceKeyboardInteractiveShortDesc": "Pakota manuaalinen salasanakirjaus, vaikka avaimet olisivat läsnä", - "terminalAppearance": "Pääteikkunan Ulkonäkö", - "colorTheme": "Väri Teema", - "fontFamilyLabel": "Kirjasinperhe", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Kohdistimen Tyyli", - "letterSpacingPx": "Kirje Väli (px)", - "lineHeightLabel": "Rivin Korkeus", - "bellStyleLabel": "Äänimerkin Tyyli", - "backspaceModeLabel": "Askelpalautin Tila", - "cursorBlinking": "Kohdistimen Vilkkuminen", - "cursorBlinkingDesc": "Ota käyttöön vilkkuva animaatio päätelaitteen kohdistimeen", - "rightClickSelectsWordLabel": "Oikea-klikkaus Valitsee Sanan", - "rightClickSelectsWordShortDesc": "Valitse sana kohdistimen alta hiiren kakkospainikkeella", - "behaviorAndAdvanced": "Käyttäytyminen Ja Lisäasetukset", - "scrollbackBufferLabel": "Vierityspuskuri", - "scrollbackMaxLines": "Historiassa säilytettyjen rivien enimmäismäärä", - "sshAgentForwardingLabel": "Ssh Agentti Eteenpäinvälitys", - "sshAgentForwardingShortDesc": "Ohita paikalliset SSH avaimet tähän isäntään", - "enableAutoMosh": "Ota AutomaattiMosh Käyttöön", - "enableAutoMoshDesc": "Suosi Mosh SSH jos saatavilla", - "enableAutoTmux": "Ota Automaattisesti Tmux Käyttöön", - "enableAutoTmuxDesc": "Käynnistä tai liitä automaattisesti tmux-istuntoon", - "sudoPasswordAutoFillLabel": "Sudo Salasana Täytä Automaattisesti", - "sudoPasswordAutoFillShortDesc": "Anna sudo-salasana automaattisesti pyydettäessä", - "sudoPasswordLabel": "Sudo Salasana", - "environmentVariablesLabel": "Ympäristön Muuttujat", - "addVariableBtn": "Lisää Muuttuja", - "noEnvVars": "Ympäristömuuttujia ei ole määritetty.", - "fastScrollModifierLabel": "Nopea Vieritysmuokkain", - "fastScrollSensitivityLabel": "Nopea Vieritä Herkkyys", - "moshCommandLabel": "Mosh Komento", - "startupSnippetLabel": "Käynnistä Projekti", - "keepaliveIntervalLabel": "Säilytysaika (sekuntia)", - "maxKeepaliveMisses": "Enimmäismäärä Keepalive Misses", - "tunnelSettings": "Tunnelin Asetukset", - "enableTunneling": "Ota Tunnistus Käyttöön", - "enableTunnelingDesc": "Ota käyttöön SSH tunnelin toiminnot tälle isännälle", - "serverTunnelsSection": "Palvelimen Tunnelit", - "addTunnelBtn": "Lisää Tunneli", - "noTunnelsConfigured": "Tunneleita ei ole määritetty.", - "tunnelLabel": "Tunneli {{number}}", - "tunnelType": "Tunnelin Tyyppi", - "tunnelModeLocalDesc": "Välitä paikallinen portti porttiin etäpalvelimella (tai palvelimella, johon se on saavutettavissa).", - "tunnelModeRemoteDesc": "Lähetä etäpalvelimella oleva portti takaisin koneesi paikalliseen porttiin.", - "tunnelModeDynamicDesc": "Luo paikalliseen porttiin SOCKS5-välityspalvelin dynaamista porttien huolintaa varten.", - "sameHost": "Tämä isäntä (suora tunneli)", - "endpointHost": "Päätepisteen Isäntä", - "endpointPort": "Päätepisteen Portti", - "bindHost": "Sido Isäntä", - "sourcePort": "Lähde Portti", - "maxRetries": "Maksimi Uudelleen", - "retryIntervalS": "Uudelleen Aikaväli (s)", - "autoStartLabel": "Automaattikäynnistys", - "autoStartDesc": "Yhdistä tämä tunneli automaattisesti kun isäntä on ladattu", - "tunnelConnecting": "Tunneli yhdistää...", - "tunnelDisconnected": "Tunneli katkaistu", - "failedToConnectTunnel": "Yhteyden muodostaminen epäonnistui", - "failedToDisconnectTunnel": "Yhteyden katkaisu epäonnistui", - "dockerIntegration": "Docker Integraatio", - "enableDockerMonitor": "Ota Docker Käyttöön", - "enableDockerMonitorDesc": "Tarkkaile ja hallitse tämän isännän kontteja Dockerin kautta", - "enableFileManagerMonitor": "Ota Tiedostonhallinta Käyttöön", - "enableFileManagerMonitorDesc": "Selaa ja hallitse tiedostoja tässä palvelimessa SFTP:n kautta", - "defaultPathLabel": "Oletus Polku", - "fileManagerPathHint": "Hakemisto, joka avataan, kun tiedostonhallinta käynnistyy tälle palvelimelle.", - "statusChecksLabel": "Tilan Tarkastukset", - "enableStatusChecks": "Ota Tilatarkastukset Käyttöön", - "enableStatusChecksDesc": "Pellamalla tätä isäntää määräajoin varmistaaksesi käytettävyyden", - "useGlobalInterval": "Käytä Globaalia Aikaväliä", - "useGlobalIntervalDesc": "Ohita palvelimen koko tilan tarkistusväli", - "checkIntervalS": "Tarkastusväli (s)", - "checkIntervalDesc": "Sekuntia jokaisen yhteyden pingin välillä", - "metricsCollectionLabel": "Metriikan Kokoelma", - "enableMetricsLabel": "Ota Metrics Käyttöön", - "enableMetricsDesc": "Kerää suoritin, RAM, levy ja verkon käyttö tältä palvelimelta", - "useGlobalMetrics": "Käytä Globaalia Aikaväliä", - "useGlobalMetricsDesc": "Ohita palvelimen leveä mittausväli", - "metricsIntervalS": "Metrics Aikaväli (s)", - "metricsIntervalDesc2": "Sekuntia metristen tilannekuvien välillä", - "visibleWidgets": "Näkyvät Widgetit", - "cpuUsageLabel": "Suorittimen Käyttö", - "cpuUsageDesc": "Prosessin prosentti, kuorman keskiarvot, kipinäviiva", - "memoryLabel": "Muistin Käyttö", - "memoryDesc": "RAM-muistin käyttö, swap, välimuistissa", - "storageLabel": "Levyn Käyttö", - "storageDesc": "Levyn käyttö kiinnityspistettä kohden", - "networkLabel": "Verkon Liitännät", - "networkDesc": "Käyttöliittymän luettelo ja kaistanleveys", - "uptimeLabel": "Käyttöaika", - "uptimeDesc": "Järjestelmän käyttöaika ja käynnistysaika", - "systemInfoLabel": "Järjestelmän Tiedot", - "systemInfoDesc": "Käyttöjärjestelmä, ytimen, isäntänimi, arkkitehtuuri", - "recentLoginsLabel": "Viimeisimmät Kirjautumistiedot", - "recentLoginsDesc": "Onnistuneet ja epäonnistuneet kirjautumistapahtumat", - "topProcessesLabel": "Parhaat Prosessit", - "topProcessesDesc": "PID, CPU%, MEM%, komento", - "listeningPortsLabel": "Kuunnellaan Satamia", - "listeningPortsDesc": "Avoimet portit prosessin ja tilan kanssa", - "firewallLabel": "Palomuuri", - "firewallDesc": "Palomuuli, AppArmor, SELinux tila", - "quickActionsLabel": "Nopeat Toiminnot", - "quickActionsToolbar": "Pikatoiminnot näkyvät painikkeina palvelimen tilastojen työkalurivissä, kun yhden napsautuksen komento suoritetaan.", - "noQuickActions": "Ei vielä nopeita toimintoja.", - "buttonLabel": "Painikkeen nimi", - "selectSnippetPlaceholder": "Valitse leikkaus...", - "addActionBtn": "Lisää Toiminto", - "hostSharedSuccessfully": "Isäntä jaettu onnistuneesti", - "failedToShareHost": "Palvelimen jakaminen epäonnistui", - "accessRevoked": "Pääsy peruttu", - "failedToRevokeAccess": "Käyttöoikeuksien peruuttaminen epäonnistui", - "cancelBtn": "Peruuta", - "savingBtn": "Tallennetaan...", - "addHostBtn": "Lisää Isäntä", - "hostUpdated": "Isäntä päivitetty", - "hostCreated": "Isäntä luotu", - "failedToSave": "Palvelimen tallentaminen epäonnistui", - "credentialUpdated": "Käyttöoikeustiedot päivitetty", - "credentialCreated": "Tekijä luotu", - "failedToSaveCredential": "Käyttäjätunnuksen tallentaminen epäonnistui", - "backToHosts": "Takaisin isäntiin", - "backToCredentials": "Takaisin käyttäjätunnuksiin", - "pinned": "Kiinnitetty", - "noHostsFound": "Isäntää ei löytynyt", - "tryDifferentTerm": "Kokeile toista termiä", - "addFirstHost": "Lisää ensimmäinen isäntä aloittaaksesi", - "noCredentialsFound": "Kirjautumistietoja ei löytynyt", - "addCredentialBtn": "Lisää Käyttöoikeustieto", - "updateCredentialBtn": "Päivitä Käyttöoikeustiedot", - "features": "Ominaisuudet", - "noFolder": "(Ei kansiota)", - "deleteSelected": "Poista", - "exitSelection": "Poistu valinnasta", - "importSkip": "Tuo (ohita olemassa)", - "importOverwrite": "Tuonti (ylikirjoitettu)", - "collapseBtn": "Kutista", - "importExportBtn": "Tuo / Vie", - "hostStatusesRefreshed": "Isäntätilan päivitys", - "failedToRefreshHosts": "Ei voitu päivittää isäntiä", - "movedHostTo": "Siirretty {{host}} kohtaan \"{{folder}}\"", - "failedToMoveHost": "Palvelimen siirto epäonnistui", - "folderRenamedTo": "Kansio nimetty uudelleen \"{{name}}\"", - "deletedFolder": "Poistettu kansio \"{{name}}\"", - "failedToDeleteFolder": "Kansion poistaminen epäonnistui", - "deleteAllInFolder": "Poista kaikki isännät \"{{name}}\"? Tätä ei voi perua.", - "deletedHost": "Poistettu {{name}}", - "copiedToClipboard": "Kopioitu leikepöydälle", - "terminalUrlCopied": "Pääteikkunan URL kopioitu", - "fileManagerUrlCopied": "Tiedostonhallinnan URL kopioitu", - "tunnelUrlCopied": "Tunnelin URL kopioitu", - "dockerUrlCopied": "Docker URL kopioitu", - "serverStatsUrlCopied": "Palvelimen tilastojen URL kopioitu", - "rdpUrlCopied": "RDP URL kopioitu", - "vncUrlCopied": "VNC URL kopioitu", - "telnetUrlCopied": "Telnet URL kopioitu", - "remoteDesktopUrlCopied": "Etätyöpöydän URL kopioitu", - "expandActions": "Laajenna toimintoja", - "collapseActions": "Tiivistä toiminnot", - "wakeOnLanAction": "Herätys lähiverkossa", - "wakeOnLanSuccess": "Taikapaketti lähetetty osoitteeseen {{name}}", - "wakeOnLanError": "Taikapaketin lähettäminen epäonnistui", - "cloneHostAction": "Kloonaa Isäntä", - "copyAddress": "Kopioi Osoite", - "copyLink": "Kopioi Linkki", - "copyTerminalUrlAction": "Kopioi Päätteen URL", - "copyFileManagerUrlAction": "Kopioi Tiedostonhallinnan URL", - "copyTunnelUrlAction": "Kopioi Tunnelin URL", - "copyDockerUrlAction": "Kopioi Docker URL", - "copyServerStatsUrlAction": "Kopioi Palvelimen Tilastot Url", - "copyRdpUrlAction": "Kopioi RDP URL", - "copyVncUrlAction": "Kopioi VNC URL", - "copyTelnetUrlAction": "Kopioi Telnet URL", - "copyRemoteDesktopUrlAction": "Kopioi Työpöydän URL", - "deleteCredentialConfirm": "Poista käyttäjätunnus \"{{name}}\"?", - "deletedCredential": "Poistettu {{name}}", - "deploySSHKeyTitle": "Julkaise Ssh Avain", - "deployingBtn": "Otetaan Käyttöön...", - "deployBtn": "Julkaise", - "failedToDeployKey": "Avaimen käyttöönotto epäonnistui", - "deleteHostsConfirm": "Poistetaanko {{count}} isäntä{{plural}}? Tätä ei voi perua.", - "movedToRoot": "Siirretty juuriin", - "failedToMoveHosts": "Isäntäpisteiden siirto epäonnistui", - "enableTerminalFeature": "Ota Pääte Käyttöön", - "disableTerminalFeature": "Poista Pääte Käytöstä", - "enableFilesFeature": "Ota Tiedostot Käyttöön", - "disableFilesFeature": "Poista Tiedostot Käytöstä", - "enableTunnelsFeature": "Salli Tunnelit", - "disableTunnelsFeature": "Poista Tunnelit Käytöstä", - "enableDockerFeature": "Ota Docker Käyttöön", - "disableDockerFeature": "Poista Docker Käytöstä", - "addTagsPlaceholder": "Lisää tunnisteita...", - "authDetails": "Todennuksen Tiedot", - "credType": "Tyyppi", - "generateKeyPairDesc": "Luo uusi avainpari, sekä yksityiset että julkiset avaimet täytetään automaattisesti.", - "generatingKey": "Luodaan...", - "generateLabel": "Luo {{label}}", - "uploadFileBtn": "Lataa tiedosto", - "keyPassphraseOptional": "Avaimen Tunnuslause (Valinnainen)", - "sshPublicKeyOptional": "Ssh Julkinen Avain (Valinnainen)", - "publicKeyGenerated": "Julkinen avain luotu", - "failedToGeneratePublicKey": "Julkisen avaimen johtaminen epäonnistui", - "publicKeyCopied": "Julkinen avain kopioitu", - "keyPairGenerated": "{{label}} avainpari luotu", - "failedToGenerateKeyPair": "Avainparin luonti epäonnistui", - "searchHostsPlaceholder": "Etsi isäntiä, osoitteita, tunnisteita…", - "searchCredentialsPlaceholder": "Hae käyttäjätunnuksia…", - "refreshBtn": "Päivitä", - "addTag": "Lisää tunnisteita...", - "deleteConfirmBtn": "Poista", - "tunnelRequirementsText": "SSH-palvelimella on oltava GatewayPorts kyllä, AllowTcpForwarding kyllä, ja PermitRootLogin kyllä asetettu /etc/ssh/sshd_config.", - "deleteHostConfirm": "Poista \"{{name}}\"?", - "enableAtLeastOneProtocol": "Ota käyttöön vähintään yksi yllä oleva protokolla määrittääksesi todennuksen ja yhteysasetukset.", - "keyPassphrase": "Avaimen Tunnuslause", - "connectBtn": "Yhdistä", - "disconnectBtn": "Katkaise", - "basicInformation": "Perustiedot", - "authDetailsSection": "Todennuksen Tiedot", - "credTypeLabel": "Tyyppi", - "hostsTab": "Isäntä", - "credentialsTab": "Käyttäjätunnukset", - "selectMultiple": "Valitse useita", - "selectHosts": "Valitse isännät", - "connectionLabel": "Yhteys", - "authenticationLabel": "Todennus", - "generateKeyPairTitle": "Luo Avaimen Pari", - "generateKeyPairDescription": "Luo uusi avainpari, sekä yksityiset että julkiset avaimet täytetään automaattisesti.", - "generateFromPrivateKey": "Luo yksityisellä avaimella", - "refreshBtn2": "Päivitä", - "exitSelectionTitle": "Poistu valinnasta", - "exportAll": "Vie Kaikki", - "addHostBtn2": "Lisää Isäntä", - "addCredentialBtn2": "Lisää Käyttöoikeustieto", - "checkingHostStatuses": "Tarkistetaan isäntätiloja...", - "pinnedSection": "Kiinnitetty", - "hostsExported": "Isäntä viety onnistuneesti", - "exportFailed": "Isäntien vienti epäonnistui", - "sampleDownloaded": "Mallitiedosto ladattu", - "failedToDeleteCredential2": "Käyttäjätunnuksen poistaminen epäonnistui", - "noFolderOption": "(Ei kansiota)", - "nSelected": "{{count}} valittu", - "featuresMenu": "Ominaisuudet", - "moveMenu": "Siirrä", - "cancelSelection": "Peruuta", - "deployDialogDesc": "Julkaise {{name}} isännän authorized_keys.", - "targetHostLabel": "Kohde Isäntä", - "selectHostOption": "Valitse isäntä...", - "keyDeployedSuccess": "Avaimen käyttöönotto onnistui", - "failedToDeployKey2": "Avaimen käyttöönotto epäonnistui", - "deletedCount": "Poistettu {{count}} isäntä", - "failedToDeleteCount": "{{count}} isäntien poistaminen epäonnistui", - "duplicatedHost": "Monistettu \"{{name}}\"", - "failedToDuplicateHost": "Palvelimen kopiointi epäonnistui", - "updatedCount": "Päivitetään {{count}} isäntää", - "friendlyNameLabel": "Ystävällinen Nimi", - "descriptionLabel": "Kuvaus", - "loadingHost": "Ladataan konetta...", - "loadingHosts": "Ladataan isäntiä...", - "loadingCredentials": "Ladataan käyttäjätietoja...", - "noHostsYet": "Ei vielä isäntiä", - "noHostsMatchSearch": "Yksikään isäntä ei vastaa hakuasi", - "hostNotFound": "Palvelinta ei löydy", - "searchHosts": "Etsi palvelimia...", - "sortHosts": "Lajittele isännät", - "sortDefault": "Oletusjärjestys", - "sortNameAsc": "Nimi (A → Ö)", - "sortNameDesc": "Nimi (Ö → A)", - "sortIpAsc": "IP-osoite (nouseva)", - "sortIpDesc": "IP-osoite (kuvaus)", - "sortOnlineFirst": "Verkossa ensin", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Kiinnitetty ensin", - "filterHosts": "Suodata isännät", - "filterClearAll": "Tyhjennä suodattimet", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "Verkossa", - "filterOffline": "Offline-tilassa", - "filterPinned": "Kiinnitetty", - "filterAuthGroup": "Valtuutustyyppi", - "filterAuthPassword": "Salasana", - "filterAuthKey": "SSH-avain", - "filterAuthCredential": "Valtakirja", - "filterAuthNone": "Ei mitään", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokolla", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "Maaseudun kehittämisohjelma", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Ominaisuudet", - "filterFeatureTerminal": "Terminaali", - "filterFeatureFileManager": "Tiedostonhallinta", - "filterFeatureTunnel": "Tunneli", - "filterFeatureDocker": "Satamatyöläinen", - "filterTagsGroup": "Tunnisteet", - "shareHost": "Jaa Isäntä", - "shareHostTitle": "Jaa: {{name}}", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Tämän isännän on käytettävä käyttäjätunnusta ottaakseen jakamisen käyttöön. Muokkaa isäntää ja määritä käyttäjätunnus ensin." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Yhteys", - "authentication": "Todennus", - "connectionSettings": "Yhteyden Asetukset", - "displaySettings": "Näytä Asetukset", - "audioSettings": "Äänen Asetukset", - "rdpPerformance": "RDP Suorituskyky", - "deviceRedirection": "Laitteen Uudelleenohjaus", - "session": "Istunto", - "gateway": "Yhdyskäytävä", - "remoteApp": "Etäsovellus", - "clipboard": "Leikepöytä", - "sessionRecording": "Istunnon Tallennus", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Vnc Asetukset", - "terminalSettings": "Pääteikkunan Asetukset", - "rdpPort": "RDP Portti", - "username": "Käyttäjätunnus", - "password": "Salasana", - "domain": "Verkkotunnus", - "securityMode": "Turvallisuus Tila", - "colorDepth": "Värin Syvyys", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Korkeus", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Muuta Metodin Kokoa", - "clientName": "Asiakkaan Nimi", - "initialProgram": "Alkuperäinen Ohjelma", - "serverLayout": "Palvelimen Asettelu", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Yhdyskäytävän Portti", - "gatewayUsername": "Yhdyskäytävän Käyttäjänimi", - "gatewayPassword": "Gatewayn Salasana", - "gatewayDomain": "Yhdyskäytävän Verkkotunnus", - "remoteAppProgram": "RemoteApp Ohjelma", - "workingDirectory": "Työhakemisto", - "arguments": "Argumentit", - "normalizeLineEndings": "Normalisoi Rivimääritykset", - "recordingPath": "Tallennuksen Polku", - "recordingName": "Tallennuksen Nimi", - "macAddress": "Mapc Osoite", - "broadcastAddress": "Lähetyksen Osoite", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Odotusaika (s)", - "driveName": "Aseman Nimi", - "drivePath": "Aseman Polku", - "ignoreCertificate": "Ohita Sertifikaatti", - "ignoreCertificateDesc": "Salli yhteydet isäntiin, joilla on itseallekirjoitetut varmenteet", - "forceLossless": "Pakota Häviötön", - "forceLosslessDesc": "Pakota häviötön kuvakoodaus (parempi laatu, kaistanleveys)", - "disableAudio": "Poista Ääni Käytöstä", - "disableAudioDesc": "Mykistä kaikki ääni etäistunnosta", - "enableAudioInput": "Ota Äänisyöttö Käyttöön (Mikrofoni)", - "enableAudioInputDesc": "Välitä paikallinen mikrofoni kaukosäätimelle", - "wallpaper": "Taustakuva", - "wallpaperDesc": "Näytä työpöydän taustakuva (pois käytöstä parantaa suorituskykyä)", - "theming": "Teema", - "themingDesc": "Käytä visuaalisia teemoja ja tyylejä", - "fontSmoothing": "Kirjasimen Pehmennys", - "fontSmoothingDesc": "Ota tyhjennystyypin kirjasinrenderointi käyttöön", - "fullWindowDrag": "Koko Ikkunan Vedä", - "fullWindowDragDesc": "Näytä ikkunan sisältö vedettäessä", - "desktopComposition": "Työpöydän Kokoonpano", - "desktopCompositionDesc": "Ota käyttöön Aeron lasitehosteet", - "menuAnimations": "Valikon Animaatiot", - "menuAnimationsDesc": "Ota valikon häivytys ja diaanimaatiot käyttöön", - "disableBitmapCaching": "Poista Bittikarttavälimuisti Käytöstä", - "disableBitmapCachingDesc": "Poista bittikarttavälimuisti käytöstä (voi auttaa glitchesissä)", - "disableOffscreenCaching": "Poista Offscreen Välimuisti Käytöstä", - "disableOffscreenCachingDesc": "Poista näytön välimuisti käytöstä", - "disableGlyphCaching": "Poista Glyph Välimuisti Käytöstä", - "disableGlyphCachingDesc": "Poista glyph välimuisti käytöstä", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Käytä RemoteFX grafiikkaputkea", - "enablePrinting": "Käytä Tulostusta", - "enablePrintingDesc": "Ohjaa paikalliset tulostimet etäistuntoon", - "enableDriveRedirection": "Ota Käyttöön Aseman Uudelleenohjaus", - "enableDriveRedirectionDesc": "Kartta paikallinen kansio asemaksi etäistunnossa", - "createDrivePath": "Luo Asemapolku", - "createDrivePathDesc": "Luo kansio automaattisesti, jos sitä ei ole olemassa", - "disableDownload": "Poista Lataus Käytöstä", - "disableDownloadDesc": "Estä tiedostojen lataaminen etäistunnosta", - "disableUpload": "Poista Lataus Käytöstä", - "disableUploadDesc": "Estä tiedostojen lataaminen etäistuntoon", - "enableTouch": "Ota Kosketus Käyttöön", - "enableTouchDesc": "Ota kosketussyötteen siirto käyttöön", - "consoleSession": "Konsolin Istunto", - "consoleSessionDesc": "Yhdistä konsoliin (istunto 0) uuden istunnon sijaan", - "sendWolPacket": "Lähetä WOL-paketti", - "sendWolPacketDesc": "Lähetä maaginen paketti herättääksesi tämän isännän ennen yhdistämistä", - "disableCopy": "Poista Kopiointi Käytöstä", - "disableCopyDesc": "Estä tekstin kopiointi etäistunnosta", - "disablePaste": "Poista Liitä", - "disablePasteDesc": "Estä tekstin liittäminen etäistuntoon", - "createPathIfMissing": "Luo polku puuttuessa", - "createPathIfMissingDesc": "Luo automaattisesti tallennuskansio", - "excludeOutput": "Sulje Ulostulo", - "excludeOutputDesc": "Älä nauhoita näytön ulostuloa (vain metatiedot)", - "excludeMouse": "Ohita Hiiri", - "excludeMouseDesc": "Älä nauhoita hiiren liikkeitä", - "includeKeystrokes": "Sisällytä Avaimet", - "includeKeystrokesDesc": "Tallenna raa'at näppäimistöt näytön lisäksi", - "vncPort": "Vnc Portti", - "vncPassword": "Vnc Salasana", - "vncUsernameOptional": "Käyttäjänimi (valinnainen)", - "vncLeaveBlank": "Jätä tyhjäksi, jos ei vaadita", - "cursorMode": "Kohdistimen Tila", - "swapRedBlue": "Vaihda Punainen/Sininen", - "swapRedBlueDesc": "Vaihda punaisen ja sinisen värin kanavat (korjaa joitakin väriongelmia)", - "readOnly": "Vain Lukutilassa", - "readOnlyDesc": "Näytä etänäyttö lähettämättä mitään syötettä", - "telnetPort": "Telnet Portti", - "terminalType": "Päätteen Tyyppi", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Väriteema", - "backspaceKey": "Askelpalautin Avain", - "saveHostFirst": "Tallenna isäntä ensin.", - "sharingOptionsAfterSave": "Jakamisen asetukset ovat käytettävissä kun isäntä on tallennettu.", - "sharingLoadError": "Jakamisen tietojen lataaminen epäonnistui. Tarkista yhteytesi ja yritä uudelleen.", - "shareHostSection": "Jaa Isäntä", - "shareWithUser": "Jaa käyttäjän kanssa", - "shareWithRole": "Jaa roolin kanssa", - "selectUser": "Valitse Käyttäjä", - "selectRole": "Valitse Rooli", - "selectUserOption": "Valitse käyttäjä...", - "selectRoleOption": "Valitse rooli...", - "permissionLevel": "Käyttöoikeuden Taso", - "expiresInHours": "Vanhenee (tunteina)", - "noExpiryPlaceholder": "Jätä tyhjäksi ei vanhene", - "shareBtn": "Jaa", - "currentAccess": "Nykyinen Käyttöoikeus", - "typeHeader": "Tyyppi", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Käyttöoikeus", - "grantedByHeader": "Myöntänyt", - "expiresHeader": "Vanhenee", - "noAccessEntries": "Ei oikeuksia vielä.", - "expiredLabel": "Vanhentunut", - "neverLabel": "Ei Koskaan", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Peruuta", - "savingBtn": "Tallennetaan...", - "updateHostBtn": "Päivitä Isäntä", - "addHostBtn": "Lisää Isäntä" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Hae isäntiä, komentoja tai asetuksia...", - "quickActions": "Nopeat Toiminnot", - "hostManager": "Isäntälaitteen Hallinta", - "hostManagerDesc": "Hallitse tai lisää tai muokkaa isäntiä", - "addNewHost": "Lisää Uusi Isäntä", - "addNewHostDesc": "Rekisteröi uusi isäntä", - "adminSettings": "Ylläpitäjän Asetukset", - "adminSettingsDesc": "Määritä järjestelmän asetukset ja käyttäjät", - "userProfile": "Käyttäjän Profiili", - "userProfileDesc": "Hallitse tiliäsi ja asetuksiasi", - "addCredential": "Lisää Käyttöoikeustieto", - "addCredentialDesc": "Tallenna SSH näppäimet tai salasanat", - "recentActivity": "Viimeaikainen Toiminta", - "serversAndHosts": "Palvelimet Ja Palvelimet", - "noHostsFound": "Vastaavia palvelimia ei löytynyt \"{{search}}\"", - "links": "Linkit", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Valitse", - "toggleWith": "Vaihda käyttäen" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Yhtään välilehteä ei määritetty", - "focusedPane": "Aktiivinen ruutu" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Ei yhteyksiä", - "noConnectionsDesc": "Avaa pääte, tiedostonhallinta tai etätyöpöytä nähdäksesi yhteydet täällä", - "connectedFor": "Yhdistetty kohteeseen {{duration}}", - "connected": "Yhdistetty", - "disconnected": "Yhteys katkennut", - "closeTab": "Sulje välilehti", - "closeConnection": "Sulje yhteys", - "forgetTab": "Unohtaa", - "removeBackground": "Poistaa", - "reconnect": "Yhdistä uudelleen", - "reopenTab": "Avaa uudelleen", - "sectionOpen": "Avata", - "sectionBackground": "Tausta", - "backgroundDesc": "Istunto jatkuu 30 minuuttia yhteyden katkaisemisen jälkeen, ja se voidaan yhdistää uudelleen.", - "persisted": "Jatkui taustalla", - "expiresIn": "Vanhenee {{duration}} kuluttua", - "search": "Hae yhteyksiä...", - "noSearchResults": "Ei hakuasi vastaavia yhteyksiä" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Yhdistetään {{type}} istuntoon...", - "connectionError": "Virhe yhteydenpidossa", - "connectionFailed": "Yhteys epäonnistui", - "failedToConnect": "Yhteyden tunnuksen haku epäonnistui", - "hostNotFound": "Palvelinta ei löydy", - "noHostSelected": "Ei isäntää valittuna", - "reconnect": "Yhdistä Uudelleen", - "retry": "Yritä Uudelleen", - "guacdUnavailable": "Etätyöpöytäpalvelu (guacd) ei ole käytettävissä. Varmista, että guacd on käynnissä ja käytettävissä ja että se on määritetty asianmukaisesti järjestelmänvalvojan asetuksissa.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Lukitusnäyttö)", - "winKey": "Ikkunan Avain", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Vaihto", - "win": "Voita", - "stickyActive": "{{key}} (pakattu - napsauta vapauttaaksesi)", - "stickyInactive": "{{key}} (napauta latch)", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", "esc": "Escape", "tab": "Tab", - "home": "Koti", - "end": "Loppu", - "pageUp": "Sivu Ylös", - "pageDown": "Sivu Alas", - "arrowUp": "Nuoli Ylös", - "arrowDown": "Nuoli Alas", - "arrowLeft": "Nuoli Vasemmalle", - "arrowRight": "Nuoli Oikealle", - "fnToggle": "Funktion Avaimet", - "reconnect": "Yhdistä Istunto Uudelleen", - "collapse": "Tiivistä työkalupalkki", - "expand": "Laajenna työkalupalkki", - "dragHandle": "Vedä uudelleensijoittaaksesi" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Yhdistä palvelimeen", - "clear": "Tyhjennä", - "paste": "Liitä", - "reconnect": "Yhdistä Uudelleen", - "connectionLost": "Yhteys katkesi", - "connected": "Yhdistetty", - "clipboardWriteFailed": "Kopiointi leikepöydälle epäonnistui. Varmista, että sivu on tarjolla HTTPS:n tai localhostin kautta.", - "clipboardReadFailed": "Leikepöydältä ei voitu lukea. Varmista, että leikepöydän oikeudet on myönnetty.", - "clipboardHttpWarning": "Liitä vaatii HTTPS. Käytä Ctrl+Shift+V tai tarjoile Termix HTTPS:n päällä.", - "unknownError": "Tuntematon virhe tapahtui", - "websocketError": "WebSocketin yhteysvirhe", - "connecting": "Yhdistetään...", - "noHostSelected": "Ei isäntää valittuna", - "reconnecting": "Yhdistetään Uudelleen... ({{attempt}}/{{max}})", - "reconnected": "Yhdistetty uudelleen onnistuneesti", - "tmuxSessionCreated": "tmux-istunto luotu: {{name}}", - "tmuxSessionAttached": "tmux-istunto liitteenä: {{name}}", - "tmuxUnavailable": "tmuxia ei ole asennettu kaukosäätimessä olevaan isäntään, joka palautuu vakiomuotoiseen komentotulkkiin", - "tmuxSessionPickerTitle": "tmux-istunnot", - "tmuxSessionPickerDesc": "Olemassa olevat tmux-istunnot löytyvät tästä isännästä. Valitse istunto, joka liitetään uudelleen tai luodaan uusi istunto.", - "tmuxWindows": "Ikkunat", - "tmuxWindowCount": "{{count}} ikkuna", - "tmuxAttached": "Liitetyt asiakkaat", - "tmuxAttachedCount": "{{count}} liitetty", - "tmuxLastActivity": "Viimeisin toiminta", - "tmuxTimeJustNow": "juuri nyt", - "tmuxTimeMinutes": "{{count}}m sitten", - "tmuxTimeHours": "{{count}}tuntia sitten", - "tmuxTimeDays": "{{count}}pv sitten", - "tmuxCreateNew": "Aloita uusi istunto", - "tmuxCopyHint": "Säädä valintaa ja paina Enter kopioidaksesi leikepöydälle", - "tmuxDetach": "Irrota tmux-istunnosta", - "tmuxDetached": "Irrotettu tmux-istunnosta", - "maxReconnectAttemptsReached": "Suurin uudelleenyhdistämisyritys saavutettu", - "closeTab": "Sulje", - "connectionTimeout": "Yhteyden aikakatkaisu", - "terminalTitle": "Pääte - {{host}}", - "terminalWithPath": "Pääte - {{host}}:{{path}}", - "runTitle": "Juoksemassa {{command}} - {{host}}", - "totpRequired": "Kaksivaiheinen Todennus Vaaditaan", - "totpCodeLabel": "Vahvistuskoodi", - "totpVerify": "Vahvista", - "warpgateAuthRequired": "Varmennus Vaaditaan", - "warpgateSecurityKey": "Turvallisuus Avain", - "warpgateAuthUrl": "Todennuksen URL", - "warpgateOpenBrowser": "Avaa selaimessa", - "warpgateContinue": "Olen Suorittanut Todentamisen", - "opksshAuthRequired": "OPKSSH Todennus Vaaditaan", - "opksshAuthDescription": "Lopeta todennus selaimessasi jatkaaksesi. Tämä istunto pysyy voimassa 24 tuntia.", - "opksshOpenBrowser": "Avaa selain todennettavaksi", - "opksshWaitingForAuth": "Odotetaan todennusta selaimessa...", - "opksshAuthenticating": "Käsitellään todennusta...", - "opksshTimeout": "Todennus aikakatkaistiin. Yritä uudelleen.", - "opksshAuthFailed": "Todennus epäonnistui. Tarkista käyttäjätunnuksesi ja yritä uudelleen.", - "opksshSignInWith": "Kirjaudu sisään {{provider}} avulla", - "sudoPasswordPopupTitle": "Lisää Salasana?", - "websocketAbnormalClose": "Yhteys suljettiin odottamattomasti. Tämä saattaa johtua käänteisestä välityspalvelusta tai SSL-asetuksista. Tarkista palvelimen lokit.", - "connectionLogTitle": "Yhteyden Loki", - "connectionLogCopy": "Kopioi lokit leikepöydälle", - "connectionLogEmpty": "Ei vielä yhteyslokeja", - "connectionLogWaiting": "Odotetaan yhteyslokeja...", - "connectionLogCopied": "Yhteyden lokit kopioitu leikepöydälle", - "connectionLogCopyFailed": "Lokien kopiointi leikepöydälle epäonnistui", - "connectionRejected": "Palvelin hylkäsi yhteyden. Tarkista todennus ja verkon asetukset.", - "hostKeyRejected": "SSH isäntäavaimen vahvistus hylätty. Yhteys peruutettu.", - "sessionTakenOver": "Istunto avattiin toisessa välilehdessä. Yhdistetään uudelleen..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Ei isäntää valittuna", - "initializingEditor": "Alustetaan editoriaa...", - "file": "Tiedosto", - "folder": "Kansio", - "uploadFile": "Lataa Tiedosto", - "downloadFile": "Lataa", - "extractArchive": "Pura Arkisto", - "extractingArchive": "Puretaan {{name}}...", - "archiveExtractedSuccessfully": "{{name}} purettu onnistuneesti", - "extractFailed": "Poisto epäonnistui", - "compressFile": "Pakkaa Tiedosto", - "compressFiles": "Pakkaa Tiedostoja", - "compressFilesDesc": "Pakkaa {{count}} kohdetta arkistoon", - "archiveName": "Arkiston Nimi", - "enterArchiveName": "Kirjoita arkiston nimi...", - "compressionFormat": "Pakkausmuoto", - "selectedFiles": "Valitut tiedostot", - "andMoreFiles": "ja {{count}} lisää...", - "compress": "Pakkaa", - "compressingFiles": "Pakkaamalla {{count}} alkiota {{name}}...", - "filesCompressedSuccessfully": "{{name}} luotu onnistuneesti", - "compressFailed": "Pakkaus epäonnistui", - "edit": "Muokkaa", - "preview": "Esikatselu", - "previous": "Edellinen", - "next": "Seuraava", - "pageXOfY": "Sivu {{current}} / {{total}}", - "zoomOut": "Zoomaa Ulos", - "zoomIn": "Zoomaa Sisään", - "newFile": "Uusi Tiedosto", - "newFolder": "Uusi Kansio", - "rename": "Uudelleennimeä", - "uploading": "Lähetetään...", - "uploadingFile": "Ladataan {{name}}...", - "fileName": "Tiedoston Nimi", - "folderName": "Kansion Nimi", - "fileUploadedSuccessfully": "Tiedosto \"{{name}}\" ladattu onnistuneesti", - "failedToUploadFile": "Tiedoston lataaminen epäonnistui", - "fileDownloadedSuccessfully": "Tiedosto \"{{name}}\" ladattu onnistuneesti", - "failedToDownloadFile": "Tiedoston lataaminen epäonnistui", - "fileCreatedSuccessfully": "Tiedosto \"{{name}}\" luotu onnistuneesti", - "folderCreatedSuccessfully": "Kansio \"{{name}}\" luotu onnistuneesti", - "failedToCreateItem": "Kohteen luominen epäonnistui", - "operationFailed": "{{operation}} toiminto epäonnistui kohteelle {{name}}: {{error}}", - "failedToResolveSymlink": "Symlinkin poistaminen epäonnistui", - "itemsDeletedSuccessfully": "{{count}} kohdetta poistettu onnistuneesti", - "failedToDeleteItems": "Kohteiden poistaminen epäonnistui", - "sudoPasswordRequired": "Ylläpitäjän Salasana Vaaditaan", - "enterSudoPassword": "Syötä sudo salasana jatkaaksesi tätä toimintoa", - "sudoPassword": "Sudo salasana", - "sudoOperationFailed": "Sudo toiminto epäonnistui", - "sudoAuthFailed": "Sudo todennus epäonnistui", - "dragFilesToUpload": "Pudota tiedostot tähän ladataksesi", - "emptyFolder": "Tämä kansio on tyhjä", - "searchFiles": "Etsi tiedostoja...", - "upload": "Lähetä", - "selectHostToStart": "Valitse isäntä aloittaaksesi tiedostonhallinnan", - "sshRequiredForFileManager": "Tiedostonhallinta vaatii SSH:n. Tässä palvelimessa ei ole SSH käytössä.", - "failedToConnect": "Yhteyden muodostaminen SSH:ään epäonnistui", - "failedToLoadDirectory": "Hakemiston lataaminen epäonnistui", - "noSSHConnection": "SSH yhteyttä ei saatavilla", - "copy": "Kopioi", - "cut": "Leikkaa", - "paste": "Liitä", - "copyPath": "Kopioi Polku", - "copyPaths": "Kopioi Polut", - "delete": "Poista", - "properties": "Ominaisuudet", - "refresh": "Päivitä", - "downloadFiles": "Lataa {{count}} tiedostoa selaimelle", - "copyFiles": "Kopioi {{count}} kohdetta", - "cutFiles": "Leikkaa {{count}} kohdetta", - "deleteFiles": "Poista {{count}} kohdetta", - "filesCopiedToClipboard": "{{count}} kohdetta kopioitu leikepöydälle", - "filesCutToClipboard": "{{count}} kohdetta leikattu leikepöydälle", - "pathCopiedToClipboard": "Polku kopioitu leikepöydälle", - "pathsCopiedToClipboard": "{{count}} polkua kopioitu leikepöydälle", - "failedToCopyPath": "Polun kopiointi leikepöydälle epäonnistui", - "movedItems": "Siirretty {{count}} kohdetta", - "failedToDeleteItem": "Kohteen poistaminen epäonnistui", - "itemRenamedSuccessfully": "{{type}} uudelleennimetty onnistuneesti", - "failedToRenameItem": "Kohteen nimeäminen uudelleen epäonnistui", - "download": "Lataa", - "permissions": "Käyttöoikeudet", - "size": "Koko", - "modified": "Muokattu", - "path": "Polku", - "confirmDelete": "Oletko varma, että haluat poistaa {{name}}?", - "permissionDenied": "Lupa evätty", - "serverError": "Palvelin Virhe", - "fileSavedSuccessfully": "Tiedosto tallennettu onnistuneesti", - "failedToSaveFile": "Tiedoston tallennus epäonnistui", - "confirmDeleteSingleItem": "Oletko varma, että haluat poistaa pysyvästi \"{{name}}\"?", - "confirmDeleteMultipleItems": "Oletko varma, että haluat pysyvästi poistaa {{count}} kohteita?", - "confirmDeleteMultipleItemsWithFolders": "Oletko varma, että haluat poistaa pysyvästi {{count}} kohteita? Tämä sisältää kansiot ja niiden sisällön.", - "confirmDeleteFolder": "Oletko varma, että haluat pysyvästi poistaa kansion \"{{name}}\" ja kaiken sen sisällön?", - "permanentDeleteWarning": "Tätä toimintoa ei voi peruuttaa. Nimike (kohteet) poistetaan pysyvästi palvelimelta.", - "recent": "Viimeisimmät", - "pinned": "Kiinnitetty", - "folderShortcuts": "Kansion Pikanäppäimet", - "failedToReconnectSSH": "SSH istunnon yhdistäminen uudelleen epäonnistui", - "openTerminalHere": "Avaa Pääte Tähän", - "run": "Suorita", - "openTerminalInFolder": "Avaa pääte tässä kansiossa", - "openTerminalInFileLocation": "Avaa pääte tiedoston sijainnissa", - "runningFile": "Käynnissä - {{file}}", - "onlyRunExecutableFiles": "Voi suorittaa vain suoritettavia tiedostoja", - "directories": "Hakemistot", - "removedFromRecentFiles": "Poistettu \"{{name}}\" viimeisimmistä tiedostoista", - "removeFailed": "Poistaminen epäonnistui", - "unpinnedSuccessfully": "Unpeed \"{{name}}\" onnistui", - "unpinFailed": "Kiinnityksen poisto epäonnistui", - "removedShortcut": "Poistettu pikakuvake \"{{name}}\"", - "removeShortcutFailed": "Pikakuvakkeen poisto epäonnistui", - "clearedAllRecentFiles": "Tyhjennetty kaikki viimeaikaiset tiedostot", - "clearFailed": "Tyhjennys epäonnistui", - "removeFromRecentFiles": "Poista viimeaikaisista tiedostoista", - "clearAllRecentFiles": "Tyhjennä kaikki viimeaikaiset tiedostot", - "unpinFile": "Irroita tiedosto", - "removeShortcut": "Poista pikakuvake", - "pinFile": "Kiinnitä tiedosto", - "addToShortcuts": "Lisää pikakuvakkeisiin", - "pasteFailed": "Liitä epäonnistui", - "noUndoableActions": "Ei kelpaamattomia toimintoja", - "undoCopySuccess": "Päivitetty kopiointitoiminto: Poistettu {{count}} kopioitu tiedosto", - "undoCopyFailedDelete": "Peruminen epäonnistui: Ei voitu poistaa kopioitavia tiedostoja", - "undoCopyFailedNoInfo": "Peruminen epäonnistui: Kopioituja tiedostotietoja ei löytynyt", - "undoMoveSuccess": "Peruttu toiminto: Siirretty {{count}} tiedostoa takaisin alkuperäiseen sijaintiin", - "undoMoveFailedMove": "Peruminen epäonnistui: Ei voitu siirtää yhtään tiedostoa takaisin", - "undoMoveFailedNoInfo": "Peruminen epäonnistui: Siirretyn tiedoston tietoja ei löytynyt", - "undoDeleteNotSupported": "Poistotoimintoa ei voi peruuttaa: Tiedostot on poistettu pysyvästi palvelimelta", - "undoTypeNotSupported": "Kumoa toimintotyyppi ei tueta", - "undoOperationFailed": "Toiminnon peruminen epäonnistui", - "unknownError": "Tuntematon virhe", - "confirm": "Vahvista", - "find": "Etsi...", - "replace": "Korvaa", - "downloadInstead": "Lataa Sen sijaan", - "keyboardShortcuts": "Näppäimistön Pikanäppäimet", - "searchAndReplace": "Etsi Ja Korvaa", - "editing": "Muokataan", - "search": "Etsi", - "findNext": "Etsi Seuraava", - "findPrevious": "Etsi Edellinen", - "save": "Tallenna", - "selectAll": "Valitse Kaikki", - "undo": "Kumoa", - "redo": "Toista", - "moveLineUp": "Siirrä Rivi Ylös", - "moveLineDown": "Siirrä Rivi Alas", - "toggleComment": "Vaihda Kommentin Tilaa", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Kuvan lataaminen epäonnistui", - "startTyping": "Aloita kirjoittaminen...", - "unknownSize": "Tuntematon koko", - "fileIsEmpty": "Tiedosto on tyhjä", - "largeFileWarning": "Suuri Tiedostojen Varoitus", - "largeFileWarningDesc": "Tämä tiedosto on kooltaan {{size}} ja se voi aiheuttaa suoritusongelmia kun se avataan tekstinä.", - "fileNotFoundAndRemoved": "Tiedostoa \"{{name}}\" ei löydy ja se on poistettu viimeisimmistä / pinned tiedostoista", - "failedToLoadFile": "Tiedoston lataaminen epäonnistui: {{error}}", - "serverErrorOccurred": "Palvelinvirhe tapahtui. Yritä myöhemmin uudelleen.", - "autoSaveFailed": "Automaattitallennus epäonnistui", - "fileAutoSaved": "Tiedosto tallennettu automaattisesti", - "moveFileFailed": "{{name}} siirtäminen epäonnistui", - "moveOperationFailed": "Toiminnon siirtäminen epäonnistui", - "canOnlyCompareFiles": "Voi vain verrata kahta tiedostoa", - "comparingFiles": "Tiedostojen vertailu: {{file1}} ja {{file2}}", - "dragFailed": "Vedä toiminto epäonnistui", - "filePinnedSuccessfully": "Tiedosto \"{{name}}\" kiinnitetty onnistuneesti", - "pinFileFailed": "Tiedoston kiinnitys epäonnistui", - "fileUnpinnedSuccessfully": "Tiedosto \"{{name}}\" kiinnitetty onnistuneesti", - "unpinFileFailed": "Tiedoston kiinnitys epäonnistui", - "shortcutAddedSuccessfully": "Pikakuvake kansiossa \"{{name}}\" lisätty onnistuneesti", - "addShortcutFailed": "Pikakuvakkeen lisääminen epäonnistui", - "operationCompletedSuccessfully": "{{operation}} {{count}} kohteita onnistuneesti", - "operationCompleted": "{{operation}} {{count}} kohdetta", - "downloadFileSuccess": "Tiedosto {{name}} ladattu onnistuneesti", - "downloadFileFailed": "Lataus epäonnistui", - "moveTo": "Siirrä {{name}}", - "diffCompareWith": "Vertaa {{name}} versioon", - "dragOutsideToDownload": "Lataa vedä ikkunan ulkopuolelle ({{count}} tiedostoa)", - "newFolderDefault": "Uusi Kansio", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} kohdetta siirretty onnistuneesti kohteeseen {{target}}", - "move": "Siirrä", - "searchInFile": "Etsi tiedostosta (Ctrl+F)", - "showKeyboardShortcuts": "Näytä pikakuvakkeet", - "startWritingMarkdown": "Aloita markdown sisällön kirjoittaminen...", - "loadingFileComparison": "Ladataan tiedoston vertailua...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Vertaa", - "sideBySide": "Sivu sivulta", + "compare": "Compare", + "sideBySide": "Side by Side", "inline": "Inline", - "fileComparison": "Tiedostovertailu: {{file1}} vs. {{file2}}", - "fileTooLarge": "Tiedosto liian suuri: {{error}}", - "sshConnectionFailed": "SSH-yhteys epäonnistui. Tarkista yhteytesi kohteeseen {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Tiedoston lataaminen epäonnistui: {{error}}", - "connecting": "Yhdistetään...", - "connectedSuccessfully": "Yhdistetty onnistuneesti", - "totpVerificationFailed": "TOTP vahvistus epäonnistui", - "warpgateVerificationFailed": "Varmennuksen varmennus epäonnistui", - "authenticationFailed": "Todennus epäonnistui", - "verificationCodePrompt": "Vahvistuskoodi:", - "changePermissions": "Muuta Käyttöoikeuksia", - "currentPermissions": "Nykyiset Käyttöoikeudet", - "owner": "Omistaja", - "group": "Ryhmä", - "others": "Muut", - "read": "Lue", - "write": "Kirjoita", - "execute": "Suorita", - "permissionsChangedSuccessfully": "Oikeuksien vaihto onnistui", - "failedToChangePermissions": "Käyttöoikeuksien muuttaminen epäonnistui", - "name": "Nimi", - "sortByName": "Nimi", - "sortByDate": "Muokkaus Päivämäärä", - "sortBySize": "Koko", - "ascending": "Nouseva", - "descending": "Laskeva", - "root": "Juuri", - "new": "Uusi", - "sortBy": "Järjestä Mukaan", - "items": "Kohteet", - "selected": "Valittu", - "editor": "Muokkain", - "octal": "Oktaali", - "storage": "Tallennustila", - "disk": "Levy", - "used": "Käytetty", - "of": "jostakin", - "toggleSidebar": "Vaihda Sivupalkkia", - "cannotLoadPdf": "Ei voi ladata PDF:ää", - "pdfLoadError": "Tämän PDF-tiedoston lataamisessa tapahtui virhe.", - "loadingPdf": "Ladataan PDF...", - "loadingPage": "Ladataan sivua..." + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopioi isäntäkoneeseen…", - "moveToHost": "Siirrä isäntäkoneeseen…", - "copyItemsToHost": "Kopioi {{count}} kohdetta isäntään…", - "moveItemsToHost": "Siirrä {{count}} kohdetta isäntään…", - "noHostsConnected": "Muita tiedostonhallintaohjelmia ei ole saatavilla.", - "noHostsConnectedHint": "Lisää toinen SSH-isäntä, jonka tiedostonhallinta on käytössä isäntähallinnassa.", - "selectDestinationHost": "Valitse kohdeisäntä", - "destinationPath": "Kohdepolku", - "recentDestinations": "Viimeaikaiset kohteet", - "collapseRecentDestinations": "Pienennä viimeisimmät kohteet", - "expandRecentDestinations": "Laajenna viimeisimmät kohteet", - "browseFolders": "Selaa kohdekansioita", - "browseDestination": "Selaa tai anna polku", - "confirmCopy": "Kopioida", - "confirmMove": "Liikkua", - "transferring": "Siirretään…", - "compressing": "Pakkaamalla…", - "extracting": "Poimitaan…", - "transferringItems": "Siirretään {{current}} / {{total}} kohdetta…", - "transferSuccess": "Siirto valmis", - "transferError": "Siirto epäonnistui", - "transferPartial": "Siirto suoritettu, mutta virheitä on {{count}}", - "transferPartialHint": "Siirto epäonnistui: {{paths}}", - "itemsSummary": "{{count}} kohdetta", - "destMustBeDirectory": "Määränpään on oltava hakemisto useiden kohteiden siirtoja varten", - "selectThisFolder": "Valitse tämä kansio", - "browsePathWillBeCreated": "Tätä kansiota ei ole vielä olemassa. Se luodaan, kun siirto alkaa.", - "browsePathError": "Tätä polkua ei voitu avata kohdepalvelimella.", - "goUp": "Mene ylös", - "copyFolderToHost": "Kopioi kansio isäntään…", - "moveFolderToHost": "Siirrä kansio isäntään…", - "hostReady": "Valmis", - "hostConnecting": "Yhdistetään…", - "hostDisconnected": "Ei yhdistetty", - "hostAuthRequired": "Todennus vaaditaan — avaa ensin tiedostonhallinta tällä isännöintikoneella", - "hostConnectionFailed": "Yhteys epäonnistui", - "metricsTitle": "Siirtoajat", - "metricsPrepare": "Valmistele kohde: {{duration}}", - "metricsCompress": "Pakkaa lähdekoodissa: {{duration}}", - "metricsHopSourceRead": "Lähde → palvelin: {{throughput}}", - "metricsHopDestSftpWrite": "Palvelin → kohde (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Palvelin → kohde (paikallinen): {{throughput}}", - "metricsTransfer": "Päästä päähän: {{throughput}} ({{duration}})", - "metricsExtract": "Ote määränpäässä: {{duration}}", - "metricsSourceDelete": "Poista lähteestä: {{duration}}", - "metricsTotal": "Yhteensä: {{duration}}", - "progressCompressing": "Pakkaus lähdepalvelimella…", - "progressExtracting": "Puretaan kohteessa…", - "progressTransferring": "Tiedon siirtäminen…", - "progressReconnecting": "Yhdistetään uudelleen…", - "parallelSegmentsLabel": "Rinnakkaiset siirtokaistat", - "parallelSegmentsOption": "{{count}} kaistaa", - "parallelSegmentsHint": "Suuret tiedostot jaetaan 256 Mt:n osiin. Useat kaistat käyttävät erillisiä yhteyksiä (kuten useiden siirtojen aloittaminen) suuremman kokonaisläpivirtauksen saavuttamiseksi.", - "progressTotalSpeed": "{{speed}} yhteensä ({{lanes}} kaistaa)", - "progressTransferringItems": "Tiedostojen siirtäminen ({{current}} / {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} tiedostoa", - "sourceNotDeletedPartial": "Lähdetiedostot säilytetään (osittainen siirto)", - "jumpHostLimitation": "Molempien isäntien on oltava tavoitettavissa Termix-palvelimelta. Suoraa isäntien välistä reititystä ei tueta.", - "cancel": "Peruuttaa", - "methodLabel": "Siirtotapa", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Tar-arkisto", - "methodItemSftp": "Tiedostokohtainen SFTP", - "methodAutoHint": "Valitsee tar- tai tiedostokohtaisen SFTP:n tiedostomäärän, koon ja pakkautuvuuden perusteella. Yksittäiset tiedostot käyttävät aina suoratoistettavaa SFTP:tä.", - "methodTarHint": "Pakkaa lähdekoodissa, siirrä yksi arkisto, pura kohdetiedostossa. Vaatii tar-tiedoston molemmissa Unix-koneissa.", - "methodItemSftpHint": "Siirrä jokainen tiedosto erikseen SFTP:n kautta. Toimii kaikilla isännöintijärjestelmillä, mukaan lukien Windows.", - "methodPreviewLoading": "Siirtomenetelmän laskeminen…", - "methodPreviewError": "Siirtomenetelmän esikatselu ei onnistunut. Palvelin valitsee silti menetelmän käynnistyksen yhteydessä.", - "methodPreviewWillUseTar": "Käytetään: Tar-arkistoa", - "methodPreviewWillUseItemSftp": "Käytetään: Tiedostokohtaista SFTP:tä", - "methodPreviewScanSummary": "{{fileCount}} tiedostoa, {{totalSize}} yhteensä (skannattu lähdepalvelimella).", - "methodItemSftpLimitation": "Jokainen tiedosto käyttää samaa SFTP-tietovirtaa kuin yksittäiset tiedostokopiot, yksi kerrallaan. Edistyminen yhdistetään kaikkien tiedostojen välillä, joten palkki liikkuu hitaasti suurten tiedostojen käsittelyssä.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Valitsit tiedostokohtaisen SFTP:n.", - "user_tar": "Valitsit tar-arkiston.", - "tar_unavailable": "Tar ei ole käytettävissä yhdellä tai molemmilla isännillä — sen sijaan käytetään tiedostokohtaista SFTP:tä.", - "windows_host": "Windows-isäntä on mukana – tar-tiedostoa ei käytetä.", - "auto_multi_large": "Automaattinen: useita tiedostoja, mukaan lukien suuri tiedosto ({{largestSize}}), jossa on pakattavaa dataa — tar niputtaa yhdeksi siirroksi.", - "auto_single_large_in_archive": "Automaattinen: yksi suuri tiedosto ({{largestSize}}) tässä joukossa — tiedostokohtainen SFTP.", - "auto_many_incompressible": "Automaattinen: enimmäkseen pakkaamatonta dataa — tiedostokohtainen SFTP.", - "auto_many_files": "Automaattinen: monta tiedostoa ({{fileCount}}) — tar vähentää tiedostokohtaista laskentatehoa.", - "auto_default": "Automaattinen: tiedostokohtainen SFTP tälle joukolle." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Peruuttaa", - "progressCancelling": "Peruutetaan…", - "progressStalled": "Pysähtynyt", - "resumedHint": "Yhdistetty uudelleen toisessa ikkunassa aloitettuun aktiiviseen siirtoon.", - "transferCancelled": "Siirto peruttu", - "transferCancelledCopyHint": "Osittaisia tiedostoja voi jäädä kohteeseen.", - "transferCancelledMoveHint": "Kohdekansioon saattaa jäädä osittaisia tiedostoja. Lähdetiedostoja ei poistettu.", - "cleanupDestFiles": "Siivouskohde", - "cleanupDestFilesSuccess": "Osittainen tiedosto poistettu kohteesta", - "cleanupDestFilesPartial": "Joitakin osittaisia tiedostoja ei voitu poistaa", - "cleanupDestFilesNothing": "Ei mitään siivottavaa määränpäässä", - "cleanupDestFilesError": "Siivous epäonnistui", - "retryTransfer": "Yritä uudelleen", - "retryTransferError": "Uudelleenyritys epäonnistui", - "transferFailedRetryHint": "Kohteessa säilytettiin vain osa tiedoista. Uudelleenyritys jatkuu, kun yhteys on palannut." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Ei SSH Tunneleita", - "createFirstTunnelMessage": "Et ole vielä luonut yhtään SSH tunnelia. Määritä tunneliyhteyksiä isäntäpäällikössä aloittaaksesi tämän.", - "connected": "Yhdistetty", - "disconnected": "Yhteys Katkaistu", - "connecting": "Yhdistetään...", - "error": "Virhe", - "canceling": "Peruutetaan...", - "connect": "Yhdistä", - "disconnect": "Katkaise", - "cancel": "Peruuta", - "port": "Portti", - "localPort": "Paikallinen Portti", - "remotePort": "Etäportti", - "currentHostPort": "Nykyinen Isäntäportti", - "endpointPort": "Päätepisteen Portti", - "bindIp": "Paikallinen IP-osoite", - "endpointSshConfig": "Päätepisteen Ssh Asetukset", - "endpointSshHost": "Päätepiste SSH Isäntä", - "endpointSshHostPlaceholder": "Valitse määritetty isäntä", - "endpointSshHostRequired": "Valitse päätepiste SSH isäntä jokaiselle asiakkaan tunneli.", - "attempt": "Yritys {{current}} / {{max}}", - "nextRetryIn": "Seuraava retry {{seconds}} sekunnissa", - "clientTunnels": "Asiakkaan Tunnelit", - "clientTunnel": "Asiakkaan Tunneli", - "addClientTunnel": "Lisää Asiakastunneli", - "noClientTunnels": "Tällä työpöydällä ei ole määritettyjä asiakastunneleita.", - "tunnelName": "Tunnelin Nimi", - "remoteHost": "Etäpalvelin", - "autoStart": "Automaattinen Käynnistys", - "clientAutoStartDesc": "Aloittaa kun työpöytäohjelma avautuu ja pysyy kytkettynä.", - "clientManualStartDesc": "Käytä Käynnistä ja lopeta tämä rivi. Termix ei avaa sitä automaattisesti.", - "clientRemoteServerNote": "Etäsiirto saattaa vaatia AllowTcpForwarding ja GatewayPorts päätepisteessä SSH-palvelimella. Etäsiirros sulkeutuu kun työpöytä irrotetaan.", - "clientTunnelStarted": "Asiakkaan tunneli aloitettu", - "clientTunnelStopped": "Asiakkaan tunneli pysäytetty", - "tunnelTestSucceeded": "Tunnelin testi onnistui", - "tunnelTestFailed": "Tunnelitesti epäonnistui", - "localSaved": "Asiakastunnelit tallennettu", - "localSaveError": "Paikallisten asiakastunneleiden tallennus epäonnistui", - "invalidBindIp": "Paikallisen IP-osoitteen on oltava kelvollinen IPv4-osoite.", - "invalidLocalTargetIp": "Paikallisen kohteen IP-osoitteen on oltava kelvollinen IPv4-osoite.", - "invalidLocalPort": "Paikallinen satama on oltava välillä 1 ja 65535.", - "invalidRemotePort": "Etäportin on oltava välillä 1 ja 65535.", - "invalidLocalTargetPort": "Paikallisen tavoiteportin on oltava välillä 1–65535.", - "invalidEndpointPort": "Päätesataman on oltava välillä 1–65535.", - "duplicateAutoStartBind": "Vain yksi automaattinen käynnistys -asiakastunneli voi käyttää {{bind}} -tunnusta.", - "manualControlError": "Tunnelin tilan päivitys epäonnistui.", - "active": "Aktiivinen", - "start": "Aloita", - "stop": "Pysäytä", - "test": "Testi", - "type": "Tunnelin Tyyppi", - "typeLocal": "Paikallinen (-L)", - "typeRemote": "Etäkäyttö (-R)", - "typeDynamic": "Dynaaminen (-D)", - "typeServerLocalDesc": "Nykyinen isäntä päätepisteeseen.", - "typeServerRemoteDesc": "Päätä piste takaisin nykyiseen isäntään.", - "typeClientLocalDesc": "Paikallinen tietokone tutkittavaksi.", - "typeClientRemoteDesc": "Endpoint takaisin paikalliseen tietokoneeseen.", - "typeClientDynamicDesc": "SOCKS paikallisella tietokoneella.", - "typeDynamicDesc": "Siirrä SOCKS5 CONNECT liikennettä SSH:n kautta", - "forwardDescriptionServerLocal": "Nykyinen isäntä {{sourcePort}} → päätepiste {{endpointPort}}.", - "forwardDescriptionServerRemote": "Päätepiste {{endpointPort}} → nykyinen isäntä {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS nykyisellä koneella {{sourcePort}}.", - "forwardDescriptionClientLocal": "Paikallinen {{sourcePort}} → etäinen {{endpointPort}}.", - "forwardDescriptionClientRemote": "Etäinen {{sourcePort}} → paikallinen {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS paikallisessa portissa {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SUKAT {{endpoint}} kautta", - "autoNameClientLocal": "Paikallinen {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → paikallinen {{localPort}}", - "autoNameClientDynamic": "SUKAT {{localPort}} kautta {{endpoint}}", - "route": "Reitti:", - "lastStarted": "Viimeksi aloitettu", - "lastTested": "Viimeksi testattu", - "lastError": "Viimeisin virhe", - "maxRetries": "Maksimi Uudelleen", - "maxRetriesDescription": "Enimmäismäärä uudelleen kokeilevia yrityksiä.", - "retryInterval": "Uudelleen Aikaväli (Sekuntia)", - "retryIntervalDescription": "Aika odottaa yritysten uudelleenkokeilun välillä.", - "local": "Paikallinen", - "remote": "Etä", - "destination": "Kohde", - "host": "Isäntä", - "mode": "Tila", - "noHostSelected": "Ei isäntää valittuna", - "working": "Työskennellään..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Suoritin", - "memory": "Muisti", - "disk": "Levy", - "network": "Verkko", - "uptime": "Käyttöaika", - "processes": "Prosessit", - "available": "Saatavilla", - "free": "Ilmainen", - "connecting": "Yhdistetään...", - "connectionFailed": "Yhteyden muodostaminen palvelimeen epäonnistui", - "naCpus": "N/A CPU", - "cpuCores_one": "{{count}} Ydin", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Suorittimen Käyttö", - "memoryUsage": "Muistin Käyttö", - "diskUsage": "Levyn Käyttö", - "failedToFetchHostConfig": "Palvelimen asetusten noutaminen epäonnistui", - "serverOffline": "Palvelin Offline-tilassa", - "cannotFetchMetrics": "Mittareita ei voi hakea offline-palvelimelta", - "totpFailed": "TOTP vahvistus epäonnistui", - "noneAuthNotSupported": "Palvelimen tilastot eivät tue 'ei mitään' todennusta.", - "load": "Lataa", - "systemInfo": "Järjestelmän Tiedot", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Käyttöjärjestelmä", - "kernel": "Ydin", - "seconds": "sekuntia", - "networkInterfaces": "Verkon Liitännät", - "noInterfacesFound": "Verkkoliittymiä ei löytynyt", - "noProcessesFound": "Prosesseja ei löytynyt", - "loginStats": "Ssh Kirjautumistilastot", - "noRecentLoginData": "Ei viimeaikaisia kirjautumistietoja", - "executingQuickAction": "Suoritetaan {{name}}...", - "quickActionSuccess": "{{name}} suoritettu onnistuneesti", - "quickActionFailed": "{{name}} epäonnistui", - "quickActionError": "{{name}} suoritus epäonnistui", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Kuunnellaan Satamia", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Portti", - "address": "Osoite", - "process": "Prosessi", - "noData": "Ei kuunnella porttien tietoja" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Palomuuri", - "inactive": "Passiivinen", - "policy": "Politiikka", - "rules": "säännöt", - "noData": "Palomuurin tietoja ei ole saatavilla", - "action": "Toiminto", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Portti", - "source": "Lähde", - "anywhere": "Missä" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Lataa Keskim.", - "swap": "Vaihda", - "architecture": "Arkkitehtuuri", - "refresh": "Päivitä", - "retry": "Yritä Uudelleen" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Itse isännöity SSH ja etätyöpöytähallinta", - "loginTitle": "Kirjaudu Termixiin", - "registerTitle": "Luo Tili", - "forgotPassword": "Unohditko Salasanan?", - "rememberMe": "Muista laite 30 päivän ajan (sisältää TOTP)", - "noAccount": "Eikö sinulla ole tiliä?", - "hasAccount": "Onko sinulla jo tili?", - "twoFactorAuth": "Kaksivaiheinen Todennus", - "enterCode": "Syötä vahvistuskoodi", - "backupCode": "Tai käytä varmuuskopion koodia", - "verifyCode": "Vahvista Koodi", - "redirectingToApp": "Uudelleenohjataan sovellukseen...", - "sshAuthenticationRequired": "Ssh Todennus Vaaditaan", - "sshNoKeyboardInteractive": "Näppäimistö-Interaktiivinen Todennus Ei Saatavilla", - "sshAuthenticationFailed": "Todennus Epäonnistui", - "sshAuthenticationTimeout": "Todennus Aikakatkaistiin", - "sshNoKeyboardInteractiveDescription": "Palvelin ei tue näppäimistön interaktiivista tunnistautumista. Anna salasanasi tai SSH avain.", - "sshAuthFailedDescription": "Annetut tunnukset olivat virheellisiä. Yritä uudelleen oikeilla tunnuksilla.", - "sshTimeoutDescription": "Todennusyritys aikakatkaistiin. Ole hyvä ja yritä uudelleen.", - "sshProvideCredentialsDescription": "Ole hyvä ja anna SSH käyttäjätunnuksesi yhdistääksesi tähän palvelimeen.", - "sshPasswordDescription": "Syötä tämän SSH yhteyden salasana.", - "sshKeyPasswordDescription": "Jos SSH avaimesi on salattu, kirjoita tunnuslause tähän.", - "passphraseRequired": "Tunnuslause Vaaditaan", - "passphraseRequiredDescription": "SSH avain on salattu. Ole hyvä ja kirjoita salasana avataksesi sen.", - "back": "Takaisin", - "firstUser": "Ensimmäinen Käyttäjä", - "firstUserMessage": "Olet ensimmäinen käyttäjä ja siitä tehdään ylläpitäjä. Voit tarkastella järjestelmänvalvojan asetuksia sivupalkin käyttäjän pudotusvalikossa. Jos luulet tämän olevan virhe, tarkista telakan lokit, tai luo GitHub-ongelma.", - "external": "Ulkoinen", - "loginWithExternal": "Kirjaudu ulkoiseen palveluntarjoajaan", - "loginWithExternalDesc": "Kirjaudu käyttäen määritettyä ulkoista identiteettitarjoajaa", - "externalNotSupportedInElectron": "Ulkoista todennusta ei ole vielä tuettu Electron sovelluksessa. Ole hyvä ja käytä OIDC kirjautumiseen tarkoitettua web-versiota.", - "resetPasswordButton": "Nollaa Salasana", - "sendResetCode": "Lähetä Nollauskoodi", - "resetCodeDesc": "Syötä käyttäjänimesi, jotta saat salasanan nollauskoodin. Koodi on kirjautunut telakkakontin lokeihin.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Vahvista Koodi", - "enterResetCode": "Syötä 6-numeroinen koodi telakan kontin lokeista käyttäjälle:", - "newPassword": "Uusi Salasana", - "confirmNewPassword": "Vahvista Salasana", - "enterNewPassword": "Anna käyttäjälle uusi salasana:", - "signUp": "Rekisteröidy", - "desktopApp": "Työpöytäsovellus", - "loggingInToDesktopApp": "Kirjaudutaan sisään työpöytäsovellukseen", - "loadingServer": "Ladataan palvelinta...", - "dataLossWarning": "Salasanan palauttaminen tällä tavalla poistaa kaikki tallennetut SSH palvelimet, tunnukset ja muut salatut tiedot. Tätä toimintoa ei voi peruuttaa. Käytä tätä vain, jos olet unohtanut salasanasi ja et ole kirjautunut sisään.", - "authenticationDisabled": "Tunnistautuminen Pois Käytöstä", - "authenticationDisabledDesc": "Kaikki todennusmenetelmät on tällä hetkellä poistettu käytöstä. Ota yhteyttä järjestelmänvalvojaan.", - "attemptsRemaining": "{{count}} yritystä jäljellä" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Vahvista Ssh Isäntäavain", - "keyChangedWarning": "Ssh Isäntäavain Vaihdettu", - "firstConnectionTitle": "Yhteyden muodostaminen tähän palvelimeen ensimmäistä kertaa", - "firstConnectionDescription": "Tämän isännän aitoutta ei voida vahvistaa. Varmista sormenjälki vastaa sitä, mitä odotat.", - "keyChangedDescription": "Palvelimen isäntäavain on muuttunut viimeisen yhteytesi jälkeen. Tämä saattaa merkitä tietoturvaongelmaa.", - "previousKey": "Edellinen Avain", - "newFingerprint": "Uusi Sormenjälki", - "fingerprint": "Sormenjälki", - "verifyInstructions": "Jos luotat tähän isäntään, jatka ja tallenna sormenjälki tulevia yhteyksiä varten.", - "securityWarning": "Turvallisuusvaroitus", - "acceptAndContinue": "Hyväksy & Jatka", - "acceptNewKey": "Hyväksy Uusi Avain Ja Jatka" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Tietokantaan ei saatu yhteyttä", - "unknownError": "Tuntematon virhe", - "loginFailed": "Kirjautuminen epäonnistui", - "failedPasswordReset": "Salasanan nollaamisen aloittaminen epäonnistui", - "failedVerifyCode": "Ei voitu todentaa nollakoodia", - "failedCompleteReset": "Salasanan nollaus epäonnistui", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "OIDC kirjautumisen käynnistäminen epäonnistui", - "silentSigninOidcUnavailable": "Äänetön kirjautuminen on pyydetty, mutta OIDC kirjautuminen ei ole käytettävissä.", - "failedUserInfo": "Käyttäjätietojen saaminen epäonnistui kirjautumisen jälkeen", - "oidcAuthFailed": "OIDC todennus epäonnistui", - "invalidAuthUrl": "Virheellinen varmenteen URL-osoite vastaanotettu taustaosasta", - "requiredField": "Tämä kenttä on pakollinen", - "minLength": "Minimi pituus on {{min}}", - "passwordMismatch": "Salasanat eivät täsmää", - "passwordLoginDisabled": "Käyttäjätunnus/salasanan kirjautuminen on poistettu käytöstä", - "sessionExpired": "Istunto vanhentunut - Kirjaudu sisään uudelleen", - "totpRateLimited": "Hintarajoitus: Liian monta TOTP vahvistusyritystä. Yritä myöhemmin uudelleen.", - "totpRateLimitedWithTime": "Nopeus rajoitettu: Liian monta TOTP-vahvistusyritystä. Odota {{time}} sekuntia ennen kuin yrität uudelleen.", - "resetCodeRateLimited": "Hintarajoitus: Liian monta vahvistusyritystä. Yritä myöhemmin uudelleen.", - "resetCodeRateLimitedWithTime": "Hintarajoitus: Liian monta vahvistusyritystä. Odota {{time}} sekuntia ennen kuin yrität uudelleen.", - "authTokenSaveFailed": "Todennustunnuksen tallennus epäonnistui", - "failedToLoadServer": "Palvelimen lataaminen epäonnistui" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Uuden tilin rekisteröinti on tällä hetkellä poistettu käytöstä. Kirjaudu sisään tai ota yhteyttä järjestelmänvalvojaan.", - "userNotAllowed": "Tililläsi ei ole oikeutta rekisteröityä. Ota yhteyttä järjestelmänvalvojaan.", - "databaseConnectionFailed": "Yhteyden muodostaminen tietokantapalvelimeen epäonnistui", - "resetCodeSent": "Nollaa Dockerin lokeihin lähetetty koodi", - "codeVerified": "Koodi vahvistettu onnistuneesti", - "passwordResetSuccess": "Salasanan vaihtaminen onnistui", - "loginSuccess": "Kirjautuminen onnistui", - "registrationSuccess": "Rekisteröinti onnistui" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Paikalliset työpöydän tunnelit kohdennettu määritetty SSH isännät.", - "c2sTunnelPresets": "Asiakkaan Tunnelin Esiasetukset", - "c2sTunnelPresetsDesc": "Tallenna tämä työpöytäasiakkaan paikallinen tunneliluettelo nimenä palvelimen esiasetuksena tai lataa esiasetus takaisin tähän ohjelmaan.", - "c2sTunnelPresetsUnavailable": "Asiakkaan tunnelin esiasetukset ovat käytettävissä vain työpöytäsovelluksessa.", - "c2sPresetName": "Esiasetuksen Nimi", - "c2sPresetNamePlaceholder": "Asiakkaan esiasetus nimi", - "c2sPresetToLoad": "Esiasetus Ladattavaksi", - "c2sNoPresetSelected": "Ei esiasetusta valittuna", - "c2sNoPresets": "Ei tallennettuja esiasetuksia", - "c2sLoadPreset": "Lataa", - "c2sCurrentLocalConfig": "{{count}} paikallista asiakastunnelia määritetty tällä työpöydällä.", - "c2sPresetSyncNote": "Esiasetukset ovat nimenomaisia kuvakaappauksia; lataus korvaa tämän työpöytäasiakkaan paikallisen tunneliluettelon.", - "c2sPresetSaved": "Asiakkaan tunnelin esiasetus tallennettu", - "c2sPresetLoaded": "Asiakastunnelin esiasetus ladattu paikallisesti", - "c2sPresetRenamed": "Asiakkaan tunnelin esiasetus uudelleen nimetty", - "c2sPresetDeleted": "Asiakkaan tunnelin esiasetus poistettu", - "c2sPresetLoadError": "Asiakastunnelin esiasetusten lataaminen epäonnistui" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Kieli", - "keyPassword": "avaimen salasana", - "pastePrivateKey": "Liitä yksityinen avain tähän...", - "localListenerHost": "127.0.0.1 (kuuntele paikallisesti)", - "localTargetHost": "127.0.0.1 (tämän tietokoneen tavoite)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Syötä salasanasi", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Hallintapaneeli", - "loading": "Ladataan kojelautaa...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Tuki", + "support": "Support", "discord": "Discord", - "serverOverview": "Palvelimen Yleiskatsaus", - "version": "Versio", - "upToDate": "Enintään päivämäärä", - "updateAvailable": "Päivitys Saatavilla", - "beta": "Beeta", - "uptime": "Käyttöaika", - "database": "Tietokanta", - "healthy": "Terveet", - "error": "Virhe", - "totalHosts": "Isännät Yhteensä", - "totalTunnels": "Tunnelit Yhteensä", - "totalCredentials": "Käyttäjätunnukset Yhteensä", - "recentActivity": "Viimeaikainen Toiminta", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Ladataan viimeaikaista toimintaa...", - "noRecentActivity": "Ei viimeaikaista toimintaa", - "quickActions": "Nopeat Toiminnot", - "addHost": "Lisää Isäntä", - "addCredential": "Lisää Käyttöoikeustieto", - "adminSettings": "Ylläpitäjän Asetukset", - "userProfile": "Käyttäjän Profiili", - "serverStats": "Palvelimen Tilastot", - "loadingServerStats": "Ladataan palvelimen tilastoja...", - "noServerData": "Palvelimen tietoja ei ole saatavilla", - "cpu": "Suoritin", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Muokkaa Kojelautaa", - "dashboardSettings": "Hallintapaneelin Asetukset", - "enableDisableCards": "Ota Kortit Käyttöön/Poista Käytöstä", - "resetLayout": "Palauta oletukset", - "serverOverviewCard": "Palvelimen Yleiskatsaus", - "recentActivityCard": "Viimeaikainen Toiminta", - "networkGraphCard": "Verkkokaavio", - "networkGraph": "Verkkokaavio", - "quickActionsCard": "Nopeat Toiminnot", - "serverStatsCard": "Palvelimen Tilastot", - "panelMain": "Ensisijainen", - "panelSide": "Sivu", - "justNow": "juuri nyt" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "VAKUUTUS", - "hostsOnline": "Isäntä Paikalla", - "activeTunnels": "Aktiiviset Tunnelit", - "registerNewServer": "Rekisteröi uusi palvelin", - "storeSshKeysOrPasswords": "Tallenna SSH näppäimet tai salasanat", - "manageUsersAndRoles": "Hallinnoi käyttäjiä ja rooleja", - "manageYourAccount": "Hallinnoi tiliäsi", - "hostStatus": "Isäntäpisteen Tila", - "noHostsConfigured": "Ei isäntiä määritetty", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", - "offline": "OFFLINE-TILA", - "onlineLower": "Paikalla", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Lisää:", - "commandPalette": "Komentopaletti", - "done": "Valmis", - "editModeInstructions": "Vedä kortteja järjestääksesi uudelleen · Vedä sarakkeen jakajaa muuttaaksesi sarakkeita · Vedä kortin alareunaa muuttaaksesi sen korkeutta · Roskakorttia poistaaksesi", - "empty": "Tyhjä", - "clear": "Tyhjennä" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Lisää Isäntä", - "addGroup": "Lisää Ryhmä", - "addLink": "Lisää Linkki", - "zoomIn": "Zoomaa Sisään", - "zoomOut": "Zoomaa Ulos", - "resetView": "Palauta Näkymä", - "selectHost": "Valitse Isäntä", - "chooseHost": "Valitse isäntä...", - "parentGroup": "Ylätason Ryhmä", - "noGroup": "Ei Ryhmää", - "groupName": "Ryhmän Nimi", - "color": "Väri", - "source": "Lähde", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Siirrä ryhmään", - "selectGroup": "Valitse ryhmä...", - "addConnection": "Lisää Yhteys", - "hostDetails": "Palvelimen Tiedot", - "removeFromGroup": "Poista ryhmästä", - "addHostHere": "Lisää Isäntä Tähän", - "editGroup": "Muokkaa Ryhmää", - "delete": "Poista", - "add": "Lisää", - "create": "Luo", - "move": "Siirrä", - "connect": "Yhdistä", - "createGroup": "Luo Ryhmä", - "selectSourcePlaceholder": "Valitse Lähdekoodi...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Virheellinen Tiedosto", - "hostAlreadyExists": "Isäntä on jo rakenteessa", - "connectionExists": "Yhteys on jo olemassa", - "unknown": "Tuntematon", - "name": "Nimi", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Tila", - "failedToAddNode": "Solmun lisääminen epäonnistui", - "sourceDifferentFromTarget": "Lähteen ja kohteen on oltava erilainen", - "exportJSON": "Vie JSON", - "importJSON": "Tuo JSON", - "terminal": "Pääte", - "fileManager": "Tiedostojen Hallinta", - "tunnel": "Tunneli", - "docker": "Telakoitsija", - "serverStats": "Palvelimen Tilastot", - "noNodes": "Ei vielä solmuja" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Telakointi ei ole käytössä tälle isännälle", - "validating": "Tarkistetaan Dockeria...", - "connecting": "Yhdistetään...", - "error": "Virhe", - "version": "Telakoija {{version}}", - "connectionFailed": "Yhteyden muodostaminen Dockeriin epäonnistui", - "containerStarted": "Kontti {{name}} aloitettu", - "failedToStartContainer": "Säiliön {{name}} käynnistäminen epäonnistui", - "containerStopped": "Säiliö {{name}} pysäytetty", - "failedToStopContainer": "{{name}} säilytyksen pysäyttäminen epäonnistui", - "containerRestarted": "Kontti {{name}} uudelleenkäynnistetty", - "failedToRestartContainer": "Kontin uudelleenkäynnistys epäonnistui {{name}}", - "containerPaused": "Säiliö {{name}} keskeytetty", - "containerUnpaused": "Säiliö {{name}} keskeytetty", - "failedToTogglePauseContainer": "Paketin tilan vaihto epäonnistui kontin {{name}}", - "containerRemoved": "Säiliö {{name}} poistettu", - "failedToRemoveContainer": "{{name}} laatikon poisto epäonnistui", - "image": "Kuva", - "ports": "Satamat", - "noPorts": "Ei portteja", - "start": "Aloita", - "confirmRemoveContainer": "Oletko varma, että haluat poistaa säiliön '{{name}}'? Tätä toimintoa ei voi perua.", - "runningContainerWarning": "Varoitus: Tämä säiliö on käynnissä. Pakkauksen poistaminen pysäyttää ensin pakkauksen.", - "loadingContainers": "Ladataan säiliöitä...", - "manager": "Telakoitsijoiden Hallinta", - "autoRefresh": "Automaattinen Päivitys", - "timestamps": "Aikaleimat", - "lines": "Rivejä", - "filterLogs": "Suodata lokia...", - "refresh": "Päivitä", - "download": "Lataa", - "clear": "Tyhjennä", - "logsDownloaded": "Lokit ladattu onnistuneesti", - "last50": "Viimeiset 50", - "last100": "Viimeiset 100", - "last500": "Viimeiset 500", - "last1000": "Viimeiset 1000", - "allLogs": "Kaikki Lokit", - "noLogsMatching": "Ei vastaavia lokeja \"{{query}}\"", - "noLogsAvailable": "Lokeja ei saatavilla", - "noContainersFound": "Säiliöitä ei löytynyt", - "noContainersFoundHint": "Telakointikontteja ei ole saatavilla tässä palvelimessa", - "searchPlaceholder": "Etsi kontteja...", - "allStatuses": "Kaikki Tilat", - "stateRunning": "Käynnissä", - "statePaused": "Keskeytetty", - "stateExited": "Poistui", - "stateRestarting": "Käynnistetään Uudelleen", - "noContainersMatchFilters": "Suodattimet eivät täsmää", - "noContainersMatchFiltersHint": "Yritä muokata haku- tai suodatinkriteerejäsi", - "failedToFetchStats": "Konttitilastojen noutaminen epäonnistui", - "containerNotRunning": "Kontti ei ole käynnissä", - "startContainerToViewStats": "Käynnistä kontti nähdäksesi tilastot", - "loadingStats": "Ladataan tilastoja...", - "errorLoadingStats": "Virhe tilastoja ladattaessa", - "noStatsAvailable": "Ei tilastoja saatavilla", - "cpuUsage": "Suorittimen Käyttö", - "current": "Nykyinen", - "memoryUsage": "Muistin Käyttö", - "networkIo": "Verkko I/O", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Ulostulo", - "blockIo": "Lohko I/O", - "read": "Lue", - "write": "Kirjoita", - "pids": "PID:t", - "containerInformation": "Kontin Tiedot", - "name": "Nimi", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Tila", - "containerMustBeRunning": "Kontin on oltava käynnissä päästäksesi konsoliin", - "verificationCodePrompt": "Syötä vahvistuskoodi", - "totpVerificationFailed": "TOTP vahvistus epäonnistui. Yritä uudelleen.", - "warpgateVerificationFailed": "Varmennus epäonnistui. Yritä uudelleen.", - "connectedTo": "Yhdistetty {{containerName}}", - "disconnected": "Yhteys Katkaistu", - "consoleError": "Konsolivirhe", - "errorMessage": "Virhe: {{message}}", - "failedToConnect": "Yhteyden muodostaminen konttiin epäonnistui", - "console": "Konsoli", - "selectShell": "Valitse tulkki", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "tuhka", - "connect": "Yhdistä", - "disconnect": "Katkaise", - "notConnected": "Ei yhdistetty", - "clickToConnect": "Napsauta Yhdistä aloittaaksesi komentotulkin istunnon", - "connectingTo": "Yhdistetään {{containerName}} tiedostoon...", - "containerNotFound": "Säiliötä ei löytynyt", - "backToList": "Takaisin listaan", - "logs": "Lokit", - "stats": "Tilastot", - "consoleTab": "Konsoli", - "startContainerToAccess": "Käynnistä kontti päästäksesi konsoliin" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Yleiset", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Käyttäjät", - "sectionSessions": "Istunnot", - "sectionRoles": "Roolit", - "sectionDatabase": "Tietokanta", - "sectionApiKeys": "Api Avaimet", - "allowRegistration": "Salli Käyttäjien Rekisteröinti", - "allowRegistrationDesc": "Anna uusien käyttäjien itse rekisteröityä", - "allowPasswordLogin": "Salli Salasanan Kirjautuminen", - "allowPasswordLoginDesc": "Käyttäjätunnus/salasana kirjautuminen", - "oidcAutoProvision": "Oidc Auto-Provision", - "oidcAutoProvisionDesc": "Luo automaattisesti tilejä OIDC käyttäjille, vaikka rekisteröinti on poistettu käytöstä", - "allowPasswordReset": "Salli Salasanan Nollaus", - "allowPasswordResetDesc": "Nollaa koodi Dockerin lokeilla", - "sessionTimeout": "Istunnon Aikakatkaisu", - "hours": "tuntia", - "sessionTimeoutRange": "Minimi 1 h · Max 720 h", - "monitoringDefaults": "Oletusten Seuranta", - "statusCheck": "Tilan Tarkistus", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", "metrics": "Metrics", - "sec": "sek", - "logLevel": "Lokin Taso", - "enableGuacamole": "Ota Guacamoli Käyttöön", - "enableGuacamoleDesc": "RD- NC etätyöpöytä", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Määritä OpenID Connect SSO:lle. * merkityt kentät ovat pakollisia.", - "oidcClientId": "Asiakkaan Tunnus", - "oidcClientSecret": "Asiakkaan Salainen", - "oidcAuthUrl": "Valtuutuksen URL", - "oidcIssuerUrl": "Myöntäjän URL", - "oidcTokenUrl": "Tokenin URL", - "oidcUserIdentifier": "Käyttäjän Tunnisteen Polku", - "oidcDisplayName": "Näytä Nimen Polku", - "oidcScopes": "Soveltamisalueet", - "oidcUserinfoUrl": "Ohita Käyttäjäinfo Url", - "oidcAllowedUsers": "Sallitut Käyttäjät", - "oidcAllowedUsersDesc": "Yksi sähköposti riviä kohden. Jätä tyhjäksi, jotta kaikki sallitaan.", - "removeOidc": "Poista", - "usersCount": "{{count}} käyttäjää", - "createUser": "Luo", - "newRole": "Uusi Rooli", - "roleName": "Nimi", - "roleDisplayName": "Näytön Nimi", - "roleDescription": "Kuvaus", - "rolesCount": "{{count}} roolia", - "createRole": "Luo", - "creating": "Luodaan...", - "exportDatabase": "Vie Tietokanta", - "exportDatabaseDesc": "Lataa varmuuskopio kaikista palvelimista, käyttäjätunnuksista ja asetuksista", - "export": "Vie", - "exporting": "Viedään...", - "importDatabase": "Tuo Tietokanta", - "importDatabaseDesc": "Palauta .sqlite varmuuskopio tiedostosta", - "importDatabaseSelected": "Valittu: {{name}}", - "selectFile": "Valitse Tiedosto", - "changeFile": "Muuta", - "import": "Tuo", - "importing": "Tuoda...", - "apiKeysCount": "{{count}} avainta", - "newApiKey": "Uusi API-avain", - "apiKeyCreatedWarning": "Avain luotu - kopioi se nyt, sitä ei näytetä uudelleen.", - "apiKeyName": "Nimi", - "apiKeyUser": "Käyttäjä", - "apiKeySelectUser": "Valitse käyttäjä...", - "apiKeyExpiresAt": "Vanhenee", - "createKey": "Luo Avain", - "apiKeyNoExpiry": "Ei vanhentunut", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Kaksoisaukko", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Paikallinen", - "adminStatusAdministrator": "Ylläpitäjä", - "adminStatusRegularUser": "Tavallinen Käyttäjä", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "TULLI", - "youBadge": "SINÄ", - "sessionsActive": "{{count}} aktiivinen", - "sessionActive": "Aktiivinen: {{time}}", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Kaikki", - "revokeAllSessionsSuccess": "Kaikki istunnot käyttäjälle peruutettu", - "revokeAllSessionsFailed": "Istuntojen peruuttaminen epäonnistui", - "revokeSessionFailed": "Istunnon peruuttaminen epäonnistui", - "addRole": "Lisää rooli", - "noCustomRoles": "Mukautettuja rooleja ei määritelty", - "removeRoleFailed": "Roolin poistaminen epäonnistui", - "assignRoleFailed": "Roolin määrittäminen epäonnistui", - "deleteRoleFailed": "Roolin poistaminen epäonnistui", - "userAdminAccess": "Ylläpitäjä", - "userAdminAccessDesc": "Täysi pääsy kaikkiin järjestelmänvalvojan asetuksiin", - "userRoles": "Roolit", - "revokeAllUserSessions": "Peruuta Kaikki Istunnot", - "revokeAllUserSessionsDesc": "Pakota uudelleen kirjautumaan kaikkiin laitteisiin", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Tämän käyttäjän poistaminen on pysyvää.", - "deleteUser": "Poista {{username}}", - "deleting": "Poistetaan...", - "deleteUserFailed": "Käyttäjän poistaminen epäonnistui", - "deleteUserSuccess": "Käyttäjä \"{{username}}\" poistettu", - "deleteRoleSuccess": "Rooli \"{{name}}\" poistettu", - "revokeKeySuccess": "Key \"{{name}}\" peruttu", - "revokeKeyFailed": "Avaimen peruuttaminen epäonnistui", - "copiedToClipboard": "Kopioitu leikepöydälle", - "done": "Valmis", - "createUserTitle": "Luo Käyttäjä", - "createUserDesc": "Luo uusi paikallinen tili.", - "createUserUsername": "Käyttäjätunnus", - "createUserPassword": "Salasana", - "createUserPasswordHint": "Vähintään 6 merkkiä.", - "createUserEnterUsername": "Syötä käyttäjänimi", - "createUserEnterPassword": "Syötä salasana", - "createUserSubmit": "Luo Käyttäjä", - "editUserTitle": "Käyttäjän Hallinta: {{username}}", - "editUserDesc": "Muokkaa rooleja, ylläpitäjän tilaa, istuntoja ja tilin asetuksia.", - "editUserUsername": "Käyttäjätunnus", - "editUserAuthType": "Todennuksen Tyyppi", - "editUserAdminStatus": "Ylläpitäjän Tila", - "editUserUserId": "Käyttäjän Tunnus", - "linkAccountTitle": "Linkitä OIDC salasanatilille", - "linkAccountDesc": "Yhdistä OIDC tili {{username}} olemassa olevaan paikalliseen tiliin.", - "linkAccountWarningTitle": "Näin voidaan", - "linkAccountEffect1": "Poista vain OIDC tili", - "linkAccountEffect2": "Lisää OIDC kirjautuminen kohdetilille", - "linkAccountEffect3": "Salli sekä OIDC että salasana kirjautuminen", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Syötä paikallisen tilin käyttäjänimi linkittääksesi käyttäjätunnuksen", - "linkAccounts": "Linkitä Tilit", - "linkAccountSuccess": "OIDC-tili linkitetty kohteeseen \"{{username}}\"", - "linkAccountFailed": "OIDC-tilin linkittäminen epäonnistui", - "linkAccountInProgress": "Linkitetään...", - "saving": "Tallennetaan...", - "updateRegistrationFailed": "Rekisteröintiasetuksen päivittäminen epäonnistui", - "updatePasswordLoginFailed": "Salasanan sisäänkirjautumisen asetusta ei voitu päivittää", - "updateOidcAutoProvisionFailed": "OIDC automaattisen varauksen asetuksen päivittäminen epäonnistui", - "updatePasswordResetFailed": "Salasanan nollausasetuksen päivittäminen epäonnistui", - "sessionTimeoutRange2": "Istunnon aikakatkaisun on oltava 1 - 720 tuntia", - "sessionTimeoutSaved": "Istunnon aikakatkaisu tallennettu", - "sessionTimeoutSaveFailed": "Istunnon aikakatkaisun tallennus epäonnistui", - "monitoringIntervalInvalid": "Virheelliset aika-arvot", - "monitoringSaved": "Seuranta-asetukset tallennettu", - "monitoringSaveFailed": "Seuranta-asetusten tallentaminen epäonnistui", - "guacamoleSaved": "Guacamolen asetukset tallennettu", - "guacamoleSaveFailed": "Guacamolen asetusten tallennus epäonnistui", - "guacamoleUpdateFailed": "Guacamole asetuksen päivittäminen epäonnistui", - "logLevelUpdateFailed": "Lokitason päivitys epäonnistui", - "oidcSaved": "OIDC asetukset tallennettu", - "oidcSaveFailed": "OIDC config tallennus epäonnistui", - "oidcRemoved": "OIDC asetukset poistettu", - "oidcRemoveFailed": "OIDC config poistaminen epäonnistui", - "createUserRequired": "Käyttäjätunnus ja salasana vaaditaan", - "createUserPasswordTooShort": "Salasanan on oltava vähintään 6 merkkiä", - "createUserSuccess": "Käyttäjä \"{{username}}\" luotu", - "createUserFailed": "Käyttäjän luonti epäonnistui", - "updateAdminStatusFailed": "Ylläpitäjän tilan päivitys epäonnistui", - "allSessionsRevoked": "Kaikki istunnot peruutettu", - "revokeSessionsFailed": "Istuntojen peruuttaminen epäonnistui", - "createRoleRequired": "Nimi ja nimi vaaditaan", - "createRoleSuccess": "Rooli \"{{name}}\" luotu", - "createRoleFailed": "Roolin luonti epäonnistui", - "apiKeyNameRequired": "Avaimen nimi vaaditaan", - "apiKeyUserRequired": "Käyttäjätunnus on pakollinen", - "apiKeyCreatedSuccess": "API-avain \"{{name}}\" luotu", - "apiKeyCreateFailed": "API-avaimen luonti epäonnistui", - "exportSuccess": "Tietokannan vienti onnistui", - "exportFailed": "Tietokannan vienti epäonnistui", - "importSelectFile": "Ole hyvä ja valitse tiedosto ensin", - "importCompleted": "Tuonti valmis: {{total}} kohdetta tuotu, {{skipped}} ohitettu", - "importFailed": "Tuonti epäonnistui: {{error}}", - "importError": "Tietokannan tuonti epäonnistui" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Isäntä", - "hostPlaceholder": "192.168.1.1 tai esimerkki.com", - "portLabel": "Portti", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Käyttäjätunnus", - "usernamePlaceholder": "käyttäjätunnus", - "authLabel": "Todennus", - "passwordLabel": "Salasana", - "passwordPlaceholder": "salasana", - "privateKeyLabel": "Yksityinen Avain", - "privateKeyPlaceholder": "Liitä yksityinen avain...", - "credentialLabel": "Käyttöoikeustiedot", - "credentialPlaceholder": "Valitse tallennettu käyttäjätunnus", - "connectToTerminal": "Yhdistä päätteeseen", - "connectToFiles": "Yhdistä tiedostoihin" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Päätettä ei ole valittu", - "noTerminalSelectedHint": "Avaa SSH terminaalin välilehti nähdäksesi komennon historian", - "searchPlaceholder": "Etsi historiaa...", - "clearAll": "Tyhjennä Kaikki", - "noHistoryEntries": "Ei historiatietoja", - "trackingDisabled": "Historian seuranta on poistettu käytöstä", - "trackingDisabledHint": "Ota se käyttöön profiilisi asetuksissa nauhoittaaksesi komentoja." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Avaimen Tallennus", - "recordToTerminals": "Tietue päätelaitteisiin", - "selectAll": "Kaikki", - "selectNone": "Ei Mitään", - "noTerminalTabsOpen": "Päätevälilehtiä ei ole avoinna", - "selectTerminalsAbove": "Valitse yläpuolella olevat päätteet", - "broadcastInputPlaceholder": "Kirjoita tähän lähettääksesi näppäimistöjä...", - "stopRecording": "Lopeta Tallennus", - "startRecording": "Aloita Nauhoitus", - "settingsTitle": "Asetukset", - "enableRightClickCopyPaste": "Käytä hiiren kakkospainikkeella kopiota/liitä" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Asettelu", - "selectLayoutAbove": "Valitse asettelu yläpuolella", - "selectLayoutHint": "Valitse kuinka monta paneelia näytetään", - "panesTitle": "Paneelit", - "openTabsTitle": "Avaa Välilehdet", - "dragTabsHint": "Vedä välilehdet yllä oleviin ruutuihin tai käytä pikamääritystä", - "dropHere": "Pudota tähän", - "emptyPane": "Tyhjä", - "dashboard": "Hallintapaneeli", - "clearSplitScreen": "Tyhjennä Jaettu Näyttö", - "quickAssign": "Pikamääritys", - "alreadyAssigned": "Ruutu {{index}}", - "splitTab": "Jaettu välilehti", - "addToSplit": "Lisää Splitiin", - "removeFromSplit": "Poista jaosta", - "assignToPane": "Määritä ruutuun" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Luo Projekti", - "createSnippetDescription": "Luo uusi komennon tiedosto nopeaa suoritusta varten", - "nameLabel": "Nimi", - "namePlaceholder": "esim., Uudelleenkäynnistä Nginx", - "descriptionLabel": "Kuvaus", - "descriptionPlaceholder": "Valinnainen kuvaus", - "optional": "Valinnainen", - "folderLabel": "Kansio", - "noFolder": "Ei kansiota (Luokittelematon)", - "commandLabel": "Komento", - "commandPlaceholder": "esim. sudo systemctl restart nginx", - "cancel": "Peruuta", - "createSnippetButton": "Luo Projekti", - "createFolderTitle": "Luo Kansio", - "createFolderDescription": "Järjestä leikkeet kansioihin", - "folderNameLabel": "Kansion Nimi", - "folderNamePlaceholder": "esim., System Commands, Docker Scripts", - "folderColorLabel": "Kansion Väri", - "folderIconLabel": "Kansion Kuvake", - "previewLabel": "Esikatselu", - "folderNameFallback": "Kansion Nimi", - "createFolderButton": "Luo Kansio", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Kaikki", - "selectNone": "Ei Mitään", - "noTerminalTabsOpen": "Päätevälilehtiä ei ole avoinna", - "searchPlaceholder": "Etsi tiedostoja...", - "newSnippet": "Uusi Projekti", - "newFolder": "Uusi Kansio", - "run": "Suorita", - "noSnippetsInFolder": "Tässä kansiossa ei ole leikkauksia", - "uncategorized": "Luokittelematon", - "editSnippetTitle": "Muokkaa Kirjoitusta", - "editSnippetDescription": "Päivitä tämä komento tiedosto", - "saveSnippetButton": "Tallenna Muutokset", - "createSuccess": "Projektin luominen onnistui", - "createFailed": "Snippetin luonti epäonnistui", - "updateSuccess": "Projekti päivitetty onnistuneesti", - "updateFailed": "Virhe päivitettäessä tiedosto", - "deleteFailed": "Virhe poistettaessa tiedosto", - "folderCreateSuccess": "Kansio luotu onnistuneesti", - "folderCreateFailed": "Kansion luonti epäonnistui", - "editFolderTitle": "Muokkaa Kansiota", - "editFolderDescription": "Nimeä tai muuta tämän kansion ulkonäköä", - "saveFolderButton": "Tallenna Muutokset", - "editFolder": "Muokkaa kansiota", - "deleteFolder": "Poista kansio", - "folderDeleteSuccess": "Kansio \"{{name}}\" poistettu", - "folderDeleteFailed": "Kansion poistaminen epäonnistui", - "folderEditSuccess": "Kansio päivitetty onnistuneesti", - "folderEditFailed": "Kansion päivitys epäonnistui", - "confirmRunMessage": "Suorittaa \"{{name}}\"?", - "confirmRunButton": "Juokse", - "runSuccess": "Suoritti komennon \"{{name}}\" {{count}} päätteessä/päätteissä", - "copySuccess": "Kopioitu \"{{name}}\" leikepöydälle", - "shareTitle": "Jaa Projekti", - "shareUser": "Käyttäjä", - "shareRole": "Rooli", - "selectUser": "Valitse käyttäjä...", - "selectRole": "Valitse rooli...", - "shareSuccess": "Projekti jaettu onnistuneesti", - "shareFailed": "Snippetin jakaminen epäonnistui", - "revokeSuccess": "Pääsy peruttu", - "revokeFailed": "Käyttöoikeuksien peruuttaminen epäonnistui", - "currentAccess": "Nykyinen Käyttöoikeus", - "shareLoadError": "Jakamisen tietojen lataaminen epäonnistui", - "loading": "Ladataan...", - "close": "Sulje", - "reorderFailed": "Katkelmien järjestyksen tallentaminen epäonnistui" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Tili", - "sectionAppearance": "Ulkoasu", - "sectionSecurity": "Turvallisuus", - "sectionApiKeys": "Api Avaimet", - "sectionC2sTunnels": "C2S-Tunnelit", - "usernameLabel": "Käyttäjätunnus", - "roleLabel": "Rooli", - "roleAdministrator": "Ylläpitäjä", - "authMethodLabel": "Todistusmenetelmä", - "authMethodLocal": "Paikallinen", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Päällä", - "twoFaOff": "Pois", - "versionLabel": "Versio", - "deleteAccount": "Poista Tili", - "deleteAccountDescription": "Poista tilisi pysyvästi", - "deleteButton": "Poista", - "deleteAccountPermanent": "Tämä toiminto on pysyvä, eikä sitä voi peruuttaa.", - "deleteAccountWarning": "Kaikki istunnot, isännät, tunnukset ja asetukset poistetaan pysyvästi.", - "confirmPasswordDeletePlaceholder": "Anna salasanasi vahvistaaksesi", - "languageLabel": "Kieli", - "themeLabel": "Teema", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Korostettu Väri", - "settingsTerminal": "Pääte", - "commandAutocomplete": "Komennon Automaattitäydennys", - "commandAutocompleteDesc": "Näytä automaattitäydennys kirjoitettaessa", - "historyTracking": "Historian Seuranta", - "historyTrackingDesc": "Seuraa päätekomentoja", - "syntaxHighlighting": "Syntaksin Korostus", - "syntaxHighlightingDesc": "Korosta päätteen ulostulo", - "commandPalette": "Komentopaletti", - "commandPaletteDesc": "Ota käyttöön pikanäppäin", - "reopenTabsOnLogin": "Avaa välilehdet uudelleen kirjautumisen yhteydessä", - "reopenTabsOnLoginDesc": "Palauta avoimet välilehdet, kun kirjaudut sisään tai päivität sivun, jopa toiselta laitteelta", - "confirmTabClose": "Vahvista Välilehti Sulje", - "confirmTabCloseDesc": "Kysy ennen päätevälilehtien sulkemista", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Näytä Isäntätunnisteet", - "showHostTagsDesc": "Näytä tagit isäntäluettelossa", - "hostTrayOnClick": "Napsauta laajentaaksesi isäntätoiminnot", - "hostTrayOnClickDesc": "Näytä aina yhteyspainikkeet; napsauta laajentaaksesi hallinta-asetuksia hiiren osoittimen sijaan", - "pinAppRail": "Kiinnitä sovellusraide", - "pinAppRailDesc": "Pidä vasemman sivupalkin sovelluspalkki aina laajennettuna sen sijaan, että se laajenisi hiiren osoittimen vaikutuksesta", - "settingsSnippets": "Projekti", - "foldersCollapsed": "Kansiot Tiivistetty", - "foldersCollapsedDesc": "Pienennä kansiot oletuksena", - "confirmExecution": "Vahvista Suoritus", - "confirmExecutionDesc": "Vahvista ennen leikkauksia", - "settingsUpdates": "Päivitykset", - "disableUpdateChecks": "Poista Päivitystarkistukset Käytöstä", - "disableUpdateChecksDesc": "Lopeta päivitysten tarkistus", - "totpAuthenticator": "TOTP Todennuslaite", - "totpEnabled": "2FA on käytössä", - "totpDisabled": "Lisää ylimääräinen kirjautumissuojaus", - "disable": "Poista Käytöstä", - "enable": "Aktivoi", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Skannaa QR-koodi tai kirjoita salaisuus todennussovelluksessasi ja kirjoita sitten 6-numeroinen koodi", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Vahvista", - "changePassword": "Vaihda Salasana", - "currentPasswordLabel": "Nykyinen Salasana", - "currentPasswordPlaceholder": "Nykyinen salasana", - "newPasswordLabel": "Uusi Salasana", - "newPasswordPlaceholder": "Uusi salasana", - "confirmPasswordLabel": "Vahvista Uusi Salasana", - "confirmPasswordPlaceholder": "Vahvista uusi salasana", - "updatePassword": "Päivitä Salasana", - "createApiKeyTitle": "Luo API-avain", - "createApiKeyDescription": "Luo uusi API-avain ohjelmoitavalle pääsylle.", - "apiKeyNameLabel": "Nimi", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "valinnainen", - "cancel": "Peruuta", - "createKey": "Luo Avain", - "apiKeyCount": "{{count}} avainta", - "newKey": "Uusi Avain", - "noApiKeys": "Ei vielä API-avaimia.", - "apiKeyActive": "Aktiivinen", - "apiKeyUsageHint": "Sisällytä avaimesi tähän", - "apiKeyUsageHintHeader": "otsikko.", - "apiKeyPermissionsHint": "Avaimet perivät luomisen käyttäjän oikeudet.", - "roleUser": "Käyttäjä", - "authMethodDual": "Kaksoisaukko", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP-asennuksen käynnistäminen epäonnistui", - "totpEnter6Digits": "Syötä 6-numeroinen koodi", - "totpEnabledSuccess": "Kaksivaiheinen todennus käytössä", - "totpInvalidCode": "Virheellinen koodi, yritä uudelleen", - "totpDisableInputRequired": "Syötä TOTP koodi tai salasana", - "totpDisabledSuccess": "Kaksivaiheinen todennus pois käytöstä", - "totpDisableFailed": "2FA:n poistaminen käytöstä epäonnistui", - "totpDisableTitle": "Poista 2FA Käytöstä", - "totpDisablePlaceholder": "Syötä TOTP koodi tai salasana", - "totpDisableConfirm": "Poista 2FA Käytöstä", - "totpContinueVerify": "Jatka tarkistamista", - "totpVerifyTitle": "Vahvista Koodi", - "totpBackupTitle": "Varmuuskopioi Koodit", - "totpDownloadBackup": "Lataa Varmuuskopion Koodit", - "done": "Valmis", - "secretCopied": "Salainen kopioitu leikepöydälle", - "apiKeyNameRequired": "Avaimen nimi vaaditaan", - "apiKeyCreated": "API-avain \"{{name}}\" luotu", - "apiKeyCreateFailed": "API-avaimen luonti epäonnistui", - "apiKeyUser": "Käyttäjä", - "apiKeyExpires": "Vanhenee", - "apiKeyRevoked": "API-avain \"{{name}}\" kumottu", - "apiKeyRevokeFailed": "API-avaimen peruuttaminen epäonnistui", - "passwordFieldsRequired": "Nykyiset ja uudet salasanat vaaditaan", - "passwordMismatch": "Salasanat eivät täsmää", - "passwordTooShort": "Salasanan on oltava vähintään 6 merkkiä", - "passwordUpdated": "Salasana päivitetty onnistuneesti", - "passwordUpdateFailed": "Salasanan päivittäminen epäonnistui", - "deletePasswordRequired": "Salasana vaaditaan tilin poistamiseen", - "deleteFailed": "Tiliä ei voitu poistaa", - "deleting": "Poistetaan...", - "colorPickerTooltip": "Avaa värivalitsin", - "themeSystem": "Järjestelmä", - "themeLight": "Vaalea", - "themeDark": "Tumma", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solaroitu", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Yksi Tumma", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/fr_FR.json b/src/ui/locales/translated/fr_FR.json index ebe46c0f..9bc5af32 100644 --- a/src/ui/locales/translated/fr_FR.json +++ b/src/ui/locales/translated/fr_FR.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Dossiers", - "folder": "Répertoire", - "password": "Mot de passe", - "key": "Clés", - "sshPrivateKey": "Clé privée SSH", - "upload": "Charger", - "keyPassword": "Mot de passe de la clé", - "sshKey": "Clé SSH", - "uploadPrivateKeyFile": "Charger un fichier de clé privée", - "searchCredentials": "Rechercher des identifiants...", - "addCredential": "Ajouter un mot de passe", - "caCertificate": "Certificat d'AC (-cert.pub)", - "caCertificateDescription": "Optionnel: Chargez ou collez le fichier de certificat signé par CA (par exemple id_ed25519-cert.pub). Requis lorsque votre serveur SSH utilise une autorisation basée sur un certificat.", - "uploadCertFile": "Télécharger le fichier -cert.pub", - "clearCert": "Nettoyer", - "certLoaded": "Certificat chargé", - "certPublicKeyLabel": "Certificat d'AC", - "certTypeLabel": "Type de certificat", - "pasteOrUploadCert": "Collez ou téléchargez un certificat -cert.pub...", - "hasCaCert": "A un certificat d’AC", - "noCaCert": "Pas de certificat d’AC" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Impossible de charger les alertes", - "failedToDismissAlert": "Échec de la suppression de l'alerte" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configuration du serveur", - "description": "Configurer l'URL du serveur Termix pour vous connecter à vos services backend", - "serverUrl": "URL du serveur", - "enterServerUrl": "Veuillez entrer une URL de serveur", - "saveFailed": "Échec de l'enregistrement de la configuration", - "saveError": "Erreur lors de l'enregistrement de la configuration", - "saving": "Sauvegarde en cours...", - "saveConfig": "Enregistrer la configuration", - "helpText": "Entrez l'URL où fonctionne votre serveur Termix (par exemple, http://localhost:30001 ou https://votre-serveur.com)", - "changeServer": "Changer de serveur", - "mustIncludeProtocol": "L'URL du serveur doit commencer par http:// ou https://", - "allowInvalidCertificate": "Autoriser un certificat invalide", - "allowInvalidCertificateDesc": "À utiliser uniquement pour des serveurs auto-hébergés de confiance avec des certificats auto-signés ou des certificats d'adresse IP.", - "useEmbedded": "Utiliser le serveur local", - "embeddedDesc": "Exécuter Termix avec le serveur local intégré (aucun serveur distant nécessaire)", - "embeddedConnecting": "Connexion au serveur local...", - "embeddedNotReady": "Le serveur local n'est pas encore prêt. Veuillez patienter un instant et réessayer.", - "localServer": "Serveur local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Erreur de vérification de version", - "checkFailed": "Impossible de vérifier les mises à jour", - "upToDate": "L'application est à jour", - "currentVersion": "Vous utilisez la version {{version}}", - "updateAvailable": "Mise à jour disponible", - "newVersionAvailable": "Une nouvelle version est disponible ! Vous utilisez {{current}}, mais {{latest}} est disponible.", - "betaVersion": "Version bêta", - "betaVersionDesc": "Vous utilisez {{current}}, qui est plus récent que la dernière version stable {{latest}}.", - "releasedOn": "Publié sur {{date}}", - "downloadUpdate": "Télécharger la mise à jour", - "checking": "Vérification des mises à jour...", - "checkUpdates": "Vérifier les mises à jour", - "checkingUpdates": "Vérification des mises à jour...", - "updateRequired": "Mise à jour requise" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Fermer", + "close": "Close", "minimize": "Minimize", - "online": "En ligne", - "offline": "Hors ligne", - "continue": "Continuer", - "maintenance": "Entretien", - "degraded": "Dégradé", - "error": "Erreur", - "warning": "Avertissement", - "unsavedChanges": "Modifications non enregistrées", - "dismiss": "Refuser", - "loading": "Chargement en cours...", - "optional": "Optionnel", - "connect": "Connecter", - "copied": "Copié", - "connecting": "Connexion en cours...", - "updateAvailable": "Mise à jour disponible", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Ouvrir dans un nouvel onglet", - "noReleases": "Aucune publication", - "updatesAndReleases": "Mises à jour et versions", - "newVersionAvailable": "Une nouvelle version ({{version}}) est disponible.", - "failedToFetchUpdateInfo": "Impossible de récupérer les informations de mise à jour", - "preRelease": "Pré-version", - "noReleasesFound": "Aucune version trouvée.", - "cancel": "Abandonner", - "username": "Nom d'utilisateur", - "login": "Se connecter", - "register": "Inscription", - "password": "Mot de passe", - "confirmPassword": "Confirmer le mot de passe", - "back": "Précédent", - "save": "Enregistrer", - "saving": "Sauvegarde en cours...", - "delete": "Supprimez", - "rename": "Renommer", - "edit": "Editer", - "add": "Ajouter", - "confirm": "Valider", - "no": "Non", - "or": "OU", - "next": "Suivant", - "previous": "Précédent", - "refresh": "Rafraîchir", - "language": "Langue", - "checking": "Vérification...", - "checkingDatabase": "Vérification de la connexion à la base de données...", - "checkingAuthentication": "Vérification de l'authentification...", - "backendReconnected": "Connexion au serveur restaurée", - "connectionDegraded": "Connexion au serveur perdue, récupération de…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Retirer", - "create": "Créer", - "update": "Mise à jour", - "copy": "Copie", - "copyFailed": "Échec de la copie dans le presse-papiers", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Restaurer", - "of": "de" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Domicile", + "home": "Home", "terminal": "Terminal", "docker": "Docker", "tunnels": "Tunnels", - "fileManager": "Gestionnaire de fichiers", - "serverStats": "Statistiques du serveur", - "admin": "Administrateur", - "userProfile": "Profil de l'utilisateur", - "splitScreen": "Écran partagé", - "confirmClose": "Fermer cette session active ?", - "close": "Fermer", - "cancel": "Abandonner", - "sshManager": "Gestionnaire SSH", - "cannotSplitTab": "Impossible de diviser cet onglet", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copier le mot de passe", - "copySudoPassword": "Copier le mot de passe Sudo", - "passwordCopied": "Mot de passe copié dans le presse-papiers", - "noPasswordAvailable": "Aucun mot de passe disponible", - "failedToCopyPassword": "Impossible de copier le mot de passe", - "refreshTab": "Rafraîchir la connexion", - "openFileManager": "Ouvrir le gestionnaire de fichiers", - "dashboard": "Tableau de bord", - "networkGraph": "Graphique réseau", - "quickConnect": "Connexion Rapide", - "sshTools": "Outils SSH", - "history": "Historique", - "hosts": "Hôtes", - "snippets": "Extraits", - "hostManager": "Responsable d'hôte", - "credentials": "Identifiants", - "connections": "Relations", - "roleAdministrator": "Administrateur", - "roleUser": "Utilisateur" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Hôtes", - "noHosts": "Aucun hôte SSH", - "retry": "Réessayer", - "refresh": "Rafraîchir", - "optional": "Optionnel", - "downloadSample": "Télécharger un exemple", - "failedToDeleteHost": "Échec de la suppression de {{name}}", - "importSkipExisting": "Importer (sauter le existant)", - "connectionDetails": "Détails de la connexion", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Bureau distant", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Nom d'utilisateur", - "folder": "Répertoire", + "username": "Username", + "folder": "Folder", "tags": "Tags", - "pin": "Épingler", - "addHost": "Ajouter un hôte", - "editHost": "Modifier l'hôte", - "cloneHost": "Cloner l'hôte", - "enableTerminal": "Activer le Terminal", - "enableTunnel": "Activer le tunnel", - "enableFileManager": "Activer le gestionnaire de fichiers", - "enableDocker": "Activer Docker", - "defaultPath": "Chemin par défaut", - "connection": "Raccordement", - "upload": "Charger", - "authentication": "Authentification", - "password": "Mot de passe", - "key": "Clés", - "credential": "Identification", - "none": "Aucun", - "sshPrivateKey": "Clé privée SSH", - "keyType": "Type de clé", - "uploadFile": "Charger un fichier", - "tabGeneral": "Généraux", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Fichiers", - "tabStats": "Stats", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Partage en cours", - "tabAuthentication": "Authentification", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Gestionnaire de fichiers", - "serverStats": "Statistiques du serveur", - "status": "Statut", - "folderRenamed": "Le dossier \"{{oldName}}\" a été renommé en \"{{newName}}\" avec succès", - "failedToRenameFolder": "Impossible de renommer le dossier", - "movedToFolder": "Déplacé vers \"{{folder}}\"", - "editHostTooltip": "Modifier l'hôte", - "statusChecks": "Contrôles de statut", - "metricsCollection": "Collection de métriques", - "metricsInterval": "Intervalle de collecte des métriques", - "metricsIntervalDesc": "Fréquence de collecte des statistiques du serveur (5s - 1h)", - "behavior": "Comportement", - "themePreview": "Aperçu du thème", - "theme": "Thème", - "fontFamily": "Famille de police", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Espacement des lettres", - "lineHeight": "Hauteur de la ligne", - "cursorStyle": "Style du curseur", - "cursorBlink": "Clignotement du curseur", - "scrollbackBuffer": "Défilement du tampon", - "bellStyle": "Style de la cloche", - "rightClickSelectsWord": "Clic droit sélectionne le mot", - "fastScrollModifier": "Modificateur de défilement rapide", - "fastScrollSensitivity": "Sensibilité au défilement rapide", - "sshAgentForwarding": "Transfert d'Agent SSH", - "backspaceMode": "Mode Retour arrière", - "startupSnippet": "Snippet de démarrage", - "selectSnippet": "Sélectionner un snippet", - "forceKeyboardInteractive": "Forcer l'interaction du clavier", - "overrideCredentialUsername": "Remplacer le nom d'utilisateur de l'identifiant", - "overrideCredentialUsernameDesc": "Utilisez le nom d'utilisateur spécifié ci-dessus au lieu du nom d'utilisateur", - "jumpHostChain": "Chaîne de saut d'hôte", - "portKnocking": "Saut du port", - "addKnock": "Ajouter un port", - "addProxyNode": "Ajouter un noeud", - "proxyNode": "Noeud Proxy", - "proxyType": "Type de proxy", - "quickActions": "Actions rapides", - "sudoPasswordAutoFill": "Remplissage automatique du mot de passe Sudo", - "sudoPassword": "Mot de passe Sudo", - "keepaliveInterval": "Intervalle de Keepalive (ms)", - "moshCommand": "Commande MOSH", - "environmentVariables": "Variables d'environnement", - "addVariable": "Ajouter une variable", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Copier l'URL du terminal", - "copyFileManagerUrl": "Copier l'URL du gestionnaire de fichiers", - "copyRemoteDesktopUrl": "Copier l'URL du bureau distant", - "failedToConnect": "Impossible de se connecter à la console", - "connect": "Connecter", - "disconnect": "Déconnecter", - "start": "Début", - "enableStatusCheck": "Activer la vérification de l'état", - "enableMetrics": "Activer les métriques", - "bulkUpdateFailed": "La mise à jour en bloc a échoué", - "selectAll": "Tout sélectionner", - "deselectAll": "Désélectionner tout", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Shell sécurisé", - "virtualNetwork": "Réseau virtuel", - "unencryptedShell": "shell non chiffré", - "addressIp": "Adresse / IP", - "friendlyName": "Nom convivial", - "folderAndAdvanced": "Dossier & Avancé", - "privateNotes": "Notes privées", - "privateNotesPlaceholder": "Détails sur ce serveur...", - "pinToTop": "Épingler en haut", - "pinToTopDesc": "Toujours afficher cet hôte en haut de la liste", - "portKnockingSequence": "Séquence de mise à terre du port", - "addKnockBtn": "Ajouter un Knock", - "noPortKnocking": "Aucune frappe de port configurée.", - "knockPort": "Port Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Délai après (ms)", - "useSocks5Proxy": "Utiliser le proxy SOCKS5", - "useSocks5ProxyDesc": "Router la connexion à travers un serveur proxy", - "proxyHost": "Hôte du Proxy", - "proxyPort": "Port du proxy", - "proxyUsername": "Nom d'utilisateur du proxy", - "proxyPassword": "Mot de passe du proxy", - "proxySingleMode": "Proxy unique", - "proxyChainMode": "Chaîne de Proxy", - "you": "Vous", - "jumpHostChainLabel": "Chaîne de saut d'hôte", - "addJumpBtn": "Ajouter un saut", - "noJumpHosts": "Aucun hôte de saut configuré.", - "selectAServer": "Sélectionnez un serveur...", - "sshPort": "Port SSH", - "authMethod": "Méthode d'authentification", - "storedCredential": "Identification enregistrée", - "selectACredential": "Sélectionnez un identifiant...", - "keyTypeLabel": "Type de clé", - "keyTypeAuto": "Détection automatique", - "keyPasteTab": "Coller", - "keyUploadTab": "Charger", - "keyFileLoaded": "Fichier de clé chargé", - "keyUploadClick": "Cliquez pour télécharger .pem / .key / .ppk", - "clearKey": "Effacer la clé", - "keySaved": "Clé SSH enregistrée", - "keyReplaceNotice": "collez une nouvelle clé ci-dessous pour la remplacer", - "keyPassphraseSaved": "Phrase de passe enregistrée, tapez pour changer", - "replaceKey": "Remplacer la clé", - "forceKeyboardInteractiveLabel": "Forcer l'interaction du clavier", - "forceKeyboardInteractiveShortDesc": "Forcer la saisie manuelle du mot de passe même si les clés sont présentes", - "terminalAppearance": "Apparence du terminal", - "colorTheme": "Thème de couleur", - "fontFamilyLabel": "Famille de police", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Style du curseur", - "letterSpacingPx": "Espacement de la lettre (px)", - "lineHeightLabel": "Hauteur de la ligne", - "bellStyleLabel": "Style de la cloche", - "backspaceModeLabel": "Mode Retour arrière", - "cursorBlinking": "Clignotement du curseur", - "cursorBlinkingDesc": "Activer l'animation clignotant pour le curseur du terminal", - "rightClickSelectsWordLabel": "Clic droit sélectionne le mot", - "rightClickSelectsWordShortDesc": "Sélectionnez le mot sous le curseur sur le clic droit", - "behaviorAndAdvanced": "Comportement & Avancé", - "scrollbackBufferLabel": "Défilement du tampon", - "scrollbackMaxLines": "Nombre maximum de lignes conservées dans l'historique", - "sshAgentForwardingLabel": "Transfert d'Agent SSH", - "sshAgentForwardingShortDesc": "Transmettez vos clés SSH locales à cet hôte", - "enableAutoMosh": "Activer le Mosh automatique", - "enableAutoMoshDesc": "Préférer Mosh à SSH si disponible", - "enableAutoTmux": "Activer automatiquement Tmux", - "enableAutoTmuxDesc": "Lancer ou attacher automatiquement à la session tmux", - "sudoPasswordAutoFillLabel": "Remplissage automatique du mot de passe Sudo", - "sudoPasswordAutoFillShortDesc": "Fournir automatiquement le mot de passe sudo lorsque vous y êtes invité", - "sudoPasswordLabel": "Mot de passe Sudo", - "environmentVariablesLabel": "Variables d'environnement", - "addVariableBtn": "Ajouter une variable", - "noEnvVars": "Aucune variable d'environnement configurée.", - "fastScrollModifierLabel": "Modificateur de défilement rapide", - "fastScrollSensitivityLabel": "Sensibilité au défilement rapide", - "moshCommandLabel": "Commande Mosh", - "startupSnippetLabel": "Snippet de démarrage", - "keepaliveIntervalLabel": "Intervalle de maintien en vie (secondes)", - "maxKeepaliveMisses": "Nombre max de manques de Keepalive", - "tunnelSettings": "Paramètres du tunnel", - "enableTunneling": "Activer le Tunneling", - "enableTunnelingDesc": "Activer les fonctionnalités du tunnel SSH pour cet hôte", - "serverTunnelsSection": "Tunnels serveur", - "addTunnelBtn": "Ajouter un tunnel", - "noTunnelsConfigured": "Aucun tunnel configuré.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Type de tunnel", - "tunnelModeLocalDesc": "Transférer un port local vers un port sur le serveur distant (ou un hôte accessible depuis celui-ci).", - "tunnelModeRemoteDesc": "Rediriger un port sur le serveur distant vers un port local sur votre machine.", - "tunnelModeDynamicDesc": "Créer un proxy SOCKS5 sur un port local pour une redirection de port dynamique.", - "sameHost": "Cet hôte (tunnel direct)", - "endpointHost": "Hôte de point d'extrémité", - "endpointPort": "Port de terminaison", - "bindHost": "Lier l'hôte", - "sourcePort": "Port source", - "maxRetries": "Nombre maximum de tentatives", - "retryIntervalS": "Intervalle d'essai (s)", - "autoStartLabel": "Démarrage automatique", - "autoStartDesc": "Connecter automatiquement ce tunnel lorsque l'hôte est chargé", - "tunnelConnecting": "Connexion du tunnel...", - "tunnelDisconnected": "Tunnel déconnecté", - "failedToConnectTunnel": "Échec de la connexion", - "failedToDisconnectTunnel": "Échec de la déconnexion", - "dockerIntegration": "Intégration de Docker", - "enableDockerMonitor": "Activer Docker", - "enableDockerMonitorDesc": "Surveiller et gérer les conteneurs sur cet hôte via Docker", - "enableFileManagerMonitor": "Activer le gestionnaire de fichiers", - "enableFileManagerMonitorDesc": "Parcourir et gérer les fichiers sur cet hôte via SFTP", - "defaultPathLabel": "Chemin par défaut", - "fileManagerPathHint": "Le répertoire à ouvrir lorsque le gestionnaire de fichiers démarre pour cet hôte.", - "statusChecksLabel": "Contrôles de statut", - "enableStatusChecks": "Activer les vérifications de statut", - "enableStatusChecksDesc": "Passe périodiquement sur cet hôte pour vérifier la disponibilité", - "useGlobalInterval": "Utiliser l'intervalle global", - "useGlobalIntervalDesc": "Remplacer par l'intervalle de vérification de l'état du serveur", - "checkIntervalS": "Intervalle de vérification (s)", - "checkIntervalDesc": "Secondes entre chaque ping de connectivité", - "metricsCollectionLabel": "Collection de métriques", - "enableMetricsLabel": "Activer les métriques", - "enableMetricsDesc": "Collecter l'utilisation du CPU, de la RAM, du disque et du réseau depuis cet hôte", - "useGlobalMetrics": "Utiliser l'intervalle global", - "useGlobalMetricsDesc": "Remplacer par l'intervalle de métriques à l'échelle du serveur", - "metricsIntervalS": "Intervalle métrique (s)", - "metricsIntervalDesc2": "Secondes entre les instantanés métriques", - "visibleWidgets": "Widgets visibles", - "cpuUsageLabel": "Usage du CPU", - "cpuUsageDesc": "Pourcentage du processeur, charge moyenne, graphique de la ligne d'étincelles", - "memoryLabel": "Utilisation de la mémoire", - "memoryDesc": "Utilisation de la mémoire, swap, mise en cache", - "storageLabel": "Utilisation du disque", - "storageDesc": "Utilisation du disque par point de montage", - "networkLabel": "Interfaces réseau", - "networkDesc": "Liste des interfaces et bande passante", - "uptimeLabel": "Délai de disponibilité", - "uptimeDesc": "Durée de disponibilité et de démarrage du système", - "systemInfoLabel": "Infos système", - "systemInfoDesc": "OS, noyau, nom d'hôte, architecture", - "recentLoginsLabel": "Connexions récentes", - "recentLoginsDesc": "Événements de connexion réussis et échoués", - "topProcessesLabel": "Les meilleurs processus", - "topProcessesDesc": "PID, CPU%, MEM%, commande", - "listeningPortsLabel": "Ports d'écoute", - "listeningPortsDesc": "Ouvrir les ports avec processus et état", - "firewallLabel": "Pare-feu", - "firewallDesc": "Pare-feu, AppArmor, statut SELinux", - "quickActionsLabel": "Actions rapides", - "quickActionsToolbar": "Les actions rapides apparaissent sous forme de boutons dans la barre d'outils Stats du serveur pour l'exécution d'une commande en un clic.", - "noQuickActions": "Aucune action rapide pour le moment.", - "buttonLabel": "Libellé du bouton", - "selectSnippetPlaceholder": "Sélectionner un snippet...", - "addActionBtn": "Ajouter une action", - "hostSharedSuccessfully": "Hôte partagé avec succès", - "failedToShareHost": "Impossible de partager l'hôte", - "accessRevoked": "Accès révoqué", - "failedToRevokeAccess": "Impossible de révoquer l'accès", - "cancelBtn": "Abandonner", - "savingBtn": "Sauvegarde en cours...", - "addHostBtn": "Ajouter un hôte", - "hostUpdated": "Hôte mis à jour", - "hostCreated": "Hôte créé", - "failedToSave": "Échec de l'enregistrement de l'hôte", - "credentialUpdated": "Identification mise à jour", - "credentialCreated": "Identification créée", - "failedToSaveCredential": "Échec de l'enregistrement des informations d'identification", - "backToHosts": "Retour aux hôtes", - "backToCredentials": "Retour aux informations d'identification", - "pinned": "Épinglé", - "noHostsFound": "Aucun hôte trouvé", - "tryDifferentTerm": "Essayez un terme différent", - "addFirstHost": "Ajoutez votre premier hôte pour commencer", - "noCredentialsFound": "Aucun identifiant trouvé", - "addCredentialBtn": "Ajouter un mot de passe", - "updateCredentialBtn": "Mettre à jour les informations d'identification", - "features": "Fonctionnalités", - "noFolder": "(Aucun dossier)", - "deleteSelected": "Supprimez", - "exitSelection": "Quitter la sélection", - "importSkip": "Importer (sauter le existant)", - "importOverwrite": "Importer (écraser)", - "collapseBtn": "Réduire", - "importExportBtn": "Importer/Exporter", - "hostStatusesRefreshed": "Statuts de l'hôte actualisés", - "failedToRefreshHosts": "Échec de l'actualisation des hôtes", - "movedHostTo": "A déplacé {{host}} vers \"{{folder}}\"", - "failedToMoveHost": "Impossible de déplacer l'hôte", - "folderRenamedTo": "Dossier renommé en \"{{name}}\"", - "deletedFolder": "Dossier \"{{name}} \" supprimé", - "failedToDeleteFolder": "Échec de la suppression du dossier", - "deleteAllInFolder": "Supprimer tous les hôtes de \"{{name}}\" ? Cette action est irréversible.", - "deletedHost": "Supprimé {{name}}", - "copiedToClipboard": "Copié dans le presse-papiers", - "terminalUrlCopied": "URL du terminal copiée", - "fileManagerUrlCopied": "URL du gestionnaire de fichiers copiée", - "tunnelUrlCopied": "URL du tunnel copiée", - "dockerUrlCopied": "URL de Docker copiée", - "serverStatsUrlCopied": "URL des statistiques du serveur copiée", - "rdpUrlCopied": "URL RDP copiée", - "vncUrlCopied": "URL VNC copiée", - "telnetUrlCopied": "URL Telnet copiée", - "remoteDesktopUrlCopied": "URL du bureau distant copiée", - "expandActions": "Développer les actions", - "collapseActions": "Actions d'effondrement", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Paquet magique envoyé à {{name}}", - "wakeOnLanError": "Échec de l'envoi du paquet magique", - "cloneHostAction": "Cloner l'hôte", - "copyAddress": "Copier l'adresse", - "copyLink": "Copier le lien", - "copyTerminalUrlAction": "Copier l'URL du terminal", - "copyFileManagerUrlAction": "Copier l'URL du gestionnaire de fichiers", - "copyTunnelUrlAction": "Copier l'URL du tunnel", - "copyDockerUrlAction": "Copier l'URL de Docker", - "copyServerStatsUrlAction": "Copier l'URL des statistiques du serveur", - "copyRdpUrlAction": "Copier l'URL RDP", - "copyVncUrlAction": "Copier l'URL VNC", - "copyTelnetUrlAction": "Copier l'URL Telnet", - "copyRemoteDesktopUrlAction": "Copier l'URL du bureau distant", - "deleteCredentialConfirm": "Supprimer les identifiants \"{{name}} \" ?", - "deletedCredential": "Supprimé {{name}}", - "deploySSHKeyTitle": "Déployer la clé SSH", - "deployingBtn": "Déploiement...", - "deployBtn": "Déployer", - "failedToDeployKey": "Échec du déploiement de la clé", - "deleteHostsConfirm": "Supprimer l'hôte {{count}}{{plural}}? Cette action est irréversible.", - "movedToRoot": "Déplacé vers la racine", - "failedToMoveHosts": "Impossible de déplacer les hôtes", - "enableTerminalFeature": "Activer le Terminal", - "disableTerminalFeature": "Désactiver le terminal", - "enableFilesFeature": "Activer les fichiers", - "disableFilesFeature": "Désactiver les fichiers", - "enableTunnelsFeature": "Activer les tunnels", - "disableTunnelsFeature": "Désactiver les tunnels", - "enableDockerFeature": "Activer Docker", - "disableDockerFeature": "Désactiver Docker", - "addTagsPlaceholder": "Ajouter des tags...", - "authDetails": "Détails d'authentification", - "credType": "Type de texte", - "generateKeyPairDesc": "Générer une nouvelle paire de clés, les clés privées et publiques seront remplies automatiquement.", - "generatingKey": "Génération en cours...", - "generateLabel": "Générer {{label}}", - "uploadFileBtn": "Charger un fichier", - "keyPassphraseOptional": "Phrase de passe de la clé (facultatif)", - "sshPublicKeyOptional": "Clé publique SSH (facultatif)", - "publicKeyGenerated": "Clé publique générée", - "failedToGeneratePublicKey": "Impossible de dériver la clé publique", - "publicKeyCopied": "Clé publique copiée", - "keyPairGenerated": "{{label}} paire de clés générée", - "failedToGenerateKeyPair": "Impossible de générer la paire de clés", - "searchHostsPlaceholder": "Rechercher des hôtes, adresses, tags…", - "searchCredentialsPlaceholder": "Rechercher les identifiants…", - "refreshBtn": "Rafraîchir", - "addTag": "Ajouter des tags...", - "deleteConfirmBtn": "Supprimez", - "tunnelRequirementsText": "Le serveur SSH doit avoir GatewayPorts yes, AllowTcpForwarding oui, et PermitRootLogin yes défini dans /etc/ssh/sshd_config.", - "deleteHostConfirm": "Supprimer \"{{name}}\" ?", - "enableAtLeastOneProtocol": "Activez au moins un protocole ci-dessus pour configurer les paramètres d'authentification et de connexion.", - "keyPassphrase": "Mot de passe de la clé", - "connectBtn": "Connecter", - "disconnectBtn": "Déconnecter", - "basicInformation": "Informations de base", - "authDetailsSection": "Détails d'authentification", - "credTypeLabel": "Type de texte", - "hostsTab": "Hôtes", - "credentialsTab": "Identifiants", - "selectMultiple": "Sélectionner plusieurs", - "selectHosts": "Sélectionner les hôtes", - "connectionLabel": "Raccordement", - "authenticationLabel": "Authentification", - "generateKeyPairTitle": "Générer une paire de clés", - "generateKeyPairDescription": "Générer une nouvelle paire de clés, les clés privées et publiques seront remplies automatiquement.", - "generateFromPrivateKey": "Générer à partir de la clé privée", - "refreshBtn2": "Rafraîchir", - "exitSelectionTitle": "Quitter la sélection", - "exportAll": "Exporter tout", - "addHostBtn2": "Ajouter un hôte", - "addCredentialBtn2": "Ajouter un mot de passe", - "checkingHostStatuses": "Vérification des statuts des hôtes...", - "pinnedSection": "Épinglé", - "hostsExported": "Hôtes exportés avec succès", - "exportFailed": "Échec de l'exportation des hôtes", - "sampleDownloaded": "Exemple de fichier téléchargé", - "failedToDeleteCredential2": "Échec de la suppression des identifiants", - "noFolderOption": "(Aucun dossier)", - "nSelected": "{{count}} sélectionné", - "featuresMenu": "Fonctionnalités", - "moveMenu": "Déplacer", - "cancelSelection": "Abandonner", - "deployDialogDesc": "Déployer {{name}} sur les clefs authorized_keys d'un hôte.", - "targetHostLabel": "Hôte cible", - "selectHostOption": "Sélectionnez un hôte...", - "keyDeployedSuccess": "Clé déployée avec succès", - "failedToDeployKey2": "Échec du déploiement de la clé", - "deletedCount": "Hôtes {{count}} supprimés", - "failedToDeleteCount": "Impossible de supprimer les hôtes {{count}}", - "duplicatedHost": "Dupliqué \"{{name}}\"", - "failedToDuplicateHost": "Impossible de dupliquer l'hôte", - "updatedCount": "Hôtes {{count}} mis à jour", - "friendlyNameLabel": "Nom convivial", - "descriptionLabel": "Libellé", - "loadingHost": "Chargement de l'hôte...", - "loadingHosts": "Chargement des hôtes...", - "loadingCredentials": "Chargement des identifiants...", - "noHostsYet": "Pas encore d'hôtes", - "noHostsMatchSearch": "Aucun hôte ne correspond à votre recherche", - "hostNotFound": "Hôte introuvable", - "searchHosts": "Rechercher des hôtes...", - "sortHosts": "Trier les hôtes", - "sortDefault": "Ordre par défaut", - "sortNameAsc": "Nom (A → Z)", - "sortNameDesc": "Nom (Z → A)", - "sortIpAsc": "Adresse IP (Asc)", - "sortIpDesc": "Adresse IP (Description)", - "sortOnlineFirst": "En ligne d'abord", - "sortOfflineFirst": "D'abord hors ligne", - "sortPinnedFirst": "Épinglé en premier", - "filterHosts": "Filtrer les hôtes", - "filterClearAll": "Filtres transparents", - "filterStatusGroup": "Statut", - "filterOnline": "En ligne", - "filterOffline": "Hors ligne", - "filterPinned": "Épinglé", - "filterAuthGroup": "Type d'autorisation", - "filterAuthPassword": "Mot de passe", - "filterAuthKey": "Clé SSH", - "filterAuthCredential": "Attestation", - "filterAuthNone": "Aucun", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protocole", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Caractéristiques", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Gestionnaire de fichiers", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Étiquettes", - "shareHost": "Partager l'hôte", - "shareHostTitle": "Partager: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Cet hôte doit utiliser un identifiant pour activer le partage. Modifiez l'hôte et assignez d'abord un identifiant." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Raccordement", - "authentication": "Authentification", - "connectionSettings": "Paramètres de connexion", - "displaySettings": "Paramètres d'affichage", - "audioSettings": "Paramètres audio", - "rdpPerformance": "Performance RDP", - "deviceRedirection": "Redirection de l'appareil", - "session": "Séance", - "gateway": "Passerelle", - "remoteApp": "Application distante", - "clipboard": "Presse-papiers", - "sessionRecording": "Enregistrement de session", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Paramètres VNC", - "terminalSettings": "Paramètres du terminal", - "rdpPort": "Port RDP", - "username": "Nom d'utilisateur", - "password": "Mot de passe", - "domain": "Domaine", - "securityMode": "Mode de sécurité", - "colorDepth": "Profondeur des couleurs", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Hauteur", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Redimensionner la méthode", - "clientName": "Nom du client", - "initialProgram": "Programme initial", - "serverLayout": "Mise en page du serveur", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Port de la passerelle", - "gatewayUsername": "Nom d'utilisateur de la passerelle", - "gatewayPassword": "Mot de passe de la passerelle", - "gatewayDomain": "Domaine de la passerelle", - "remoteAppProgram": "Programme RemoteApp", - "workingDirectory": "Répertoire de travail", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", "arguments": "Arguments", - "normalizeLineEndings": "Normaliser les terminaisons de ligne", - "recordingPath": "Chemin de l'enregistrement", - "recordingName": "Nom de l'enregistrement", - "macAddress": "Adresse MAC", - "broadcastAddress": "Adresse de diffusion", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Temps d'attente (s)", - "driveName": "Nom du lecteur", - "drivePath": "Chemin du lecteur", - "ignoreCertificate": "Ignorer le certificat", - "ignoreCertificateDesc": "Autoriser les connexions aux hôtes avec des certificats auto-signés", - "forceLossless": "Forcer sans perte", - "forceLosslessDesc": "Forcer l'encodage sans perte d'image (meilleure qualité, plus de bande passante)", - "disableAudio": "Désactiver l'audio", - "disableAudioDesc": "Couper tous les sons de la session distante", - "enableAudioInput": "Activer la saisie audio (Microphone)", - "enableAudioInputDesc": "Transférer le microphone local vers la session distante", - "wallpaper": "Fond d'écran", - "wallpaperDesc": "Afficher le fond d'écran du bureau (désactiver améliore les performances)", - "theming": "Thème", - "themingDesc": "Activer les thèmes et styles visuels", - "fontSmoothing": "Lissage de la police", - "fontSmoothingDesc": "Activer le rendu de police ClearType", - "fullWindowDrag": "Glisser la fenêtre entière", - "fullWindowDragDesc": "Afficher le contenu de la fenêtre en faisant glisser", - "desktopComposition": "Composition de bureau", - "desktopCompositionDesc": "Activer les effets de verre Aero", - "menuAnimations": "Animations du menu", - "menuAnimationsDesc": "Activer les animations de fondu du menu et de diapositives", - "disableBitmapCaching": "Désactiver la mise en cache des Bitmaps", - "disableBitmapCachingDesc": "Désactiver le cache bitmap (peut aider à résoudre les bugs)", - "disableOffscreenCaching": "Désactiver la mise en cache hors écran", - "disableOffscreenCachingDesc": "Désactiver le cache de l'écran", - "disableGlyphCaching": "Désactiver la mise en cache des glyphes", - "disableGlyphCachingDesc": "Désactiver le cache des glyphes", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Utiliser le pipeline graphique de RemoteFX", - "enablePrinting": "Activer l'impression", - "enablePrintingDesc": "Rediriger les imprimantes locales vers la session distante", - "enableDriveRedirection": "Activer la redirection du lecteur", - "enableDriveRedirectionDesc": "Mapper un dossier local en tant que lecteur dans la session distante", - "createDrivePath": "Créer un chemin d'accès", - "createDrivePathDesc": "Créer automatiquement le dossier s'il n'existe pas", - "disableDownload": "Désactiver le téléchargement", - "disableDownloadDesc": "Empêcher le téléchargement des fichiers de la session distante", - "disableUpload": "Désactiver le téléchargement", - "disableUploadDesc": "Empêcher le téléversement de fichiers vers la session distante", - "enableTouch": "Activer le Toucher", - "enableTouchDesc": "Activer le transfert tactile", - "consoleSession": "Session de la console", - "consoleSessionDesc": "Se connecter à la console (session 0) au lieu d'une nouvelle session", - "sendWolPacket": "Envoyer le paquet WOL", - "sendWolPacketDesc": "Envoyer un paquet magique pour réveiller cet hôte avant de se connecter", - "disableCopy": "Désactiver la copie", - "disableCopyDesc": "Empêcher la copie du texte de la session distante", - "disablePaste": "Désactiver Coller", - "disablePasteDesc": "Empêcher le collage du texte dans la session distante", - "createPathIfMissing": "Créer un chemin si manquant", - "createPathIfMissingDesc": "Créer automatiquement le répertoire d'enregistrement", - "excludeOutput": "Exclure la sortie", - "excludeOutputDesc": "Ne pas enregistrer la sortie de l'écran (métadonnées seulement)", - "excludeMouse": "Exclure la souris", - "excludeMouseDesc": "Ne pas enregistrer les mouvements de la souris", - "includeKeystrokes": "Inclure les touches", - "includeKeystrokesDesc": "Enregistrer les traits de touches brutes en plus de la sortie de l'écran", - "vncPort": "Port VNC", - "vncPassword": "Mot de passe VNC", - "vncUsernameOptional": "Nom d'utilisateur (facultatif)", - "vncLeaveBlank": "Laisser vide si non requis", - "cursorMode": "Mode Curseur", - "swapRedBlue": "Échanger Rouge/Bleu", - "swapRedBlueDesc": "Inverser les canaux de couleur rouge et bleu (corrige quelques problèmes de couleur)", - "readOnly": "Lecture seule", - "readOnlyDesc": "Afficher l'écran distant sans envoyer de saisie", - "telnetPort": "Port Telnet", - "terminalType": "Type de terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Schéma de couleurs", - "backspaceKey": "Clé de Retour arrière", - "saveHostFirst": "Enregistrez d'abord l'hôte.", - "sharingOptionsAfterSave": "Les options de partage sont disponibles après l'enregistrement de l'hôte.", - "sharingLoadError": "Impossible de charger les données de partage. Vérifiez votre connexion et réessayez.", - "shareHostSection": "Partager l'hôte", - "shareWithUser": "Partager avec l'utilisateur", - "shareWithRole": "Partager avec le rôle", - "selectUser": "Sélectionner un utilisateur", - "selectRole": "Sélectionner un rôle", - "selectUserOption": "Sélectionnez un utilisateur...", - "selectRoleOption": "Sélectionnez un rôle...", - "permissionLevel": "Niveau de permission", - "expiresInHours": "Expire dans (heures)", - "noExpiryPlaceholder": "Laisser vide pour aucune expiration", - "shareBtn": "Partager", - "currentAccess": "Accès actuel", - "typeHeader": "Type de texte", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", "permissionHeader": "Permission", - "grantedByHeader": "Accepté par", - "expiresHeader": "Expire", - "noAccessEntries": "Pas encore d'entrées d'accès.", - "expiredLabel": "Expiré", - "neverLabel": "Jamais", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Abandonner", - "savingBtn": "Sauvegarde en cours...", - "updateHostBtn": "Mettre à jour l'hôte", - "addHostBtn": "Ajouter un hôte" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Rechercher des hôtes, des commandes ou des paramètres...", - "quickActions": "Actions rapides", - "hostManager": "Responsable d'hôte", - "hostManagerDesc": "Gérer, ajouter ou éditer des hôtes", - "addNewHost": "Ajouter un nouvel hôte", - "addNewHostDesc": "Enregistrer un nouvel hôte", - "adminSettings": "Paramètres de l'administrateur", - "adminSettingsDesc": "Configurer les préférences système et les utilisateurs", - "userProfile": "Profil de l'utilisateur", - "userProfileDesc": "Gérer votre compte et vos préférences", - "addCredential": "Ajouter un mot de passe", - "addCredentialDesc": "Stocker les clés SSH ou les mots de passe", - "recentActivity": "Activité récente", - "serversAndHosts": "Serveurs & Hôtes", - "noHostsFound": "Aucun hôte ne correspond à \"{{search}}\"", - "links": "Liens", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Sélectionner", - "toggleWith": "Basculer avec" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Aucun onglet assigné", - "focusedPane": "Volet actif" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Aucune connexion", - "noConnectionsDesc": "Ouvrez un terminal, un gestionnaire de fichiers ou un bureau à distance pour voir les connexions ici.", - "connectedFor": "Connecté pour {{duration}}", - "connected": "Connecté", - "disconnected": "Déconnecté", - "closeTab": "Fermer l'onglet", - "closeConnection": "Connexion étroite", - "forgetTab": "Oublier", - "removeBackground": "Retirer", + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", "reconnect": "Reconnect", - "reopenTab": "Rouvrir", - "sectionOpen": "Ouvrir", - "sectionBackground": "Arrière-plan", - "backgroundDesc": "Les sessions restent actives pendant 30 minutes après la déconnexion et peuvent être reconnectées.", - "persisted": "Persistant en arrière-plan", - "expiresIn": "Expire dans {{duration}}", - "search": "Rechercher des connexions...", - "noSearchResults": "Aucun résultat ne correspond à votre recherche" + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Connexion à la session {{type}}...", - "connectionError": "Erreur de connexion", - "connectionFailed": "Échec de la connexion", - "failedToConnect": "Impossible d'obtenir le jeton de connexion", - "hostNotFound": "Hôte introuvable", - "noHostSelected": "Aucun hôte sélectionné", - "reconnect": "Reconnecter", - "retry": "Réessayer", - "guacdUnavailable": "Le service de bureau à distance (guacd) n'est pas disponible. Veuillez vous assurer que guacd fonctionne et est accessible et configuré correctement dans les paramètres d'administration.", - "ctrlAltDel": "Ctrl+Alt+Suppr", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl+Alt+Suppr", - "winL": "Win+L (Écran de verrouillage)", - "winKey": "Clé Windows", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Maj", - "win": "Victoire", - "stickyActive": "{{key}} (accroché - cliquez pour libérer)", - "stickyInactive": "{{key}} (cliquez pour verrouiller)", - "esc": "Échapper", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Domicile", - "end": "Fin", - "pageUp": "Page Haut", - "pageDown": "Page Bas", - "arrowUp": "Flèche vers le haut", - "arrowDown": "Flèche vers le bas", - "arrowLeft": "Flèche Gauche", - "arrowRight": "Flèche droite", - "fnToggle": "Touches de fonction", - "reconnect": "Reconnecter la session", - "collapse": "Réduire la barre d'outils", - "expand": "Développer la barre d'outils", - "dragHandle": "Faites glisser pour repositionner" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Se connecter à l'hôte", - "clear": "Nettoyer", - "paste": "Coller", - "reconnect": "Reconnecter", - "connectionLost": "Connexion perdue", - "connected": "Connecté", - "clipboardWriteFailed": "Échec de la copie dans le presse-papiers. Assurez-vous que la page est servie via HTTPS ou localhost.", - "clipboardReadFailed": "Échec de lecture du presse-papiers. Assurez-vous que les permissions du presse-papiers sont accordées.", - "clipboardHttpWarning": "Coller nécessite HTTPS. Utilisez Ctrl+Maj+V ou servir Termix via HTTPS.", - "unknownError": "Une erreur inconnue s'est produite", - "websocketError": "Erreur de connexion WebSocket", - "connecting": "Connexion en cours...", - "noHostSelected": "Aucun hôte sélectionné", - "reconnecting": "Reconnexion en cours... ({{attempt}}/{{max}})", - "reconnected": "Reconnexion réussie", - "tmuxSessionCreated": "session tmux créée : {{name}}", - "tmuxSessionAttached": "Session tmux attachée : {{name}}", - "tmuxUnavailable": "tmux n'est pas installé sur l'hôte distant, passant à l'interpréteur de commandes standard", - "tmuxSessionPickerTitle": "Sessions tmux", - "tmuxSessionPickerDesc": "Sessions tmux existantes trouvées sur cet hôte. Sélectionnez-en une pour réattacher ou créer une nouvelle session.", - "tmuxWindows": "Fenêtres", - "tmuxWindowCount": "Fenêtre {{count}}", - "tmuxAttached": "Clients attachés", - "tmuxAttachedCount": "{{count}} attaché", - "tmuxLastActivity": "Dernière activité", - "tmuxTimeJustNow": "à l'instant", - "tmuxTimeMinutes": "{{count}}il y a m", - "tmuxTimeHours": "{{count}}il y a h", - "tmuxTimeDays": "{{count}}il y a j jours", - "tmuxCreateNew": "Démarrer une nouvelle session", - "tmuxCopyHint": "Ajuster la sélection et appuyer sur Entrée pour copier dans le presse-papiers", - "tmuxDetach": "Détacher de la session tmux", - "tmuxDetached": "Détaché de la session tmux", - "maxReconnectAttemptsReached": "Nombre maximum de tentatives de reconnexion atteint", - "closeTab": "Fermer", - "connectionTimeout": "Délai de connexion dépassé", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Exécution {{command}} - {{host}}", - "totpRequired": "Authentification à deux facteurs requise", - "totpCodeLabel": "Code de vérification", - "totpVerify": "Vérifier", - "warpgateAuthRequired": "Authentification de Warpgate requise", - "warpgateSecurityKey": "Clé de sécurité", - "warpgateAuthUrl": "URL d'authentification", - "warpgateOpenBrowser": "Ouvrir dans le navigateur", - "warpgateContinue": "J'ai terminé l'authentification", - "opksshAuthRequired": "Authentification OPKSSH requise", - "opksshAuthDescription": "Authentification complète dans votre navigateur pour continuer. Cette session restera valide pendant 24 heures.", - "opksshOpenBrowser": "Ouvrez le navigateur pour vous authentifier", - "opksshWaitingForAuth": "En attente d'authentification dans le navigateur...", - "opksshAuthenticating": "Traitement de l'authentification...", - "opksshTimeout": "Délai d'authentification dépassé. Veuillez réessayer.", - "opksshAuthFailed": "L'authentification a échoué. Veuillez vérifier vos identifiants et réessayer.", - "opksshSignInWith": "Connectez-vous avec {{provider}}", - "sudoPasswordPopupTitle": "Insérer un mot de passe ?", - "websocketAbnormalClose": "Connexion fermée de manière inattendue. Cela peut être dû à un problème de proxy inversé ou de configuration SSL. Veuillez vérifier les journaux du serveur.", - "connectionLogTitle": "Journal de connexion", - "connectionLogCopy": "Copier les logs dans le presse-papiers", - "connectionLogEmpty": "Aucun journal de connexion pour l'instant", - "connectionLogWaiting": "En attente de logs de connexion...", - "connectionLogCopied": "Journaux de connexion copiés dans le presse-papiers", - "connectionLogCopyFailed": "Impossible de copier les logs dans le presse-papiers", - "connectionRejected": "Connexion refusée par le serveur. Veuillez vérifier votre authentification et la configuration du réseau.", - "hostKeyRejected": "La vérification de la clé d'hôte SSH a été rejetée. La connexion a été annulée.", - "sessionTakenOver": "La session a été ouverte dans un autre onglet. Reconnexion..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Aucun hôte sélectionné", - "initializingEditor": "Initialisation de l'éditeur...", - "file": "Fichier", - "folder": "Répertoire", - "uploadFile": "Charger un fichier", - "downloadFile": "Télécharger", - "extractArchive": "Extraire les archives", - "extractingArchive": "Extraction de {{name}}...", - "archiveExtractedSuccessfully": "{{name}} extrait avec succès", - "extractFailed": "L'extraction a échoué", - "compressFile": "Compresser le fichier", - "compressFiles": "Compresser les fichiers", - "compressFilesDesc": "Compresser les éléments {{count}} dans une archive", - "archiveName": "Nom de l'archive", - "enterArchiveName": "Entrez le nom de l'archive...", - "compressionFormat": "Format de compression", - "selectedFiles": "Fichiers sélectionnés", - "andMoreFiles": "et {{count}} de plus...", - "compress": "Compresser", - "compressingFiles": "Compression des éléments {{count}} en {{name}}...", - "filesCompressedSuccessfully": "{{name}} créé avec succès", - "compressFailed": "La compression a échoué", - "edit": "Editer", - "preview": "Aperçu", - "previous": "Précédent", - "next": "Suivant", - "pageXOfY": "Page {{current}} de {{total}}", - "zoomOut": "Zoom arrière", - "zoomIn": "Zoom avant", - "newFile": "Nouveau fichier", - "newFolder": "Nouveau dossier", - "rename": "Renommer", - "uploading": "Téléversement...", - "uploadingFile": "Envoi de {{name}}...", - "fileName": "Nom du fichier", - "folderName": "Nom du dossier", - "fileUploadedSuccessfully": "Le fichier «{{name}}» a été téléchargé avec succès", - "failedToUploadFile": "Impossible de télécharger le fichier", - "fileDownloadedSuccessfully": "Le fichier «{{name}}» a été téléchargé avec succès", - "failedToDownloadFile": "Impossible de télécharger le fichier", - "fileCreatedSuccessfully": "Le fichier \"{{name}}\" a été créé avec succès", - "folderCreatedSuccessfully": "Le dossier \"{{name}}\" a été créé avec succès", - "failedToCreateItem": "Impossible de créer l'élément", - "operationFailed": "L'opération {{operation}} a échoué pour {{name}}: {{error}}", - "failedToResolveSymlink": "Impossible de résoudre le lien symbolique", - "itemsDeletedSuccessfully": "Les éléments {{count}} ont été supprimés avec succès", - "failedToDeleteItems": "Impossible de supprimer les éléments", - "sudoPasswordRequired": "Mot de passe de l'administrateur requis", - "enterSudoPassword": "Entrez le mot de passe sudo pour continuer cette opération", - "sudoPassword": "Mot de passe Sudo", - "sudoOperationFailed": "L'opération Sudo a échoué", - "sudoAuthFailed": "L'authentification de Sudo a échoué", - "dragFilesToUpload": "Déposez les fichiers ici pour les télécharger", - "emptyFolder": "Ce dossier est vide", - "searchFiles": "Rechercher des fichiers...", - "upload": "Charger", - "selectHostToStart": "Sélectionnez un hôte pour démarrer la gestion des fichiers", - "sshRequiredForFileManager": "Le gestionnaire de fichiers nécessite SSH. Cet hôte n'a pas de SSH activé.", - "failedToConnect": "Impossible de se connecter à SSH", - "failedToLoadDirectory": "Impossible de charger le répertoire", - "noSSHConnection": "Aucune connexion SSH disponible", - "copy": "Copie", - "cut": "Couper", - "paste": "Coller", - "copyPath": "Copier le chemin", - "copyPaths": "Copier les chemins", - "delete": "Supprimez", - "properties": "Propriétés", - "refresh": "Rafraîchir", - "downloadFiles": "Télécharger les fichiers {{count}} vers le navigateur", - "copyFiles": "Copier les éléments {{count}}", - "cutFiles": "Couper les éléments {{count}}", - "deleteFiles": "Supprimer les éléments {{count}}", - "filesCopiedToClipboard": "Les éléments {{count}} ont été copiés dans le presse-papiers", - "filesCutToClipboard": "{{count}} éléments découpés dans le presse-papiers", - "pathCopiedToClipboard": "Chemin copié dans le presse-papiers", - "pathsCopiedToClipboard": "Chemins {{count}} copiés dans le presse-papiers", - "failedToCopyPath": "Impossible de copier le chemin vers le presse-papiers", - "movedItems": "Éléments {{count}} déplacés", - "failedToDeleteItem": "Échec de la suppression de l'élément", - "itemRenamedSuccessfully": "{{type}} renommé avec succès", - "failedToRenameItem": "Impossible de renommer l'élément", - "download": "Télécharger", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", "permissions": "Permissions", - "size": "Taille", - "modified": "Modifié", - "path": "Chemin d'accès", - "confirmDelete": "Êtes-vous sûr de vouloir supprimer {{name}}?", - "permissionDenied": "Autorisation refusée", - "serverError": "Erreur serveur", - "fileSavedSuccessfully": "Fichier enregistré avec succès", - "failedToSaveFile": "Échec de l'enregistrement du fichier", - "confirmDeleteSingleItem": "Êtes-vous sûr de vouloir supprimer définitivement \"{{name}} \" ?", - "confirmDeleteMultipleItems": "Êtes-vous sûr de vouloir supprimer définitivement les éléments {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Êtes-vous sûr de vouloir supprimer définitivement les éléments {{count}} ? Cela inclut les dossiers et leur contenu.", - "confirmDeleteFolder": "Êtes-vous sûr de vouloir supprimer définitivement le dossier \"{{name}}\" et tout son contenu ?", - "permanentDeleteWarning": "Cette action ne peut pas être annulée. Le(s) élément(s) seront définitivement supprimés du serveur.", - "recent": "Récentes", - "pinned": "Épinglé", - "folderShortcuts": "Raccourcis du dossier", - "failedToReconnectSSH": "Échec de la reconnexion de la session SSH", - "openTerminalHere": "Ouvrir le Terminal ici", - "run": "Exécuter", - "openTerminalInFolder": "Ouvrir le terminal dans ce dossier", - "openTerminalInFileLocation": "Ouvrir le terminal à l'emplacement du fichier", - "runningFile": "En cours d'exécution - {{file}}", - "onlyRunExecutableFiles": "Peut uniquement exécuter des fichiers exécutables", - "directories": "Répertoires", - "removedFromRecentFiles": "Suppression de «{{name}}» des fichiers récents", - "removeFailed": "La suppression a échoué", - "unpinnedSuccessfully": "Déépinglé avec succès \"{{name}}\"", - "unpinFailed": "Échec de la dépinglage", - "removedShortcut": "Raccourci \"{{name}} \" supprimé", - "removeShortcutFailed": "La suppression du raccourci a échoué", - "clearedAllRecentFiles": "Tous les fichiers récents ont été effacés", - "clearFailed": "Échec de l'effacement", - "removeFromRecentFiles": "Supprimer des fichiers récents", - "clearAllRecentFiles": "Effacer tous les fichiers récents", - "unpinFile": "Désépingler fichier", - "removeShortcut": "Supprimer le raccourci", - "pinFile": "Épingler le fichier", - "addToShortcuts": "Ajouter aux raccourcis", - "pasteFailed": "Échec du collage", - "noUndoableActions": "Aucune action annulable", - "undoCopySuccess": "Opération de copie annulée : fichiers copiés {{count}} supprimés", - "undoCopyFailedDelete": "Échec de l'annulation : impossible de supprimer les fichiers copiés", - "undoCopyFailedNoInfo": "Échec de l'annulation : impossible de trouver les informations sur le fichier copié", - "undoMoveSuccess": "Opération de déplacement annulée : les fichiers {{count}} ont été déplacés vers l'emplacement d'origine", - "undoMoveFailedMove": "Échec de l'annulation : impossible de déplacer les fichiers en arrière", - "undoMoveFailedNoInfo": "Échec de l'annulation : impossible de trouver les informations sur le fichier déplacé", - "undoDeleteNotSupported": "L'opération de suppression ne peut pas être annulée : les fichiers ont été définitivement supprimés du serveur", - "undoTypeNotSupported": "Type d'opération d'annulation non pris en charge", - "undoOperationFailed": "Échec de l'annulation de l'opération", - "unknownError": "Erreur inconnue", - "confirm": "Valider", - "find": "Rechercher...", - "replace": "Remplacer", - "downloadInstead": "Télécharger plutôt", - "keyboardShortcuts": "Raccourcis clavier", - "searchAndReplace": "Recherche & Remplacer", - "editing": "Édition en cours", - "search": "Chercher", - "findNext": "Rechercher suivant", - "findPrevious": "Trouver le Précédent", - "save": "Enregistrer", - "selectAll": "Tout sélectionner", - "undo": "Annuler", - "redo": "Refaire", - "moveLineUp": "Déplacer la ligne vers le haut", - "moveLineDown": "Déplacer la ligne vers le bas", - "toggleComment": "Basculer le commentaire", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Impossible de charger l'image", - "startTyping": "Commencez à taper...", - "unknownSize": "Taille inconnue", - "fileIsEmpty": "Le fichier est vide", - "largeFileWarning": "Avertissement de fichier volumineux", - "largeFileWarningDesc": "Ce fichier est de taille {{size}} en taille, ce qui peut causer des problèmes de performance lorsqu'il est ouvert en tant que texte.", - "fileNotFoundAndRemoved": "Le fichier «{{name}}» est introuvable et a été supprimé des fichiers récents/épinglés", - "failedToLoadFile": "Impossible de charger le fichier : {{error}}", - "serverErrorOccurred": "Une erreur de serveur s'est produite. Veuillez réessayer plus tard.", - "autoSaveFailed": "Échec de la sauvegarde automatique", - "fileAutoSaved": "Fichier enregistré automatiquement", - "moveFileFailed": "Impossible de déplacer {{name}}", - "moveOperationFailed": "Échec de l'opération de déplacement", - "canOnlyCompareFiles": "Peut seulement comparer deux fichiers", - "comparingFiles": "Comparaison des fichiers : {{file1}} et {{file2}}", - "dragFailed": "Échec de l'opération de glissement", - "filePinnedSuccessfully": "Le fichier \"{{name}}\" a été épinglé avec succès", - "pinFileFailed": "Impossible d'épingler le fichier", - "fileUnpinnedSuccessfully": "Le fichier \"{{name}}\" a été déépinglé avec succès", - "unpinFileFailed": "Échec de la déverrouillage du fichier", - "shortcutAddedSuccessfully": "Raccourci du dossier \"{{name}}\" ajouté avec succès", - "addShortcutFailed": "Impossible d'ajouter le raccourci", - "operationCompletedSuccessfully": "{{operation}} {{count}} éléments avec succès", - "operationCompleted": "Articles {{operation}} {{count}}", - "downloadFileSuccess": "Fichier {{name}} téléchargé avec succès", - "downloadFileFailed": "Échec du téléchargement", - "moveTo": "Déplacer vers {{name}}", - "diffCompareWith": "Comparaison de différences avec {{name}}", - "dragOutsideToDownload": "Faites glisser vers l'extérieur de la fenêtre pour télécharger (fichiers{{count}})", - "newFolderDefault": "Nouveau dossier", - "newFileDefault": "Nouveau fichier.txt", - "successfullyMovedItems": "Les éléments {{count}} ont été déplacés avec succès vers {{target}}", - "move": "Déplacer", - "searchInFile": "Rechercher dans le fichier (Ctrl+F)", - "showKeyboardShortcuts": "Afficher les raccourcis clavier", - "startWritingMarkdown": "Commencez à écrire votre contenu de markdown...", - "loadingFileComparison": "Chargement de la comparaison de fichiers...", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Comparer", - "sideBySide": "Côté à côte", - "inline": "En ligne", - "fileComparison": "Comparaison de fichiers : {{file1}} vs {{file2}}", - "fileTooLarge": "Fichier trop volumineux: {{error}}", - "sshConnectionFailed": "La connexion SSH a échoué. Veuillez vérifier votre connexion à {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Impossible de charger le fichier : {{error}}", - "connecting": "Connexion en cours...", - "connectedSuccessfully": "Connecté avec succès", - "totpVerificationFailed": "La vérification TOTP a échoué", - "warpgateVerificationFailed": "Échec de l'authentification de Warpgate", - "authenticationFailed": "Échec de l'authentification", - "verificationCodePrompt": "Code de vérification :", - "changePermissions": "Modifier les permissions", - "currentPermissions": "Autorisations actuelles", - "owner": "Propriétaire", - "group": "Groupes", - "others": "Autres", - "read": "Lu", - "write": "Écrire", - "execute": "Exécuter", - "permissionsChangedSuccessfully": "Permissions modifiées avec succès", - "failedToChangePermissions": "Impossible de modifier les autorisations", - "name": "Nom", - "sortByName": "Nom", - "sortByDate": "Date de modification", - "sortBySize": "Taille", - "ascending": "Ascendant", - "descending": "Descendant", - "root": "Racine", - "new": "Nouveau", - "sortBy": "Trier par", - "items": "Éléments", - "selected": "Sélectionnés", - "editor": "Editeur", - "octal": "Octales", - "storage": "Stockage", - "disk": "Disque", - "used": "Utilisé", - "of": "de", - "toggleSidebar": "Activer/désactiver la barre latérale", - "cannotLoadPdf": "Impossible de charger le PDF", - "pdfLoadError": "Une erreur s'est produite lors du chargement de ce fichier PDF.", - "loadingPdf": "Chargement du PDF...", - "loadingPage": "Chargement de la page..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copier vers l'hôte…", - "moveToHost": "Déplacer vers l'hôte…", - "copyItemsToHost": "Copiez les éléments {{count}} vers l'hôte…", - "moveItemsToHost": "Déplacer les éléments {{count}} vers l'hôte…", - "noHostsConnected": "Aucun autre hébergeur de gestionnaires de fichiers n'est disponible.", - "noHostsConnectedHint": "Ajoutez un autre hôte SSH avec le gestionnaire de fichiers activé dans le gestionnaire d'hôtes.", - "selectDestinationHost": "Sélectionnez l'hôte de destination", - "destinationPath": "Chemin de destination", - "recentDestinations": "Destinations récentes", - "collapseRecentDestinations": "Réduire les destinations récentes", - "expandRecentDestinations": "Développer les destinations récentes", - "browseFolders": "Parcourir les dossiers de destination", - "browseDestination": "Parcourez ou saisissez le chemin", - "confirmCopy": "Copie", - "confirmMove": "Se déplacer", - "transferring": "Transfert…", - "compressing": "Compression…", - "extracting": "Extraction de…", - "transferringItems": "Transfert de {{current}} d'éléments {{total}}…", - "transferSuccess": "Transfert terminé", - "transferError": "Échec du transfert", - "transferPartial": "Transfert terminé avec {{count}} erreurs", - "transferPartialHint": "Impossible de transférer : {{paths}}", - "itemsSummary": "{{count}} articles", - "destMustBeDirectory": "La destination doit être un répertoire pour les transferts multi-articles", - "selectThisFolder": "Sélectionnez ce dossier", - "browsePathWillBeCreated": "Ce dossier n'existe pas encore. Il sera créé au début du transfert.", - "browsePathError": "Impossible d'ouvrir ce chemin sur l'hôte de destination.", - "goUp": "Monter", - "copyFolderToHost": "Copier le dossier sur l'hôte…", - "moveFolderToHost": "Déplacer le dossier vers l'hôte…", - "hostReady": "Prêt", - "hostConnecting": "Connexion…", - "hostDisconnected": "Non connecté", - "hostAuthRequired": "Authentification requise — ouvrez d’abord le Gestionnaire de fichiers sur cet hôte", - "hostConnectionFailed": "Échec de la connexion", - "metricsTitle": "Horaires de transfert", - "metricsPrepare": "Préparer la destination : {{duration}}", - "metricsCompress": "Compression à la source : {{duration}}", - "metricsHopSourceRead": "Source → serveur : {{throughput}}", - "metricsHopDestSftpWrite": "Serveur → destination (SFTP) : {{throughput}}", - "metricsHopDestLocalWrite": "Serveur → destination (locale) : {{throughput}}", - "metricsTransfer": "De bout en bout : {{throughput}} ({{duration}})", - "metricsExtract": "Extraire à destination : {{duration}}", - "metricsSourceDelete": "Supprimer de la source : {{duration}}", - "metricsTotal": "Total : {{duration}}", - "progressCompressing": "Compression sur l'hôte source…", - "progressExtracting": "Extraction sur la destination…", - "progressTransferring": "Transfert de données…", - "progressReconnecting": "Reconnexion…", - "parallelSegmentsLabel": "voies de transfert parallèles", - "parallelSegmentsOption": "{{count}} voies", - "parallelSegmentsHint": "Les fichiers volumineux sont divisés en segments de 256 Mo. Plusieurs voies utilisent des connexions distinctes (comme le lancement de plusieurs transferts) pour un débit total plus élevé.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} voies)", - "progressTransferringItems": "Transfert de fichiers ({{current}} de {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} fichiers", - "sourceNotDeletedPartial": "Fichiers sources conservés (transfert partiel)", - "jumpHostLimitation": "Les deux hôtes doivent être accessibles depuis le serveur Termix. Le routage direct entre hôtes n'est pas pris en charge.", - "cancel": "Annuler", - "methodLabel": "Méthode de transfert", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "archive Tar", - "methodItemSftp": "SFTP par fichier", - "methodAutoHint": "Choisit entre l'archive tar et le transfert SFTP par fichier en fonction du nombre de fichiers, de leur taille et de leur compressibilité. Les fichiers uniques utilisent toujours le transfert SFTP en continu.", - "methodTarHint": "Compression à la source, transfert d'une archive, extraction à destination. Nécessite tar sur les deux hôtes Unix.", - "methodItemSftpHint": "Transférez chaque fichier individuellement via SFTP. Compatible avec tous les systèmes d'exploitation, y compris Windows.", - "methodPreviewLoading": "Méthode de calcul du transfert…", - "methodPreviewError": "Impossible de prévisualiser la méthode de transfert. Le serveur choisira une méthode au démarrage.", - "methodPreviewWillUseTar": "Utilisation : archive Tar", - "methodPreviewWillUseItemSftp": "Utilisation : SFTP par fichier", - "methodPreviewScanSummary": "{{fileCount}} fichiers, {{totalSize}} total (analysés sur l'hôte source).", - "methodItemSftpLimitation": "Chaque fichier utilise le même flux SFTP pour une copie individuelle, l'un après l'autre. La progression est cumulée pour tous les fichiers, ce qui explique la lenteur de la barre de progression pour les fichiers volumineux.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Vous avez choisi le protocole SFTP par fichier.", - "user_tar": "Vous avez choisi l'archive tar.", - "tar_unavailable": "Tar n'est pas disponible sur l'un ou les deux hôtes ; le protocole SFTP par fichier sera utilisé à la place.", - "windows_host": "Un système hôte Windows est impliqué — tar n'est pas utilisé.", - "auto_multi_large": "Auto : plusieurs fichiers, y compris un fichier volumineux ({{largestSize}}) avec des données compressibles — regroupent les fichiers tar en un seul transfert.", - "auto_single_large_in_archive": "Auto : un fichier volumineux ({{largestSize}}) dans cet ensemble — SFTP par fichier.", - "auto_many_incompressible": "Auto : données majoritairement incompressibles — SFTP par fichier.", - "auto_many_files": "Auto : plusieurs fichiers ({{fileCount}}) — tar réduit la surcharge par fichier.", - "auto_default": "Auto : SFTP par fichier pour cet ensemble." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Annuler", - "progressCancelling": "Annulation…", - "progressStalled": "Bloqué", - "resumedHint": "Reconnexion à un transfert actif démarré dans une autre fenêtre.", - "transferCancelled": "Transfert annulé", - "transferCancelledCopyHint": "Des fichiers partiels peuvent rester sur le site de destination.", - "transferCancelledMoveHint": "Des fichiers partiels peuvent subsister sur le site de destination. Les fichiers sources n'ont pas été supprimés.", - "cleanupDestFiles": "Destination de nettoyage", - "cleanupDestFilesSuccess": "Suppression partielle des fichiers de destination", - "cleanupDestFilesPartial": "Certains fichiers partiels n'ont pas pu être supprimés.", - "cleanupDestFilesNothing": "Rien à nettoyer à destination", - "cleanupDestFilesError": "Le nettoyage a échoué", - "retryTransfer": "Réessayer", - "retryTransferError": "Échec de la nouvelle tentative", - "transferFailedRetryHint": "Des données partielles ont été conservées à destination. La tentative de connexion reprendra dès que la connexion sera rétablie." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Pas de tunnels SSH", - "createFirstTunnelMessage": "Vous n'avez pas encore créé de tunnels SSH. Configurez les connexions de tunnels dans le Gestionnaire d'Hôtes pour commencer.", - "connected": "Connecté", - "disconnected": "Déconnecté", - "connecting": "Connexion en cours...", - "error": "Erreur", - "canceling": "Annulation...", - "connect": "Connecter", - "disconnect": "Déconnecter", - "cancel": "Abandonner", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Port local", - "remotePort": "Port distant", - "currentHostPort": "Port actuel de l'hôte", - "endpointPort": "Port de terminaison", - "bindIp": "IP locale", - "endpointSshConfig": "Configuration SSH du point d'extrémité", - "endpointSshHost": "Hôte SSH du point d'extrémité", - "endpointSshHostPlaceholder": "Sélectionnez un hôte configuré", - "endpointSshHostRequired": "Sélectionnez un serveur SSH de point de terminaison pour chaque tunnel client.", - "attempt": "Tentative {{current}} de {{max}}", - "nextRetryIn": "Prochaine tentative dans {{seconds}} secondes", - "clientTunnels": "Tunnels client", - "clientTunnel": "Tunnel client", - "addClientTunnel": "Ajouter un tunnel client", - "noClientTunnels": "Aucun tunnel client configuré sur ce bureau.", - "tunnelName": "Nom du tunnel", - "remoteHost": "Hôte distant", - "autoStart": "Démarrage automatique", - "clientAutoStartDesc": "Démarre lorsque ce client de bureau s'ouvre et reste connecté.", - "clientManualStartDesc": "Utiliser Démarrer et Arrêter de cette ligne. Termix ne l'ouvrira pas automatiquement.", - "clientRemoteServerNote": "La redirection à distance peut nécessiter AllowTcpForwarding et GatewayPorts sur le serveur SSH de point d'extrémité. Le port distant se ferme lorsque ce bureau se déconnecte.", - "clientTunnelStarted": "Tunnel client démarré", - "clientTunnelStopped": "Tunnel client arrêté", - "tunnelTestSucceeded": "Test du tunnel réussi", - "tunnelTestFailed": "Le test du tunnel a échoué", - "localSaved": "Tunnel client sauvegardé", - "localSaveError": "Impossible d'enregistrer les tunnels du client local", - "invalidBindIp": "L'adresse IP locale doit être une adresse IPv4 valide.", - "invalidLocalTargetIp": "L'IP locale cible doit être une adresse IPv4 valide.", - "invalidLocalPort": "Le port local doit être compris entre 1 et 65535.", - "invalidRemotePort": "Le port distant doit être compris entre 1 et 65535.", - "invalidLocalTargetPort": "Le port local cible doit être compris entre 1 et 65535.", - "invalidEndpointPort": "Le port du point d'entrée doit être compris entre 1 et 65535.", - "duplicateAutoStartBind": "Un seul tunnel client de démarrage automatique peut utiliser {{bind}}.", - "manualControlError": "Impossible de mettre à jour l'état du tunnel.", - "active": "Actif", - "start": "Début", - "stop": "Arrêter", - "test": "Tester", - "type": "Type de tunnel", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Télécommande (-R)", - "typeDynamic": "Dynamique (-D)", - "typeServerLocalDesc": "Hôte actuel vers point de terminaison.", - "typeServerRemoteDesc": "Point de fin de retour à l'hôte actuel.", - "typeClientLocalDesc": "Ordinateur local à terminal.", - "typeClientRemoteDesc": "Point de fin de retour à l'ordinateur local.", - "typeClientDynamicDesc": "SOCKS sur ordinateur local.", - "typeDynamicDesc": "Transférer le trafic SOCKS5 vers SSH", - "forwardDescriptionServerLocal": "Hôte actuel {{sourcePort}} → point de terminaison {{endpointPort}}.", - "forwardDescriptionServerRemote": "Point de terminaison {{endpointPort}} → hôte actuel {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS sur l'hôte actuel {{sourcePort}}.", - "forwardDescriptionClientLocal": "{{sourcePort}} local → {{endpointPort}} distant.", - "forwardDescriptionClientRemote": "{{sourcePort}} → {{endpointPort}} local.", - "forwardDescriptionClientDynamic": "SOCKS sur le port local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → CHAUSSETTES via {{endpoint}}", - "autoNameClientLocal": "{{localPort}} local → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → {{localPort}}", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", - "route": "Voie :", - "lastStarted": "Dernier démarrage", - "lastTested": "Dernier test", - "lastError": "Dernière erreur", - "maxRetries": "Nombre maximum de tentatives", - "maxRetriesDescription": "Nombre maximum de tentatives de réessai.", - "retryInterval": "Intervalle de réessai (secondes)", - "retryIntervalDescription": "Temps d'attente entre les deux tentatives.", - "local": "Locale", - "remote": "Distante", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", "destination": "Destination", - "host": "Hôte", + "host": "Host", "mode": "Mode", - "noHostSelected": "Aucun hôte sélectionné", - "working": "Traitement..." + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "UC", - "memory": "Mémoire", - "disk": "Disque", - "network": "Réseau", - "uptime": "Délai de disponibilité", - "processes": "Processus", - "available": "Disponible", - "free": "Gratuit", - "connecting": "Connexion en cours...", - "connectionFailed": "Échec de la connexion au serveur", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "Noyau {{count}}", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Usage du CPU", - "memoryUsage": "Utilisation de la mémoire", - "diskUsage": "Utilisation du disque", - "failedToFetchHostConfig": "Impossible de récupérer la configuration de l'hôte", - "serverOffline": "Serveur hors ligne", - "cannotFetchMetrics": "Impossible de récupérer les métriques depuis le serveur hors ligne", - "totpFailed": "La vérification TOTP a échoué", - "noneAuthNotSupported": "Les statistiques du serveur ne supportent pas le type d'authentification 'none'.", - "load": "Charger", - "systemInfo": "Informations du système", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Système d'exploitation", - "kernel": "Noyau", - "seconds": "secondes", - "networkInterfaces": "Interfaces réseau", - "noInterfacesFound": "Aucune interface réseau trouvée", - "noProcessesFound": "Aucun processus trouvé", - "loginStats": "Statistiques de connexion SSH", - "noRecentLoginData": "Aucune donnée de connexion récente", - "executingQuickAction": "Exécution de {{name}}...", - "quickActionSuccess": "{{name}} terminé avec succès", - "quickActionFailed": "{{name}} a échoué", - "quickActionError": "Impossible d'exécuter {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Ports d'écoute", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adresses", - "process": "Processus", - "noData": "Aucune donnée de port d'écoute" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Pare-feu", - "inactive": "Inactif", - "policy": "Politique de confidentialité", - "rules": "règles", - "noData": "Aucune donnée de pare-feu disponible", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", "action": "Action", "protocol": "Proto", "port": "Port", "source": "Source", - "anywhere": "Partout" + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Charge Moyenne", - "swap": "Permuter", + "loadAvg": "Load Avg", + "swap": "Swap", "architecture": "Architecture", - "refresh": "Rafraîchir", - "retry": "Réessayer" + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Gestion SSH et de bureau à distance auto-hébergée", - "loginTitle": "Se connecter à Termix", - "registerTitle": "Créer un compte", - "forgotPassword": "Mot de passe oublié ?", - "rememberMe": "Se souvenir de l'appareil pendant 30 jours (comprend TOTP)", - "noAccount": "Vous n'avez pas de compte ?", - "hasAccount": "Vous avez déjà un compte ?", - "twoFactorAuth": "Authentification à deux facteurs", - "enterCode": "Entrez le code de vérification", - "backupCode": "Ou utilisez le code de sauvegarde", - "verifyCode": "Vérifier le code", - "redirectingToApp": "Redirection vers l'application...", - "sshAuthenticationRequired": "Authentification SSH requise", - "sshNoKeyboardInteractive": "Authentification interactive du clavier indisponible", - "sshAuthenticationFailed": "Échec de l'authentification", - "sshAuthenticationTimeout": "Délai d'authentification dépassé", - "sshNoKeyboardInteractiveDescription": "Le serveur ne supporte pas l'authentification interactive. Veuillez fournir votre mot de passe ou votre clé SSH.", - "sshAuthFailedDescription": "Les identifiants fournis sont incorrects. Veuillez réessayer avec des identifiants valides.", - "sshTimeoutDescription": "La tentative d'authentification a expiré. Veuillez réessayer.", - "sshProvideCredentialsDescription": "Veuillez fournir vos identifiants SSH pour vous connecter à ce serveur.", - "sshPasswordDescription": "Entrez le mot de passe de cette connexion SSH.", - "sshKeyPasswordDescription": "Si votre clé SSH est chiffrée, entrez le mot de passe ici.", - "passphraseRequired": "Phrase de passe requise", - "passphraseRequiredDescription": "La clé SSH est chiffrée. Veuillez entrer le mot de passe pour la déverrouiller.", - "back": "Précédent", - "firstUser": "Premier utilisateur", - "firstUserMessage": "Vous êtes le premier utilisateur à devenir administrateur. Vous pouvez voir les paramètres d'administration dans la liste déroulante de l'utilisateur de la barre latérale. Si vous pensez que c'est une erreur, consultez les journaux de docker ou créez un problème GitHub.", - "external": "Externe", - "loginWithExternal": "Se connecter avec un fournisseur externe", - "loginWithExternalDesc": "Connectez-vous en utilisant votre fournisseur d'identité externe configuré", - "externalNotSupportedInElectron": "L'authentification externe n'est pas encore prise en charge dans l'application Electron. Veuillez utiliser la version web pour la connexion OIDC.", - "resetPasswordButton": "Réinitialiser le mot de passe", - "sendResetCode": "Envoyer le code de réinitialisation", - "resetCodeDesc": "Entrez votre nom d'utilisateur pour recevoir un code de réinitialisation de mot de passe. Le code sera enregistré dans les journaux du conteneur docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Vérifier le code", - "enterResetCode": "Entrez le code à 6 chiffres du journal du conteneur docker pour l'utilisateur :", - "newPassword": "Nouveau mot de passe", - "confirmNewPassword": "Confirmer le mot de passe", - "enterNewPassword": "Entrez votre nouveau mot de passe pour l'utilisateur :", - "signUp": "S'inscrire", - "desktopApp": "Application de bureau", - "loggingInToDesktopApp": "Connexion à l'application de bureau", - "loadingServer": "Chargement du serveur...", - "dataLossWarning": "La réinitialisation de votre mot de passe supprimera ainsi tous vos hôtes, identifiants et autres données chiffrées SSH enregistrées. Cette action ne peut pas être annulée. Utilisez cette action uniquement si vous avez oublié votre mot de passe et que vous n'êtes pas connecté.", - "authenticationDisabled": "Authentification désactivée", - "authenticationDisabledDesc": "Toutes les méthodes d'authentification sont actuellement désactivées. Veuillez contacter votre administrateur.", - "attemptsRemaining": "{{count}} tentatives restantes" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Vérifier la clé d'hôte SSH", - "keyChangedWarning": "Clé d'hôte SSH modifiée", - "firstConnectionTitle": "Première connexion à cet hôte", - "firstConnectionDescription": "L'authenticité de cet hôte ne peut pas être établie. Vérifiez que l'empreinte digitale correspond à ce que vous attendez.", - "keyChangedDescription": "La clé hôte de ce serveur a changé depuis votre dernière connexion. Cela pourrait indiquer un problème de sécurité.", - "previousKey": "Clé précédente", - "newFingerprint": "Nouvelle empreinte digitale", - "fingerprint": "Empreinte digitale", - "verifyInstructions": "Si vous faites confiance à cet hôte, cliquez sur Accepter pour continuer et enregistrer cette empreinte digitale pour de futures connexions.", - "securityWarning": "Avertissement de sécurité", - "acceptAndContinue": "Accepter et continuer", - "acceptNewKey": "Accepter la nouvelle clé et continuer" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Impossible de se connecter à la base de données", - "unknownError": "Erreur inconnue", - "loginFailed": "Échec de la connexion", - "failedPasswordReset": "Échec de la réinitialisation du mot de passe", - "failedVerifyCode": "Impossible de vérifier le code de réinitialisation", - "failedCompleteReset": "Échec de la réinitialisation du mot de passe", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Impossible de démarrer la connexion OIDC", - "silentSigninOidcUnavailable": "La connexion silencieuse a été demandée, mais la connexion OIDC n'est pas disponible.", - "failedUserInfo": "Impossible d'obtenir les informations de l'utilisateur après la connexion", - "oidcAuthFailed": "Échec de l'authentification OIDC", - "invalidAuthUrl": "URL d'autorisation invalide reçue du backend", - "requiredField": "Ce champ est obligatoire", - "minLength": "La longueur minimale est {{min}}", - "passwordMismatch": "Les mots de passe ne correspondent pas", - "passwordLoginDisabled": "Le nom d'utilisateur / mot de passe est actuellement désactivé", - "sessionExpired": "La session a expiré - veuillez vous reconnecter", - "totpRateLimited": "Taux limité : Trop de tentatives de vérification TOTP. Veuillez réessayer plus tard.", - "totpRateLimitedWithTime": "Taux limité : Trop de tentatives de vérification TOTP. Veuillez attendre {{time}} secondes avant de réessayer.", - "resetCodeRateLimited": "Taux limité : trop de tentatives de vérification. Veuillez réessayer plus tard.", - "resetCodeRateLimitedWithTime": "Taux limité : trop de tentatives de vérification. Veuillez attendre {{time}} secondes avant de réessayer.", - "authTokenSaveFailed": "Échec de l'enregistrement du jeton d'authentification", - "failedToLoadServer": "Impossible de charger le serveur" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "L'enregistrement d'un nouveau compte est actuellement désactivé par un administrateur. Veuillez vous connecter ou contacter un administrateur.", - "userNotAllowed": "Votre compte n'est pas autorisé à vous inscrire. Veuillez contacter un administrateur.", - "databaseConnectionFailed": "Impossible de se connecter au serveur de base de données", - "resetCodeSent": "Réinitialiser le code envoyé aux logs Docker", - "codeVerified": "Code vérifié avec succès", - "passwordResetSuccess": "Mot de passe réinitialisé avec succès", - "loginSuccess": "Connexion réussie", - "registrationSuccess": "Inscription réussie" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Tunels locaux de bureau ciblant les hôtes SSH configurés.", - "c2sTunnelPresets": "Préréglages du tunnel client", - "c2sTunnelPresetsDesc": "Enregistrez la liste des tunnels locaux de ce client de bureau sous la forme d'un préréglage nommé, ou rechargez un préréglage dans ce client.", - "c2sTunnelPresetsUnavailable": "Les préréglages du tunnel client ne sont disponibles que dans le client de bureau.", - "c2sPresetName": "Nom du préréglage", - "c2sPresetNamePlaceholder": "Nom du préréglage client", - "c2sPresetToLoad": "Préréglage à charger", - "c2sNoPresetSelected": "Aucun préréglage sélectionné", - "c2sNoPresets": "Aucun préréglage enregistré", - "c2sLoadPreset": "Charger", - "c2sCurrentLocalConfig": "{{count}} tunnel(s) client local(aux) configuré(s) sur ce bureau.", - "c2sPresetSyncNote": "Les préréglages sont des instantanés explicites ; le chargement remplace la liste des tunnels clients locaux du client de bureau.", - "c2sPresetSaved": "Préréglage du tunnel client sauvegardé", - "c2sPresetLoaded": "Préréglage du tunnel client chargé localement", - "c2sPresetRenamed": "Préréglage du tunnel client renommé", - "c2sPresetDeleted": "Préréglage du tunnel client supprimé", - "c2sPresetLoadError": "Impossible de charger les préréglages du tunnel client" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Langue", - "keyPassword": "mot de passe de la clé", - "pastePrivateKey": "Collez votre clé privée ici...", - "localListenerHost": "127.0.0.1 (écouter localement)", - "localTargetHost": "127.0.0.1 (cible sur cet ordinateur)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Entrez votre mot de passe", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Tableau de bord", - "loading": "Chargement du tableau de bord...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Soutien", - "discord": "Discord.", - "serverOverview": "Vue d'ensemble du serveur", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", "version": "Version", - "upToDate": "À jour", - "updateAvailable": "Mise à jour disponible", - "beta": "Bêta", - "uptime": "Délai de disponibilité", - "database": "Base de données", - "healthy": "Sain", - "error": "Erreur", - "totalHosts": "Nombre total d'hôtes", - "totalTunnels": "Tunnels totaux", - "totalCredentials": "Identifiants totaux", - "recentActivity": "Activité récente", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Chargement de l'activité récente...", - "noRecentActivity": "Aucune activité récente", - "quickActions": "Actions rapides", - "addHost": "Ajouter un hôte", - "addCredential": "Ajouter un mot de passe", - "adminSettings": "Paramètres de l'administrateur", - "userProfile": "Profil de l'utilisateur", - "serverStats": "Statistiques du serveur", - "loadingServerStats": "Chargement des statistiques du serveur...", - "noServerData": "Aucune donnée de serveur disponible", - "cpu": "UC", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personnaliser le tableau de bord", - "dashboardSettings": "Paramètres du tableau de bord", - "enableDisableCards": "Activer/Désactiver les cartes", - "resetLayout": "Revenir à la valeur par défaut", - "serverOverviewCard": "Vue d'ensemble du serveur", - "recentActivityCard": "Activité récente", - "networkGraphCard": "Graphique réseau", - "networkGraph": "Graphique réseau", - "quickActionsCard": "Actions rapides", - "serverStatsCard": "Statistiques du serveur", - "panelMain": "Principal", - "panelSide": "Côté", - "justNow": "à l'instant" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { "stable": "STABLE", - "hostsOnline": "Hôtes en ligne", - "activeTunnels": "Tunnels actifs", - "registerNewServer": "Enregistrer un nouveau serveur", - "storeSshKeysOrPasswords": "Stocker les clés SSH ou les mots de passe", - "manageUsersAndRoles": "Gérer les utilisateurs et les rôles", - "manageYourAccount": "Gérer votre compte", - "hostStatus": "Statut de l'Hôte", - "noHostsConfigured": "Aucun hôte configuré", - "online": "LIGNE", - "offline": "Hors ligne", - "onlineLower": "En ligne", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Ajouter :", - "commandPalette": "Palette de commandes", - "done": "Fait", - "editModeInstructions": "Glissez les cartes pour réorganiser · Faites glisser le diviseur de colonnes pour redimensionner les colonnes · Faites glisser le bord inférieur d'une carte pour redimensionner sa hauteur · Corbeille pour supprimer", - "empty": "Vide", - "clear": "Nettoyer" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Ajouter un hôte", - "addGroup": "Ajouter un groupe", - "addLink": "Ajouter un lien", - "zoomIn": "Zoom avant", - "zoomOut": "Zoom arrière", - "resetView": "Réinitialiser la vue", - "selectHost": "Sélectionner l'hôte", - "chooseHost": "Choisissez un hôte...", - "parentGroup": "Groupe parent", - "noGroup": "Aucun groupe", - "groupName": "Nom du groupe", - "color": "Couleur", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", "source": "Source", "target": "Target", - "moveToGroup": "Déplacer vers le groupe", - "selectGroup": "Sélectionner un groupe...", - "addConnection": "Ajouter une connexion", - "hostDetails": "Détails de l'hôte", - "removeFromGroup": "Retirer du groupe", - "addHostHere": "Ajouter un hôte ici", - "editGroup": "Modifier le groupe", - "delete": "Supprimez", - "add": "Ajouter", - "create": "Créer", - "move": "Déplacer", - "connect": "Connecter", - "createGroup": "Créer un groupe", - "selectSourcePlaceholder": "Sélectionnez la source...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Fichier invalide", - "hostAlreadyExists": "L'hôte est déjà dans la topologie", - "connectionExists": "La connexion existe déjà", - "unknown": "Inconnu", - "name": "Nom", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Statut", - "failedToAddNode": "Impossible d'ajouter le noeud", - "sourceDifferentFromTarget": "La source et la cible doivent être différentes", - "exportJSON": "Exporter JSON", - "importJSON": "Importer JSON", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Gestionnaire de fichiers", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Statistiques du serveur", - "noNodes": "Pas encore de nœuds" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker n'est pas activé pour cet hôte", - "validating": "Validation de Docker...", - "connecting": "Connexion en cours...", - "error": "Erreur", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Impossible de se connecter à Docker", - "containerStarted": "Le conteneur {{name}} a commencé", - "failedToStartContainer": "Impossible de démarrer le conteneur {{name}}", - "containerStopped": "Conteneur {{name}} arrêté", - "failedToStopContainer": "Impossible d’arrêter le conteneur {{name}}", - "containerRestarted": "Le conteneur {{name}} a redémarré", - "failedToRestartContainer": "Impossible de redémarrer le conteneur {{name}}", - "containerPaused": "Conteneur {{name}} suspendu", - "containerUnpaused": "Le conteneur {{name}} a été rétabli", - "failedToTogglePauseContainer": "Impossible d'activer/désactiver l'état de pause du conteneur {{name}}", - "containerRemoved": "Conteneur {{name}} supprimé", - "failedToRemoveContainer": "Impossible de supprimer le conteneur {{name}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", "image": "Image", "ports": "Ports", - "noPorts": "Aucun port", - "start": "Début", - "confirmRemoveContainer": "Êtes-vous sûr de vouloir supprimer le conteneur '{{name}}' ? Cette action ne peut pas être annulée.", - "runningContainerWarning": "Avertissement: Ce conteneur est en cours d'exécution. Le retirer arrêtera le conteneur en premier.", - "loadingContainers": "Chargement des conteneurs...", - "manager": "Gestionnaire de Docker", - "autoRefresh": "Rafraîchissement automatique", - "timestamps": "Horodatage", - "lines": "Lignes", - "filterLogs": "Filtrer les journaux...", - "refresh": "Rafraîchir", - "download": "Télécharger", - "clear": "Nettoyer", - "logsDownloaded": "Logs téléchargés avec succès", - "last50": "Les 50 derniers", - "last100": "100 derniers", - "last500": "500 derniers", - "last1000": "1000 Derniers", - "allLogs": "Tous les logs", - "noLogsMatching": "Aucun journal correspondant à \"{{query}}\"", - "noLogsAvailable": "Aucun journal disponible", - "noContainersFound": "Aucun conteneur trouvé", - "noContainersFoundHint": "Aucun conteneur Docker n'est disponible sur cet hôte", - "searchPlaceholder": "Rechercher dans les conteneurs...", - "allStatuses": "Tous les statuts", - "stateRunning": "En cours d'exécution", - "statePaused": "En pause", - "stateExited": "Sortie", - "stateRestarting": "Redémarrage en cours", - "noContainersMatchFilters": "Aucun conteneur ne correspond à vos filtres", - "noContainersMatchFiltersHint": "Essayez d'ajuster vos critères de recherche ou de filtre", - "failedToFetchStats": "Impossible de récupérer les statistiques du conteneur", - "containerNotRunning": "Conteneur non en cours d'exécution", - "startContainerToViewStats": "Lancer le conteneur pour afficher les statistiques", - "loadingStats": "Chargement des statistiques...", - "errorLoadingStats": "Erreur lors du chargement des statistiques", - "noStatsAvailable": "Aucune statistique disponible", - "cpuUsage": "Usage du CPU", - "current": "Actuel", - "memoryUsage": "Utilisation de la mémoire", - "networkIo": "E/S réseau", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Sortie", - "blockIo": "Bloquer E/S", - "read": "Lu", - "write": "Écrire", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "Informations sur le conteneur", - "name": "Nom", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "État", - "containerMustBeRunning": "Le conteneur doit être en cours d'exécution pour accéder à la console", - "verificationCodePrompt": "Entrez le code de vérification", - "totpVerificationFailed": "La vérification TOTP a échoué. Veuillez réessayer.", - "warpgateVerificationFailed": "Échec de l'authentification de Warpgate. Veuillez réessayer.", - "connectedTo": "Connecté à {{containerName}}", - "disconnected": "Déconnecté", - "consoleError": "Erreur de la console", - "errorMessage": "Erreur: {{message}}", - "failedToConnect": "Impossible de se connecter au conteneur", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", "console": "Console", - "selectShell": "Sélectionner une coquille", - "bash": "Frappe", - "sh": "shu", - "ash": "cendres", - "connect": "Connecter", - "disconnect": "Déconnecter", - "notConnected": "Non connecté", - "clickToConnect": "Cliquez sur Se connecter pour démarrer une session shell", - "connectingTo": "Connexion à {{containerName}}...", - "containerNotFound": "Conteneur introuvable", - "backToList": "Retour à la liste", - "logs": "Journaux", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Démarrez le conteneur pour accéder à la console" + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Généraux", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Utilisateurs", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", "sectionSessions": "Sessions", - "sectionRoles": "Rôles", - "sectionDatabase": "Base de données", - "sectionApiKeys": "Clés API", - "allowRegistration": "Autoriser l'enregistrement de l'utilisateur", - "allowRegistrationDesc": "Laisser les nouveaux utilisateurs s'inscrire", - "allowPasswordLogin": "Autoriser la connexion par mot de passe", - "allowPasswordLoginDesc": "Nom d'utilisateur/mot de passe", - "oidcAutoProvision": "Proposition automatique OIDC", - "oidcAutoProvisionDesc": "Création automatique de comptes pour les utilisateurs OIDC même lorsque l'inscription est désactivée", - "allowPasswordReset": "Autoriser la réinitialisation du mot de passe", - "allowPasswordResetDesc": "Réinitialiser le code via les logs Docker", - "sessionTimeout": "Délai de session dépassé", - "hours": "heures", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Surveillance par défaut", - "statusCheck": "Vérification de l'état", - "metrics": "Métriques", - "sec": "s", - "logLevel": "Niveau du journal", - "enableGuacamole": "Activer Guacamole", - "enableGuacamoleDesc": "Bureau distant RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Configurer OpenID Connect pour SSO. Les champs marqués * sont obligatoires.", - "oidcClientId": "ID du client", - "oidcClientSecret": "Secret du client", - "oidcAuthUrl": "URL d'autorisation", - "oidcIssuerUrl": "URL de l'émetteur", - "oidcTokenUrl": "URL du jeton", - "oidcUserIdentifier": "Chemin d'accès de l'utilisateur", - "oidcDisplayName": "Afficher le chemin du nom", - "oidcScopes": "Portées", - "oidcUserinfoUrl": "Remplacer l'URL Userinfo", - "oidcAllowedUsers": "Utilisateurs autorisés", - "oidcAllowedUsersDesc": "Un email par ligne. Laissez vide pour autoriser tous.", - "removeOidc": "Retirer", - "usersCount": "Utilisateurs {{count}}", - "createUser": "Créer", - "newRole": "Nouveau rôle", - "roleName": "Nom", - "roleDisplayName": "Nom affiché", - "roleDescription": "Libellé", - "rolesCount": "Rôles {{count}}", - "createRole": "Créer", - "creating": "Création en cours...", - "exportDatabase": "Exporter la base de données", - "exportDatabaseDesc": "Télécharger une sauvegarde de tous les hôtes, identifiants et paramètres", - "export": "Exportation", - "exporting": "Exportation en cours...", - "importDatabase": "Importer la base de données", - "importDatabaseDesc": "Restaurer à partir d'un fichier de sauvegarde .sqlite", - "importDatabaseSelected": "Sélectionné: {{name}}", - "selectFile": "Sélectionner un fichier", - "changeFile": "Changement", - "import": "Importation", - "importing": "Importation en cours...", - "apiKeysCount": "Clés {{count}}", - "newApiKey": "Nouvelle clé API", - "apiKeyCreatedWarning": "Clé créée - copiez-la maintenant, elle ne sera plus affichée.", - "apiKeyName": "Nom", - "apiKeyUser": "Utilisateur", - "apiKeySelectUser": "Sélectionnez un utilisateur...", - "apiKeyExpiresAt": "Expire à", - "createKey": "Créer une clé", - "apiKeyNoExpiry": "Pas d'expiration", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Double Auth", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Locale", - "adminStatusAdministrator": "Administrateur", - "adminStatusRegularUser": "Utilisateur normal", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "PERSONNALISÉ", - "youBadge": "TU", - "sessionsActive": "{{count}} actif", - "sessionActive": "Actif : {{time}}", - "sessionExpires": "Exp : {{time}}", - "revokeAll": "Tous", - "revokeAllSessionsSuccess": "Toutes les sessions pour l'utilisateur révoquées", - "revokeAllSessionsFailed": "Impossible de révoquer les sessions", - "revokeSessionFailed": "Échec de la révocation de la session", - "addRole": "Ajouter un rôle", - "noCustomRoles": "Aucun rôle personnalisé défini", - "removeRoleFailed": "Impossible de supprimer le rôle", - "assignRoleFailed": "Échec de l'attribution du rôle", - "deleteRoleFailed": "Impossible de supprimer le rôle", - "userAdminAccess": "Administrateur", - "userAdminAccessDesc": "Accès complet à tous les paramètres d'administration", - "userRoles": "Rôles", - "revokeAllUserSessions": "Révoquer toutes les sessions", - "revokeAllUserSessionsDesc": "Forcer la reconnexion sur tous les appareils", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "La suppression de cet utilisateur est permanente.", - "deleteUser": "Supprimer {{username}}", - "deleting": "Suppression en cours...", - "deleteUserFailed": "Échec de la suppression de l'utilisateur", - "deleteUserSuccess": "Utilisateur \"{{username}}\" supprimé", - "deleteRoleSuccess": "Rôle \"{{name}}\" supprimé", - "revokeKeySuccess": "Clé \"{{name}}\" révoquée", - "revokeKeyFailed": "Impossible de révoquer la clé", - "copiedToClipboard": "Copié dans le presse-papiers", - "done": "Fait", - "createUserTitle": "Créer un utilisateur", - "createUserDesc": "Créer un nouveau compte local.", - "createUserUsername": "Nom d'utilisateur", - "createUserPassword": "Mot de passe", - "createUserPasswordHint": "6 caractères minimum.", - "createUserEnterUsername": "Entrez le nom d'utilisateur", - "createUserEnterPassword": "Entrez le mot de passe", - "createUserSubmit": "Créer un utilisateur", - "editUserTitle": "Gérer l'utilisateur : {{username}}", - "editUserDesc": "Modifier les rôles, le statut d'administrateur, les sessions et les paramètres du compte.", - "editUserUsername": "Nom d'utilisateur", - "editUserAuthType": "Type d'authentification", - "editUserAdminStatus": "Statut de l'administrateur", - "editUserUserId": "Identifiant de l'utilisateur", - "linkAccountTitle": "Lier OIDC au compte de mot de passe", - "linkAccountDesc": "Fusionner le compte OIDC {{username}} avec un compte local existant.", - "linkAccountWarningTitle": "Cela vaudra :", - "linkAccountEffect1": "Supprimer le compte OIDC uniquement", - "linkAccountEffect2": "Ajouter une connexion OIDC au compte cible", - "linkAccountEffect3": "Autoriser la connexion OIDC et mot de passe", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Entrez le nom d'utilisateur du compte local à lier à", - "linkAccounts": "Lier les comptes", - "linkAccountSuccess": "Compte OIDC lié à \"{{username}}\"", - "linkAccountFailed": "Échec de la liaison du compte OIDC", - "linkAccountInProgress": "Enchaînement...", - "saving": "Sauvegarde en cours...", - "updateRegistrationFailed": "Échec de la mise à jour des paramètres d'enregistrement", - "updatePasswordLoginFailed": "Échec de la mise à jour des paramètres de connexion du mot de passe", - "updateOidcAutoProvisionFailed": "Impossible de mettre à jour les paramètres de la fourniture automatique OIDC", - "updatePasswordResetFailed": "Échec de la mise à jour des paramètres de réinitialisation du mot de passe", - "sessionTimeoutRange2": "Le délai d'attente de la session doit être compris entre 1 et 720 heures", - "sessionTimeoutSaved": "Délai de session enregistré", - "sessionTimeoutSaveFailed": "Impossible d'enregistrer le délai d'attente de la session", - "monitoringIntervalInvalid": "Valeurs d'intervalle non valides", - "monitoringSaved": "Paramètres de surveillance enregistrés", - "monitoringSaveFailed": "Échec de l'enregistrement des paramètres de surveillance", - "guacamoleSaved": "Paramètres Guacamole enregistrés", - "guacamoleSaveFailed": "Impossible d'enregistrer les paramètres de Guacamole", - "guacamoleUpdateFailed": "Impossible de mettre à jour le paramètre Guacamole", - "logLevelUpdateFailed": "Impossible de mettre à jour le niveau du journal", - "oidcSaved": "Configuration OIDC enregistrée", - "oidcSaveFailed": "Échec de l'enregistrement de la configuration OIDC", - "oidcRemoved": "Configuration OIDC supprimée", - "oidcRemoveFailed": "Impossible de supprimer la configuration OIDC", - "createUserRequired": "Le nom d'utilisateur et le mot de passe sont requis", - "createUserPasswordTooShort": "Le mot de passe doit comporter au moins 6 caractères", - "createUserSuccess": "L'utilisateur \"{{username}}\" créé", - "createUserFailed": "Impossible de créer l'utilisateur", - "updateAdminStatusFailed": "Impossible de mettre à jour le statut d'administrateur", - "allSessionsRevoked": "Toutes les sessions révoquées", - "revokeSessionsFailed": "Impossible de révoquer les sessions", - "createRoleRequired": "Le nom et le nom d'affichage sont requis", - "createRoleSuccess": "Rôle \"{{name}}\" créé", - "createRoleFailed": "Impossible de créer le rôle", - "apiKeyNameRequired": "Le nom de la clé est requis", - "apiKeyUserRequired": "L'identifiant de l'utilisateur est requis", - "apiKeyCreatedSuccess": "Clé API \"{{name}}\" créée", - "apiKeyCreateFailed": "Impossible de créer la clé API", - "exportSuccess": "Base de données exportée avec succès", - "exportFailed": "L'exportation de la base de données a échoué", - "importSelectFile": "Veuillez d'abord sélectionner un fichier", - "importCompleted": "Importation terminée : {{total}} éléments importés, {{skipped}} ignorée", - "importFailed": "Échec de l'import : {{error}}", - "importError": "Échec de l'importation de la base de données" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Hôte", - "hostPlaceholder": "192.168.1.1 ou exemple.com", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nom d'utilisateur", - "usernamePlaceholder": "nom d'utilisateur", - "authLabel": "Auteur", - "passwordLabel": "Mot de passe", - "passwordPlaceholder": "mot de passe", - "privateKeyLabel": "Clé privée", - "privateKeyPlaceholder": "Coller la clé privée...", - "credentialLabel": "Identification", - "credentialPlaceholder": "Sélectionnez un identifiant enregistré", - "connectToTerminal": "Se connecter au terminal", - "connectToFiles": "Se connecter aux fichiers" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Aucun terminal sélectionné", - "noTerminalSelectedHint": "Ouvrir un onglet de terminal SSH pour afficher l'historique de ses commandes", - "searchPlaceholder": "Rechercher dans l'historique...", - "clearAll": "Tout effacer", - "noHistoryEntries": "Aucune entrée d'historique", - "trackingDisabled": "Le suivi de l'historique est désactivé", - "trackingDisabledHint": "Activez-le dans les paramètres de votre profil pour enregistrer des commandes." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Enregistrement de la clé", - "recordToTerminals": "Enregistrer dans les terminaux", - "selectAll": "Tous", - "selectNone": "Aucun", - "noTerminalTabsOpen": "Aucun onglet de terminal ouvert", - "selectTerminalsAbove": "Sélectionnez les terminaux ci-dessus", - "broadcastInputPlaceholder": "Tapez ici pour diffuser les touches...", - "stopRecording": "Arrêter l'enregistrement", - "startRecording": "Démarrer l'enregistrement", - "settingsTitle": "Réglages", - "enableRightClickCopyPaste": "Activer le clic droit copier/coller" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Mise en page", - "selectLayoutAbove": "Sélectionnez une mise en page ci-dessus", - "selectLayoutHint": "Choisir le nombre de panneaux à afficher", - "panesTitle": "Panneaux", - "openTabsTitle": "Onglets ouverts", - "dragTabsHint": "Faites glisser les onglets dans les volets ci-dessus ou utilisez l'attribution rapide.", - "dropHere": "Déposer ici", - "emptyPane": "Vide", - "dashboard": "Tableau de bord", - "clearSplitScreen": "Nettoyer l'écran partagé", - "quickAssign": "Affectation rapide", - "alreadyAssigned": "Volet {{index}}", - "splitTab": "Onglet fractionné", - "addToSplit": "Ajouter au fractionnement", - "removeFromSplit": "Retirer de la division", - "assignToPane": "Affecter au volet" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Créer un snippet", - "createSnippetDescription": "Créer un nouveau snippet de commande pour une exécution rapide", - "nameLabel": "Nom", - "namePlaceholder": "ex: Redémarrer Nginx", - "descriptionLabel": "Libellé", - "descriptionPlaceholder": "Description facultative", - "optional": "Optionnel", - "folderLabel": "Répertoire", - "noFolder": "Aucun dossier (non catégorisé)", - "commandLabel": "Commandes", - "commandPlaceholder": "ex: sudo systemctl redémarrage nginx", - "cancel": "Abandonner", - "createSnippetButton": "Créer un snippet", - "createFolderTitle": "Créer un dossier", - "createFolderDescription": "Organiser vos modules de texte dans des dossiers", - "folderNameLabel": "Nom du dossier", - "folderNamePlaceholder": "ex: Commandes système, Scripts Docker", - "folderColorLabel": "Couleur du dossier", - "folderIconLabel": "Icône du dossier", - "previewLabel": "Aperçu", - "folderNameFallback": "Nom du dossier", - "createFolderButton": "Créer un dossier", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Tous", - "selectNone": "Aucun", - "noTerminalTabsOpen": "Aucun onglet de terminal ouvert", - "searchPlaceholder": "Rechercher des snippets...", - "newSnippet": "Nouveau Snippet", - "newFolder": "Nouveau dossier", - "run": "Exécuter", - "noSnippetsInFolder": "Aucun extrait dans ce dossier", - "uncategorized": "Non catégorisé", - "editSnippetTitle": "Modifier le Snippet", - "editSnippetDescription": "Mettre à jour ce snippet de commande", - "saveSnippetButton": "Enregistrer les modifications", - "createSuccess": "Snippet créé avec succès", - "createFailed": "Impossible de créer un snippet", - "updateSuccess": "Snippet mis à jour avec succès", - "updateFailed": "Échec de la mise à jour du snippet", - "deleteFailed": "Impossible de supprimer le snippet", - "folderCreateSuccess": "Dossier créé avec succès", - "folderCreateFailed": "Impossible de créer le dossier", - "editFolderTitle": "Modifier le dossier", - "editFolderDescription": "Renommer ou modifier l'apparence de ce dossier", - "saveFolderButton": "Enregistrer les modifications", - "editFolder": "Modifier le dossier", - "deleteFolder": "Supprimer le dossier", - "folderDeleteSuccess": "Le dossier \"{{name}}\" a été supprimé", - "folderDeleteFailed": "Échec de la suppression du dossier", - "folderEditSuccess": "Dossier mis à jour avec succès", - "folderEditFailed": "Échec de la mise à jour du dossier", - "confirmRunMessage": "Exécutez \"{{name}}\"?", - "confirmRunButton": "Courir", - "runSuccess": "Exécuter \"{{name}}\" dans un terminal {{count}}", - "copySuccess": "Copié «{{name}}» dans le presse-papiers", - "shareTitle": "Partager le Snippet", - "shareUser": "Utilisateur", - "shareRole": "Rôle", - "selectUser": "Sélectionnez un utilisateur...", - "selectRole": "Sélectionnez un rôle...", - "shareSuccess": "Snippet partagé avec succès", - "shareFailed": "Impossible de partager le snippet", - "revokeSuccess": "Accès révoqué", - "revokeFailed": "Impossible de révoquer l'accès", - "currentAccess": "Accès actuel", - "shareLoadError": "Impossible de charger les données de partage", - "loading": "Chargement en cours...", - "close": "Fermer", - "reorderFailed": "Impossible d'enregistrer l'ordre des extraits" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Compte client", - "sectionAppearance": "Apparence", - "sectionSecurity": "Sécurité", - "sectionApiKeys": "Clés API", - "sectionC2sTunnels": "Tunnels C2S", - "usernameLabel": "Nom d'utilisateur", - "roleLabel": "Rôle", - "roleAdministrator": "Administrateur", - "authMethodLabel": "Méthode d'authentification", - "authMethodLocal": "Locale", - "twoFaLabel": "A2F", - "twoFaOn": "Activé", - "twoFaOff": "Désactivé", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", "versionLabel": "Version", - "deleteAccount": "Supprimer le compte", - "deleteAccountDescription": "Supprimer définitivement votre compte", - "deleteButton": "Supprimez", - "deleteAccountPermanent": "Cette action est permanente et ne peut être annulée.", - "deleteAccountWarning": "Toutes les sessions, les hôtes, les identifiants et les paramètres seront supprimés définitivement.", - "confirmPasswordDeletePlaceholder": "Entrez votre mot de passe pour confirmer", - "languageLabel": "Langue", - "themeLabel": "Thème", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Couleur d'accentuation", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Auto-complétion de commande", - "commandAutocompleteDesc": "Afficher la saisie automatique lors de la saisie", - "historyTracking": "Suivi de l'historique", - "historyTrackingDesc": "Commandes de terminal de suivi", - "syntaxHighlighting": "Coloration syntaxique", - "syntaxHighlightingDesc": "Surligner la sortie du terminal", - "commandPalette": "Palette de commandes", - "commandPaletteDesc": "Activer le raccourci clavier", - "reopenTabsOnLogin": "Rouvrir les onglets à la connexion", - "reopenTabsOnLoginDesc": "Vos onglets ouverts seront restaurés lorsque vous vous connectez ou actualisez la page, même depuis un autre appareil.", - "confirmTabClose": "Confirmer la fermeture de l'onglet", - "confirmTabCloseDesc": "Demander avant de fermer les onglets du terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Afficher les tags de l'hôte", - "showHostTagsDesc": "Afficher les tags dans la liste des hôtes", - "hostTrayOnClick": "Cliquez pour afficher les actions de l'hôte", - "hostTrayOnClickDesc": "Afficher systématiquement les boutons de connexion ; cliquer pour afficher les options de gestion au lieu de survoler.", - "pinAppRail": "Rail d'application Pin", - "pinAppRailDesc": "Conserver le panneau latéral gauche toujours déployé au lieu de le déployer au survol.", - "settingsSnippets": "Extraits", - "foldersCollapsed": "Dossiers réduits", - "foldersCollapsedDesc": "Réduire les dossiers par défaut", - "confirmExecution": "Confirmer l'exécution", - "confirmExecutionDesc": "Confirmer avant d'exécuter des snippets", - "settingsUpdates": "Mises à jour", - "disableUpdateChecks": "Désactiver les vérifications de mise à jour", - "disableUpdateChecksDesc": "Arrêter la vérification des mises à jour", - "totpAuthenticator": "Authentificateur TOTP", - "totpEnabled": "L'authentification à deux facteurs est activée", - "totpDisabled": "Ajouter une sécurité de connexion supplémentaire", - "disable": "Désactiver", - "enable": "Activer", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scannez le code QR ou entrez le secret dans votre application d'authentification, puis saisissez le code à 6 chiffres", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Vérifier", - "changePassword": "Changer le mot de passe", - "currentPasswordLabel": "Mot de passe actuel", - "currentPasswordPlaceholder": "Mot de passe actuel", - "newPasswordLabel": "Nouveau mot de passe", - "newPasswordPlaceholder": "Nouveau mot de passe", - "confirmPasswordLabel": "Confirmer le nouveau mot de passe", - "confirmPasswordPlaceholder": "Confirmer le nouveau mot de passe", - "updatePassword": "Mettre à jour le mot de passe", - "createApiKeyTitle": "Créer une clé API", - "createApiKeyDescription": "Générer une nouvelle clé API pour l'accès programmatique.", - "apiKeyNameLabel": "Nom", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "optionnel", - "cancel": "Abandonner", - "createKey": "Créer une clé", - "apiKeyCount": "Clés {{count}}", - "newKey": "Nouvelle clé", - "noApiKeys": "Aucune clé API pour le moment.", - "apiKeyActive": "Actif", - "apiKeyUsageHint": "Inclure votre clé dans le", - "apiKeyUsageHintHeader": "en-tête.", - "apiKeyPermissionsHint": "Les clés héritent des permissions de l'utilisateur créateur.", - "roleUser": "Utilisateur", - "authMethodDual": "Double Auth", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Impossible de démarrer la configuration TOTP", - "totpEnter6Digits": "Entrez un code à 6 chiffres", - "totpEnabledSuccess": "Authentification à deux facteurs activée", - "totpInvalidCode": "Code invalide, veuillez réessayer", - "totpDisableInputRequired": "Entrez votre code TOTP ou votre mot de passe", - "totpDisabledSuccess": "Authentification à deux facteurs désactivée", - "totpDisableFailed": "Échec de la désactivation de l'A2F", - "totpDisableTitle": "Désactiver l'A2F", - "totpDisablePlaceholder": "Entrez le code TOTP ou le mot de passe", - "totpDisableConfirm": "Désactiver l'A2F", - "totpContinueVerify": "Continuer à vérifier", - "totpVerifyTitle": "Vérifier le code", - "totpBackupTitle": "Codes de sauvegarde", - "totpDownloadBackup": "Télécharger les codes de sauvegarde", - "done": "Fait", - "secretCopied": "Secret copié dans le presse-papiers", - "apiKeyNameRequired": "Le nom de la clé est requis", - "apiKeyCreated": "Clé API \"{{name}}\" créée", - "apiKeyCreateFailed": "Impossible de créer la clé API", - "apiKeyUser": "Utilisateur", - "apiKeyExpires": "Expire", - "apiKeyRevoked": "Clé API \"{{name}}\" révoquée", - "apiKeyRevokeFailed": "Impossible de révoquer la clé API", - "passwordFieldsRequired": "Les mots de passe actuels et nouveaux sont requis", - "passwordMismatch": "Les mots de passe ne correspondent pas", - "passwordTooShort": "Le mot de passe doit comporter au moins 6 caractères", - "passwordUpdated": "Mot de passe mis à jour avec succès", - "passwordUpdateFailed": "Échec de la mise à jour du mot de passe", - "deletePasswordRequired": "Le mot de passe est requis pour supprimer votre compte", - "deleteFailed": "Échec de la suppression du compte", - "deleting": "Suppression en cours...", - "colorPickerTooltip": "Ouvrir le sélecteur de couleurs", - "themeSystem": "Système", - "themeLight": "Lumière", - "themeDark": "Sombre", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarisé", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Un Noir", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/he_IL.json b/src/ui/locales/translated/he_IL.json index daceed89..9bc5af32 100644 --- a/src/ui/locales/translated/he_IL.json +++ b/src/ui/locales/translated/he_IL.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "תיקיות", - "folder": "תיקייה", - "password": "סִיסמָה", - "key": "מַפְתֵחַ", - "sshPrivateKey": "מפתח פרטי SSH", - "upload": "העלאה", - "keyPassword": "סיסמת מפתח", - "sshKey": "מפתח SSH", - "uploadPrivateKeyFile": "העלאת קובץ מפתח פרטי", - "searchCredentials": "חיפוש פרטי גישה...", - "addCredential": "הוסף אישור", - "caCertificate": "תעודת CA (-cert.pub)", - "caCertificateDescription": "אופציונלי: העלה או הדבק את קובץ האישור החתום על ידי רשות האישור (לדוגמה, id_ed25519-cert.pub). נדרש כאשר שרת ה-SSH שלך משתמש בהרשאה מבוססת אישורים.", - "uploadCertFile": "העלאת קובץ -cert.pub", - "clearCert": "בָּרוּר", - "certLoaded": "האישור נטען", - "certPublicKeyLabel": "תעודת CA", - "certTypeLabel": "סוג התעודה", - "pasteOrUploadCert": "הדבק או העלה אישור -cert.pub...", - "hasCaCert": "בעל תעודת CA", - "noCaCert": "אין תעודת CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "טעינת ההתראות נכשלה", - "failedToDismissAlert": "נכשלה סגירת ההתראה" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "תצורת שרת", - "description": "הגדר את כתובת ה-URL של שרת Termix כדי להתחבר לשירותי ה-backend שלך", - "serverUrl": "כתובת URL של השרת", - "enterServerUrl": "אנא הזן כתובת URL של שרת", - "saveFailed": "שמירת התצורה נכשלה", - "saveError": "שגיאה בשמירת התצורה", - "saving": "חִסָכוֹן...", - "saveConfig": "שמור תצורה", - "helpText": "הזן את כתובת ה-URL שבה פועל שרת ה-Termix שלך (לדוגמה, http://localhost:30001 או https://your-server.com)", - "changeServer": "שנה שרת", - "mustIncludeProtocol": "כתובת השרת חייבת להתחיל ב-http:// או https://", - "allowInvalidCertificate": "אפשר אישור לא חוקי", - "allowInvalidCertificateDesc": "השתמש רק עבור שרתים מהימנים המתארחים בעצמם עם אישורי חתימה עצמית או אישורי כתובת IP.", - "useEmbedded": "השתמש בשרת מקומי", - "embeddedDesc": "הפעל את Termix עם השרת המקומי המובנה (אין צורך בשרת מרוחק)", - "embeddedConnecting": "מתחבר לשרת מקומי...", - "embeddedNotReady": "השרת המקומי עדיין לא מוכן. אנא המתן רגע ונסה שוב.", - "localServer": "שרת מקומי" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "שגיאת בדיקת גרסה", - "checkFailed": "נכשל בבדיקת עדכונים", - "upToDate": "האפליקציה מעודכנת", - "currentVersion": "אתה משתמש בגרסה {{version}}", - "updateAvailable": "עדכון זמין", - "newVersionAvailable": "גרסה חדשה זמינה! אתה מפעיל את {{current}}, אבל {{latest}} זמין.", - "betaVersion": "גרסת בטא", - "betaVersionDesc": "אתה מפעיל את {{current}}, שהוא חדש יותר מהגרסה היציבה האחרונה {{latest}}.", - "releasedOn": "יצא לאור בתאריך {{date}}", - "downloadUpdate": "הורד עדכון", - "checking": "בודק עדכונים...", - "checkUpdates": "בדוק אם יש עדכונים", - "checkingUpdates": "בודק עדכונים...", - "updateRequired": "נדרש עדכון" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "לִסְגוֹר", - "minimize": "לְצַמְצֵם", - "online": "באינטרנט", - "offline": "לא מקוון", - "continue": "לְהַמשִׁיך", - "maintenance": "תַחזוּקָה", - "degraded": "מוּשׁפָל", - "error": "שְׁגִיאָה", - "warning": "אַזהָרָה", - "unsavedChanges": "שינויים שלא נשמרו", - "dismiss": "לְפַטֵר", - "loading": "טְעִינָה...", - "optional": "אופציונלי", - "connect": "לְחַבֵּר", - "copied": "מוּעֲתָק", - "connecting": "מְקַשֵׁר...", - "updateAvailable": "עדכון זמין", - "appName": "טרמיקס", - "openInNewTab": "פתח בכרטיסייה חדשה", - "noReleases": "אין פרסומים", - "updatesAndReleases": "עדכונים ומהדורות", - "newVersionAvailable": "גרסה חדשה ({{version}}זמינה.", - "failedToFetchUpdateInfo": "נכשל באחזור פרטי העדכון", - "preRelease": "טרום-הפצה", - "noReleasesFound": "לא נמצאו מהדורות.", - "cancel": "לְבַטֵל", - "username": "שם משתמש", - "login": "כְּנִיסָה לַמַעֲרֶכֶת", - "register": "לִרְשׁוֹם", - "password": "סִיסמָה", - "confirmPassword": "אשר סיסמה", - "back": "בְּחֲזָרָה", - "save": "לְהַצִיל", - "saving": "חִסָכוֹן...", - "delete": "לִמְחוֹק", - "rename": "שינוי שם", - "edit": "לַעֲרוֹך", - "add": "לְהוֹסִיף", - "confirm": "לְאַשֵׁר", - "no": "לֹא", - "or": "אוֹ", - "next": "הַבָּא", - "previous": "קוֹדֵם", - "refresh": "לְרַעֲנֵן", - "language": "שָׂפָה", - "checking": "בודק...", - "checkingDatabase": "בודק חיבור למסד הנתונים...", - "checkingAuthentication": "בודק אימות...", - "backendReconnected": "חיבור השרת שוחזר", - "connectionDegraded": "חיבור השרת אבד, מתאושש…", - "reload": "לִטעוֹן מִחָדָשׁ", - "remove": "לְהַסִיר", - "create": "לִיצוֹר", - "update": "לְעַדְכֵּן", - "copy": "לְהַעְתִיק", - "copyFailed": "נכשל בהעתקה ללוח", - "maximize": "לְהַגדִיל", - "restore": "לְשַׁחְזֵר", - "of": "שֶׁל" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "בַּיִת", - "terminal": "מָסוֹף", - "docker": "דוקר", - "tunnels": "מנהרות", - "fileManager": "מנהל הקבצים", - "serverStats": "סטטיסטיקות שרת", - "admin": "מנהל", - "userProfile": "פרופיל משתמש", - "splitScreen": "מסך מפוצל", - "confirmClose": "לסגור את ההפעלה הפעילה הזו?", - "close": "לִסְגוֹר", - "cancel": "לְבַטֵל", - "sshManager": "מנהל SSH", - "cannotSplitTab": "לא ניתן לפצל את הכרטיסייה הזו", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "העתקת סיסמה", - "copySudoPassword": "העתקת סיסמת סודו", - "passwordCopied": "הסיסמה הועתקה ללוח", - "noPasswordAvailable": "אין סיסמה זמינה", - "failedToCopyPassword": "העתקת הסיסמה נכשלה", - "refreshTab": "רענן את החיבור", - "openFileManager": "פתח את מנהל הקבצים", - "dashboard": "לוּחַ מַחווָנִים", - "networkGraph": "גרף הרשת", - "quickConnect": "חיבור מהיר", - "sshTools": "כלי SSH", - "history": "הִיסטוֹרִיָה", - "hosts": "מארחים", - "snippets": "קטעי טקסט", - "hostManager": "מנהל מארח", - "credentials": "אישורים", - "connections": "חיבורים", - "roleAdministrator": "מְנַהֵל", - "roleUser": "מִשׁתַמֵשׁ" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "מארחים", - "noHosts": "אין מארחי SSH", - "retry": "נסה שוב", - "refresh": "לְרַעֲנֵן", - "optional": "אופציונלי", - "downloadSample": "הורד דוגמה", - "failedToDeleteHost": "נכשלה המחיקה של {{name}}", - "importSkipExisting": "ייבוא (דלג על קיים)", - "connectionDetails": "פרטי חיבור", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", - "telnet": "טלנט", - "remoteDesktop": "שולחן עבודה מרוחק", - "port": "נָמָל", - "username": "שם משתמש", - "folder": "תיקייה", - "tags": "תגיות", - "pin": "פִּין", - "addHost": "הוסף מארח", - "editHost": "עריכת מארח", - "cloneHost": "מארח משוכפל", - "enableTerminal": "הפעלת טרמינל", - "enableTunnel": "הפעלת מנהרה", - "enableFileManager": "הפעל את מנהל הקבצים", - "enableDocker": "הפעל את Docker", - "defaultPath": "נתיב ברירת מחדל", - "connection": "קֶשֶׁר", - "upload": "העלאה", - "authentication": "אימות", - "password": "סִיסמָה", - "key": "מַפְתֵחַ", - "credential": "תְעוּדָה", - "none": "אַף לֹא אֶחָד", - "sshPrivateKey": "מפתח פרטי SSH", - "keyType": "סוג מפתח", - "uploadFile": "העלאת קובץ", - "tabGeneral": "כְּלָלִי", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "מנהרות", - "tabDocker": "דוקר", - "tabFiles": "קבצים", - "tabStats": "סטטיסטיקות", - "tabTelnet": "טלנט", - "tabSharing": "שיתוף", - "tabAuthentication": "אימות", - "terminal": "מָסוֹף", - "tunnel": "מִנהָרָה", - "fileManager": "מנהל הקבצים", - "serverStats": "סטטיסטיקות שרת", - "status": "סטָטוּס", - "folderRenamed": "תיקיית \"{{oldName}}\" שמה שונה בהצלחה ל- \"{{newName}}\"", - "failedToRenameFolder": "נכשל שינוי שם התיקייה", - "movedToFolder": "הועבר אל \"{{folder}}\"", - "editHostTooltip": "עריכת מארח", - "statusChecks": "בדיקות סטטוס", - "metricsCollection": "אוסף מדדים", - "metricsInterval": "מרווח איסוף מדדים", - "metricsIntervalDesc": "באיזו תדירות לאסוף סטטיסטיקות שרת (5 שניות - שעה)", - "behavior": "הִתְנַהֲגוּת", - "themePreview": "תצוגה מקדימה של ערכת נושא", - "theme": "נוֹשֵׂא", - "fontFamily": "משפחת גופנים", - "fontSize": "גודל גופן", - "letterSpacing": "ריווח אותיות", - "lineHeight": "גובה הקו", - "cursorStyle": "סגנון הסמן", - "cursorBlink": "מצמוץ סמן", - "scrollbackBuffer": "מאגר גלילה לאחור", - "bellStyle": "סגנון בל", - "rightClickSelectsWord": "לחיצה ימנית בוחרת מילה", - "fastScrollModifier": "שינוי גלילה מהירה", - "fastScrollSensitivity": "רגישות גלילה מהירה", - "sshAgentForwarding": "העברת סוכן SSH", - "backspaceMode": "מצב Backspace", - "startupSnippet": "קטע הפעלה", - "selectSnippet": "בחר קטע", - "forceKeyboardInteractive": "כפיית מקלדת אינטראקטיבית", - "overrideCredentialUsername": "עקיפת שם משתמש של פרטי כניסה", - "overrideCredentialUsernameDesc": "השתמש בשם המשתמש שצוין למעלה במקום שם המשתמש של פרטי האישור", - "jumpHostChain": "שרשרת מארח קפיצה", - "portKnocking": "פורט נוקינג", - "addKnock": "הוסף יציאה", - "addProxyNode": "הוסף צומת", - "proxyNode": "צומת פרוקסי", - "proxyType": "סוג פרוקסי", - "quickActions": "פעולות מהירות", - "sudoPasswordAutoFill": "מילוי אוטומטי של סיסמאות Sudo", - "sudoPassword": "סיסמת סודו", - "keepaliveInterval": "מרווח זמן שמירה (מילישניות)", - "moshCommand": "פיקוד MOSH", - "environmentVariables": "משתני סביבה", - "addVariable": "הוסף משתנה", - "docker": "דוקר", - "copyTerminalUrl": "העתקת כתובת URL של הטרמינל", - "copyFileManagerUrl": "העתקת כתובת URL של מנהל הקבצים", - "copyRemoteDesktopUrl": "העתקת כתובת URL של שולחן עבודה מרוחק", - "failedToConnect": "נכשלה ההתחברות לקונסולה", - "connect": "לְחַבֵּר", - "disconnect": "לְנַתֵק", - "start": "הַתחָלָה", - "enableStatusCheck": "הפעל בדיקת סטטוס", - "enableMetrics": "הפעל מדדים", - "bulkUpdateFailed": "עדכון בכמות גדולה נכשל", - "selectAll": "בחר הכל", - "deselectAll": "בטל את הבחירה של הכל", - "protocols": "פרוטוקולים", - "secureShell": "מעטפת מאובטחת", - "virtualNetwork": "רשת וירטואלית", - "unencryptedShell": "מעטפת לא מוצפנת", - "addressIp": "כתובת / IP", - "friendlyName": "שם ידידותי", - "folderAndAdvanced": "תיקיות ומתקדם", - "privateNotes": "הערות פרטיות", - "privateNotesPlaceholder": "פרטים על השרת הזה...", - "pinToTop": "הצמד לראש הדף", - "pinToTopDesc": "הצג תמיד מארח זה בראש הרשימה", - "portKnockingSequence": "רצף נוקיות פורט", - "addKnockBtn": "הוסף דפיקה", - "noPortKnocking": "לא הוגדרה הגדרת \"קישור לפורט\".", - "knockPort": "נוק פורט", - "protocol": "פּרוֹטוֹקוֹל", - "delayAfterMs": "עיכוב לאחר (מילישניות)", - "useSocks5Proxy": "השתמש בפרוקסי SOCKS5", - "useSocks5ProxyDesc": "ניתוב חיבור דרך שרת פרוקסי", - "proxyHost": "מארח פרוקסי", - "proxyPort": "יציאת פרוקסי", - "proxyUsername": "שם משתמש פרוקסי", - "proxyPassword": "סיסמת פרוקסי", - "proxySingleMode": "פרוקסי יחיד", - "proxyChainMode": "שרשרת פרוקסי", - "you": "אַתָה", - "jumpHostChainLabel": "שרשרת מארח קפיצה", - "addJumpBtn": "הוסף קפיצה", - "noJumpHosts": "לא הוגדרו מארחי קפיצה.", - "selectAServer": "בחר שרת...", - "sshPort": "פורט SSH", - "authMethod": "שיטת אימות", - "storedCredential": "אישורים מאוחסנים", - "selectACredential": "בחר אישור...", - "keyTypeLabel": "סוג מפתח", - "keyTypeAuto": "זיהוי אוטומטי", - "keyPasteTab": "לְהַדבִּיק", - "keyUploadTab": "העלאה", - "keyFileLoaded": "קובץ המפתח נטען", - "keyUploadClick": "לחץ כדי להעלות קובץ .pem / .key / .ppk", - "clearKey": "מקש נקה", - "keySaved": "מפתח SSH נשמר", - "keyReplaceNotice": "הדבק מפתח חדש למטה כדי להחליף אותו", - "keyPassphraseSaved": "סיסמת הסיסמה נשמרה, הקלד כדי לשנות", - "replaceKey": "החלפת מפתח", - "forceKeyboardInteractiveLabel": "כוח מקלדת אינטראקטיבי", - "forceKeyboardInteractiveShortDesc": "כפיית הזנת סיסמה ידנית גם אם קיימים מפתחות", - "terminalAppearance": "מראה הטרמינל", - "colorTheme": "ערכת צבעים", - "fontFamilyLabel": "משפחת גופנים", - "fontSizeLabel": "גודל גופן", - "cursorStyleLabel": "סגנון הסמן", - "letterSpacingPx": "ריווח בין אותיות (פיקסלים)", - "lineHeightLabel": "גובה הקו", - "bellStyleLabel": "סגנון בל", - "backspaceModeLabel": "מצב Backspace", - "cursorBlinking": "סמן מהבהב", - "cursorBlinkingDesc": "הפעל אנימציה מהבהבת עבור סמן הטרמינל", - "rightClickSelectsWordLabel": "לחיצה ימנית בוחרת מילה", - "rightClickSelectsWordShortDesc": "בחר את המילה שמתחת לסמן בלחיצה ימנית", - "behaviorAndAdvanced": "התנהגות ומתקדם", - "scrollbackBufferLabel": "מאגר גלילה לאחור", - "scrollbackMaxLines": "מספר השורות המרבי שנשמר בהיסטוריה", - "sshAgentForwardingLabel": "העברת סוכן SSH", - "sshAgentForwardingShortDesc": "העבירו את מפתחות ה-SSH המקומיים שלכם למארח הזה", - "enableAutoMosh": "הפעלת מודעות אוטומטית", - "enableAutoMoshDesc": "העדיפו Mosh על פני SSH אם זמין", - "enableAutoTmux": "הפעל את Auto-Tmux", - "enableAutoTmuxDesc": "הפעל או צורף אוטומטית לסשן tmux", - "sudoPasswordAutoFillLabel": "מילוי אוטומטי של סיסמת סודו", - "sudoPasswordAutoFillShortDesc": "ספק סיסמת sudo באופן אוטומטי כאשר תתבקש", - "sudoPasswordLabel": "סיסמת סודו", - "environmentVariablesLabel": "משתני סביבה", - "addVariableBtn": "הוסף משתנה", - "noEnvVars": "לא הוגדרו משתני סביבה.", - "fastScrollModifierLabel": "שינוי גלילה מהירה", - "fastScrollSensitivityLabel": "רגישות גלילה מהירה", - "moshCommandLabel": "פיקוד מוש", - "startupSnippetLabel": "קטע הפעלה", - "keepaliveIntervalLabel": "מרווח זמן שמירה (שניות)", - "maxKeepaliveMisses": "מקס קיפלייב מחמיץ", - "tunnelSettings": "הגדרות מנהרה", - "enableTunneling": "הפעלת מנהור", - "enableTunnelingDesc": "הפעל פונקציונליות מנהרת SSH עבור מארח זה", - "serverTunnelsSection": "מנהרות שרתים", - "addTunnelBtn": "הוסף מנהרה", - "noTunnelsConfigured": "לא הוגדרו מנהרות.", - "tunnelLabel": "מנהרה {{number}}", - "tunnelType": "סוג מנהרה", - "tunnelModeLocalDesc": "העברת פורט מקומי לפורט בשרת מרוחק (או למארח שניתן להגיע אליו ממנו).", - "tunnelModeRemoteDesc": "העבר פורט בשרת המרוחק חזרה לפורט מקומי במחשב שלך.", - "tunnelModeDynamicDesc": "צור פרוקסי SOCKS5 בפורט מקומי עבור העברת פורטים דינמית.", - "sameHost": "מארח זה (מנהרה ישירה)", - "endpointHost": "מארח נקודת קצה", - "endpointPort": "יציאת נקודת קצה", - "bindHost": "מארח קשירה", - "sourcePort": "יציאת מקור", - "maxRetries": "מקסימום ניסיונות חוזרים", - "retryIntervalS": "מרווח זמן של ניסיון חוזר (ים)", - "autoStartLabel": "הפעלה אוטומטית", - "autoStartDesc": "חבר אוטומטית את המנהרה הזו כאשר המארח נטען", - "tunnelConnecting": "מנהרה מתחברת...", - "tunnelDisconnected": "מנהרה מנותקת", - "failedToConnectTunnel": "נכשל בחיבור", - "failedToDisconnectTunnel": "נכשל הניתוק", - "dockerIntegration": "אינטגרציה עם Docker", - "enableDockerMonitor": "הפעל את Docker", - "enableDockerMonitorDesc": "ניטור וניהול של מכולות במארח זה באמצעות Docker", - "enableFileManagerMonitor": "הפעל את מנהל הקבצים", - "enableFileManagerMonitorDesc": "עיון וניהול קבצים במארח זה דרך SFTP", - "defaultPathLabel": "נתיב ברירת מחדל", - "fileManagerPathHint": "הספרייה שתיפתח כאשר מנהל הקבצים יופעל עבור מארח זה.", - "statusChecksLabel": "בדיקות סטטוס", - "enableStatusChecks": "הפעל בדיקות סטטוס", - "enableStatusChecksDesc": "בצע פינג מעת לעת למארח הזה כדי לוודא זמינות", - "useGlobalInterval": "השתמש במרווח גלובלי", - "useGlobalIntervalDesc": "עקיפה עם מרווח בדיקת המצב הכולל של השרת", - "checkIntervalS": "מרווח זמן בדיקה (ים)", - "checkIntervalDesc": "שניות בין כל פינג קישוריות", - "metricsCollectionLabel": "אוסף מדדים", - "enableMetricsLabel": "הפעל מדדים", - "enableMetricsDesc": "איסוף נתוני שימוש במעבד, זיכרון RAM, דיסק ורשת ממארח זה", - "useGlobalMetrics": "השתמש במרווח גלובלי", - "useGlobalMetricsDesc": "עקיפה עם מרווח המדדים הכולל של השרת", - "metricsIntervalS": "מרווח מדדים (ים)", - "metricsIntervalDesc2": "שניות בין תמונות מצב של מדדים", - "visibleWidgets": "ווידג'טים גלויים", - "cpuUsageLabel": "שימוש במעבד", - "cpuUsageDesc": "אחוז המעבד, ממוצעי עומס, גרף תרשים ניצוץ", - "memoryLabel": "שימוש בזיכרון", - "memoryDesc": "שימוש ב-RAM, החלפה, אחסון במטמון", - "storageLabel": "שימוש בדיסק", - "storageDesc": "שימוש בדיסק לכל נקודת הרכבה", - "networkLabel": "ממשקי רשת", - "networkDesc": "רשימת ממשקים ורוחב פס", - "uptimeLabel": "זמן פעולה", - "uptimeDesc": "זמן פעולה וזמן אתחול של המערכת", - "systemInfoLabel": "מידע מערכת", - "systemInfoDesc": "מערכת הפעלה, ליבה, שם מארח, ארכיטקטורה", - "recentLoginsLabel": "כניסות אחרונות", - "recentLoginsDesc": "אירועי התחברות מוצלחים ונכשלים", - "topProcessesLabel": "תהליכים מובילים", - "topProcessesDesc": "PID, CPU%, MEM%, פקודה", - "listeningPortsLabel": "יציאות האזנה", - "listeningPortsDesc": "פתיחת פורטים עם תהליך ומצב", - "firewallLabel": "חומת אש", - "firewallDesc": "חומת אש, AppArmor, סטטוס SELinux", - "quickActionsLabel": "פעולות מהירות", - "quickActionsToolbar": "פעולות מהירות מופיעות ככפתורים בסרגל הכלים של סטטיסטיקות השרת לביצוע פקודה בלחיצה אחת.", - "noQuickActions": "אין עדיין פעולות מהירות.", - "buttonLabel": "תווית כפתור", - "selectSnippetPlaceholder": "בחר קטע...", - "addActionBtn": "הוסף פעולה", - "hostSharedSuccessfully": "המארח שותף בהצלחה", - "failedToShareHost": "נכשל בשיתוף המארח", - "accessRevoked": "הגישה בוטלה", - "failedToRevokeAccess": "ביטול הגישה נכשל", - "cancelBtn": "לְבַטֵל", - "savingBtn": "חִסָכוֹן...", - "addHostBtn": "הוסף מארח", - "hostUpdated": "המארח עודכן", - "hostCreated": "מארח נוצר", - "failedToSave": "שמירת המארח נכשלה", - "credentialUpdated": "אישור עודכן", - "credentialCreated": "נוצר אישור", - "failedToSaveCredential": "שמירת האישורים נכשלה", - "backToHosts": "חזרה למארחים", - "backToCredentials": "חזרה לאישורים", - "pinned": "מוצמד", - "noHostsFound": "לא נמצאו מארחים", - "tryDifferentTerm": "נסה מונח אחר", - "addFirstHost": "הוסף את המארח הראשון שלך כדי להתחיל", - "noCredentialsFound": "לא נמצאו אישורים", - "addCredentialBtn": "הוסף אישור", - "updateCredentialBtn": "עדכון אישורים", - "features": "תכונות", - "noFolder": "(אין תיקייה)", - "deleteSelected": "לִמְחוֹק", - "exitSelection": "יציאה מבחירה", - "importSkip": "ייבוא (דלג על קיים)", - "importOverwrite": "ייבוא (החלפה)", - "collapseBtn": "הִתמוֹטְטוּת", - "importExportBtn": "ייבוא / ייצוא", - "hostStatusesRefreshed": "סטטוסים של מארחים רעננו", - "failedToRefreshHosts": "רענון המארחים נכשל", - "movedHostTo": "הועבר {{host}} ל-\"{{folder}}\"", - "failedToMoveHost": "העברת המארח נכשלה", - "folderRenamedTo": "שם התיקייה הוא \"{{name}}\"", - "deletedFolder": "תיקייה שנמחקה \"{{name}}\"", - "failedToDeleteFolder": "מחיקת התיקייה נכשלה", - "deleteAllInFolder": "למחוק את כל המארחים ב-\"{{name}}\"? לא ניתן לבטל פעולה זו.", - "deletedHost": "נמחק {{name}}", - "copiedToClipboard": "הועתק ללוח", - "terminalUrlCopied": "כתובת ה-URL של הטרמינל הועתקה", - "fileManagerUrlCopied": "כתובת ה-URL של מנהל הקבצים הועתקה", - "tunnelUrlCopied": "כתובת ה-URL של המנהרה הועתקה", - "dockerUrlCopied": "כתובת ה-URL של Docker הועתקה", - "serverStatsUrlCopied": "כתובת האתר של סטטיסטיקות השרת הועתקה", - "rdpUrlCopied": "כתובת ה-URL של RDP הועתקה", - "vncUrlCopied": "כתובת ה-URL של VNC הועתקה", - "telnetUrlCopied": "כתובת ה-URL של Telnet הועתקה", - "remoteDesktopUrlCopied": "כתובת ה-URL של שולחן עבודה מרוחק הועתקה", - "expandActions": "הרחב פעולות", - "collapseActions": "פעולות כיווץ", - "wakeOnLanAction": "התעוררות ברשת מקומית (LAN)", - "wakeOnLanSuccess": "חבילת קסם נשלחה אל {{name}}", - "wakeOnLanError": "שליחת חבילת הקסם נכשלה", - "cloneHostAction": "מארח משוכפל", - "copyAddress": "העתקת כתובת", - "copyLink": "העתק קישור", - "copyTerminalUrlAction": "העתקת כתובת URL של הטרמינל", - "copyFileManagerUrlAction": "העתקת כתובת URL של מנהל הקבצים", - "copyTunnelUrlAction": "העתקת כתובת URL של המנהרה", - "copyDockerUrlAction": "העתקת כתובת URL של Docker", - "copyServerStatsUrlAction": "העתקת כתובת URL של סטטיסטיקות שרת", - "copyRdpUrlAction": "העתקת כתובת URL של RDP", - "copyVncUrlAction": "העתקת כתובת URL של VNC", - "copyTelnetUrlAction": "העתקת כתובת URL של Telnet", - "copyRemoteDesktopUrlAction": "העתקת כתובת URL של שולחן עבודה מרוחק", - "deleteCredentialConfirm": "למחוק את פרטי הכניסה \"{{name}}\"?", - "deletedCredential": "נמחק {{name}}", - "deploySSHKeyTitle": "פריסת מפתח SSH", - "deployingBtn": "פריסה...", - "deployBtn": "לִפְרוֹס", - "failedToDeployKey": "פריסת המפתח נכשלה", - "deleteHostsConfirm": "מחיקת {{count}} מארח{{plural}}? לא ניתן לבטל פעולה זו.", - "movedToRoot": "הועבר לשורש", - "failedToMoveHosts": "העברת המארחים נכשלה", - "enableTerminalFeature": "הפעלת טרמינל", - "disableTerminalFeature": "השבתת הטרמינל", - "enableFilesFeature": "הפעל קבצים", - "disableFilesFeature": "השבת קבצים", - "enableTunnelsFeature": "הפעל מנהרות", - "disableTunnelsFeature": "השבת מנהרות", - "enableDockerFeature": "הפעל את Docker", - "disableDockerFeature": "השבת את Docker", - "addTagsPlaceholder": "הוסף תגיות...", - "authDetails": "פרטי אימות", - "credType": "סוּג", - "generateKeyPairDesc": "צור זוג מפתחות חדש, המפתחות הפרטיים והציבוריים ימולאו אוטומטית.", - "generatingKey": "יוצר...", - "generateLabel": "צור {{label}}", - "uploadFileBtn": "העלאת קובץ", - "keyPassphraseOptional": "סיסמת מפתח (אופציונלי)", - "sshPublicKeyOptional": "מפתח ציבורי SSH (אופציונלי)", - "publicKeyGenerated": "מפתח ציבורי נוצר", - "failedToGeneratePublicKey": "נכשל ביצירת מפתח ציבורי", - "publicKeyCopied": "המפתח הציבורי הועתק", - "keyPairGenerated": "נוצר זוג מפתחות {{label}}", - "failedToGenerateKeyPair": "נכשל ביצירת זוג המפתחות", - "searchHostsPlaceholder": "חיפוש מארחים, כתובות, תגיות…", - "searchCredentialsPlaceholder": "פרטי חיפוש…", - "refreshBtn": "לְרַעֲנֵן", - "addTag": "הוסף תגיות...", - "deleteConfirmBtn": "לִמְחוֹק", - "tunnelRequirementsText": "שרת ה-SSH חייב להגדיר את האפשרויות GatewayPorts כן, AllowTcpForwarding כן, ו-PermitRootLogin כן ב-/etc/ssh/sshd_config.", - "deleteHostConfirm": "למחוק את \"{{name}}\"?", - "enableAtLeastOneProtocol": "הפעל לפחות פרוטוקול אחד לעיל כדי להגדיר הגדרות אימות וחיבור.", - "keyPassphrase": "סיסמת מפתח", - "connectBtn": "לְחַבֵּר", - "disconnectBtn": "לְנַתֵק", - "basicInformation": "מידע בסיסי", - "authDetailsSection": "פרטי אימות", - "credTypeLabel": "סוּג", - "hostsTab": "מארחים", - "credentialsTab": "אישורים", - "selectMultiple": "בחר מספר רב", - "selectHosts": "בחירת מארחים", - "connectionLabel": "קֶשֶׁר", - "authenticationLabel": "אימות", - "generateKeyPairTitle": "צור זוג מפתחות", - "generateKeyPairDescription": "צור זוג מפתחות חדש, המפתחות הפרטיים והציבוריים ימולאו אוטומטית.", - "generateFromPrivateKey": "יצירה ממפתח פרטי", - "refreshBtn2": "לְרַעֲנֵן", - "exitSelectionTitle": "יציאה מבחירה", - "exportAll": "ייצוא הכל", - "addHostBtn2": "הוסף מארח", - "addCredentialBtn2": "הוסף אישור", - "checkingHostStatuses": "בודק סטטוסים של מארחים...", - "pinnedSection": "מוצמד", - "hostsExported": "מארחים יוצאו בהצלחה", - "exportFailed": "ייצוא המארחים נכשל", - "sampleDownloaded": "קובץ לדוגמה שהורד", - "failedToDeleteCredential2": "מחיקת האישורים נכשלה", - "noFolderOption": "(אין תיקייה)", - "nSelected": "{{count}} נבחר", - "featuresMenu": "תכונות", - "moveMenu": "מַהֲלָך", - "cancelSelection": "לְבַטֵל", - "deployDialogDesc": "פרוס את {{name}} למפתחות authorized של מחשב מארח.", - "targetHostLabel": "מארח היעד", - "selectHostOption": "בחר מארח...", - "keyDeployedSuccess": "המפתח נפרס בהצלחה", - "failedToDeployKey2": "פריסת המפתח נכשלה", - "deletedCount": "מחיקת מארחים {{count}}", - "failedToDeleteCount": "נכשלה מחיקת המארחים {{count}}", - "duplicatedHost": "משוכפל \"{{name}}\"", - "failedToDuplicateHost": "שכפול המארח נכשל", - "updatedCount": "מארחים מעודכנים {{count}}", - "friendlyNameLabel": "שם ידידותי", - "descriptionLabel": "תֵאוּר", - "loadingHost": "טוען מארח...", - "loadingHosts": "טוען מארחים...", - "loadingCredentials": "טוען אישורים...", - "noHostsYet": "עדיין אין מארחים", - "noHostsMatchSearch": "אין מארחים התואמים את החיפוש שלך", - "hostNotFound": "המארח לא נמצא", - "searchHosts": "חיפוש מארחים...", - "sortHosts": "מיין מארחים", - "sortDefault": "סדר ברירת מחדל", - "sortNameAsc": "שם (א' → ת')", - "sortNameDesc": "שם (ת → א)", - "sortIpAsc": "כתובת IP (עולה)", - "sortIpDesc": "כתובת IP (תיאור)", - "sortOnlineFirst": "מקוון תחילה", - "sortOfflineFirst": "לא מקוון תחילה", - "sortPinnedFirst": "הוצמד ראשון", - "filterHosts": "סנן מארחים", - "filterClearAll": "נקה מסננים", - "filterStatusGroup": "סטָטוּס", - "filterOnline": "באינטרנט", - "filterOffline": "לא מקוון", - "filterPinned": "מוצמד", - "filterAuthGroup": "סוג אימות", - "filterAuthPassword": "סִיסמָה", - "filterAuthKey": "מפתח SSH", - "filterAuthCredential": "תְעוּדָה", - "filterAuthNone": "אַף לֹא אֶחָד", - "filterAuthOpkssh": "אופקש", - "filterProtocolGroup": "פּרוֹטוֹקוֹל", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "טלנט", - "filterFeaturesGroup": "תכונות", - "filterFeatureTerminal": "מָסוֹף", - "filterFeatureFileManager": "מנהל הקבצים", - "filterFeatureTunnel": "מִנהָרָה", - "filterFeatureDocker": "דוקר", - "filterTagsGroup": "תגיות", - "shareHost": "שתף מארח", - "shareHostTitle": "שתף: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "מארח זה חייב להשתמש באישור כדי לאפשר שיתוף. ערוך את המארח והקצה אישור תחילה." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "קֶשֶׁר", - "authentication": "אימות", - "connectionSettings": "הגדרות חיבור", - "displaySettings": "הגדרות תצוגה", - "audioSettings": "הגדרות שמע", - "rdpPerformance": "ביצועי RDP", - "deviceRedirection": "ניתוב מחדש של מכשירים", - "session": "מוֹשָׁב", - "gateway": "כְּנִיסָה", - "remoteApp": "אפליקציה מרחוק", - "clipboard": "לוח גזירה", - "sessionRecording": "הקלטת סשן", - "wakeOnLan": "התעוררות ברשת LAN", - "vncSettings": "הגדרות VNC", - "terminalSettings": "הגדרות מסוף", - "rdpPort": "יציאת RDP", - "username": "שם משתמש", - "password": "סִיסמָה", - "domain": "תְחוּם", - "securityMode": "מצב אבטחה", - "colorDepth": "עומק צבע", - "width": "רוֹחַב", - "height": "גוֹבַה", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "שיטת שינוי גודל", - "clientName": "שם הלקוח", - "initialProgram": "תוכנית ראשונית", - "serverLayout": "פריסת שרת", - "timezone": "אזור זמן", - "gatewayHostname": "שם מארח השער", - "gatewayPort": "שער היציאה", - "gatewayUsername": "שם משתמש של השער", - "gatewayPassword": "סיסמת השער", - "gatewayDomain": "דומיין שער", - "remoteAppProgram": "תוכנית RemoteApp", - "workingDirectory": "ספריית עבודה", - "arguments": "טיעונים", - "normalizeLineEndings": "נרמול סיומות שורות", - "recordingPath": "נתיב ההקלטה", - "recordingName": "שם ההקלטה", - "macAddress": "כתובת MAC", - "broadcastAddress": "כתובת שידור", - "udpPort": "יציאת UDP", - "waitTimeS": "זמן המתנה (שניות)", - "driveName": "שם הכונן", - "drivePath": "נתיב הנסיעה", - "ignoreCertificate": "התעלם מהתעודה", - "ignoreCertificateDesc": "אפשר חיבורים למארחים עם אישורים חתומים עצמית", - "forceLossless": "כפיית אובדן נתונים", - "forceLosslessDesc": "כפיית קידוד תמונה ללא אובדן נתונים (איכות גבוהה יותר, רוחב פס גדול יותר)", - "disableAudio": "השבתת שמע", - "disableAudioDesc": "השתקת כל האודיו מההפעלה המרוחקת", - "enableAudioInput": "הפעלת קלט שמע (מיקרופון)", - "enableAudioInputDesc": "העברת המיקרופון המקומי לסשן המרוחק", - "wallpaper": "טָפֵּט", - "wallpaperDesc": "הצגת טפט שולחן עבודה (השבתה משפרת את הביצועים)", - "theming": "ת'מינג", - "themingDesc": "הפעלת ערכות נושא וסגנונות חזותיים", - "fontSmoothing": "החלקת גופנים", - "fontSmoothingDesc": "הפעלת עיבוד גופני ClearType", - "fullWindowDrag": "גרירת חלון מלאה", - "fullWindowDragDesc": "הצגת תוכן החלון בעת גרירה", - "desktopComposition": "קומפוזיציה לשולחן העבודה", - "desktopCompositionDesc": "הפעלת אפקטים של Aero Glass", - "menuAnimations": "אנימציות תפריטים", - "menuAnimationsDesc": "הפעלת אנימציות של דהייה והחלקה בתפריט", - "disableBitmapCaching": "השבתת אחסון במטמון של מפת סיביות", - "disableBitmapCachingDesc": "כבה את מטמון הסיביות (עשוי לעזור עם תקלות)", - "disableOffscreenCaching": "השבתת אחסון במטמון מחוץ למסך", - "disableOffscreenCachingDesc": "כבה את המטמון מחוץ למסך", - "disableGlyphCaching": "השבתת אחסון גליפים במטמון", - "disableGlyphCachingDesc": "כבה את מטמון הגליפים", - "enableGfx": "הפעלת GFX", - "enableGfxDesc": "השתמש בצינור הגרפיקה של RemoteFX", - "enablePrinting": "הפעל הדפסה", - "enablePrintingDesc": "ניתוב מחדש של מדפסות מקומיות להפעלה מרוחקת", - "enableDriveRedirection": "הפעל ניתוב מחדש של כונן", - "enableDriveRedirectionDesc": "מיפוי תיקייה מקומית ככונן בהפעלה מרוחקת", - "createDrivePath": "צור נתיב כונן", - "createDrivePathDesc": "צור את התיקייה באופן אוטומטי אם היא אינה קיימת", - "disableDownload": "השבתת הורדה", - "disableDownloadDesc": "מניעת הורדת קבצים מההפעלה המרוחקת", - "disableUpload": "השבתת העלאה", - "disableUploadDesc": "מניעת העלאת קבצים לסשן מרחוק", - "enableTouch": "הפעל מגע", - "enableTouchDesc": "הפעל העברת קלט מגע", - "consoleSession": "סשן קונסולה", - "consoleSessionDesc": "התחברות לקונסולה (סשן 0) במקום סשן חדש", - "sendWolPacket": "שלח חבילת WOL", - "sendWolPacketDesc": "שלח חבילת קסם כדי להעיר את המארח הזה לפני התחברות", - "disableCopy": "השבתת העתקה", - "disableCopyDesc": "מניעת העתקת טקסט מההפעלה המרוחקת", - "disablePaste": "השבתת הדבקה", - "disablePasteDesc": "מניעת הדבקת טקסט לתוך ההפעלה המרוחקת", - "createPathIfMissing": "צור נתיב אם חסר", - "createPathIfMissingDesc": "צור אוטומטית את ספריית ההקלטה", - "excludeOutput": "אי הכללת פלט", - "excludeOutputDesc": "אל תקליטו פלט מסך (מטא-נתונים בלבד)", - "excludeMouse": "אי הכללת עכבר", - "excludeMouseDesc": "אל תתעדו תנועות עכבר", - "includeKeystrokes": "כלול הקשות מקשים", - "includeKeystrokesDesc": "הקלטת הקשות גולמיות בנוסף לפלט המסך", - "vncPort": "יציאת VNC", - "vncPassword": "סיסמת VNC", - "vncUsernameOptional": "שם משתמש (אופציונלי)", - "vncLeaveBlank": "השאר ריק אם לא נדרש", - "cursorMode": "מצב סמן", - "swapRedBlue": "החלפת אדום/כחול", - "swapRedBlueDesc": "החלפת ערוצי הצבע האדום והכחול (תיקון של כמה בעיות צבע)", - "readOnly": "לקריאה בלבד", - "readOnlyDesc": "צפה במסך השלט הרחוק מבלי לשלוח קלט כלשהו", - "telnetPort": "יציאת טלנט", - "terminalType": "סוג הטרמינל", - "fontName": "שם הגופן", - "fontSize": "גודל גופן", - "colorScheme": "ערכת צבעים", - "backspaceKey": "מקש Backspace", - "saveHostFirst": "שמור את המארח קודם.", - "sharingOptionsAfterSave": "אפשרויות שיתוף זמינות לאחר שמירת המארח.", - "sharingLoadError": "טעינת נתוני השיתוף נכשלה. בדוק את החיבור ונסה שוב.", - "shareHostSection": "שתף מארח", - "shareWithUser": "שתף עם משתמש", - "shareWithRole": "שתף עם תפקיד", - "selectUser": "בחר משתמש", - "selectRole": "בחר תפקיד", - "selectUserOption": "בחר משתמש...", - "selectRoleOption": "בחר תפקיד...", - "permissionLevel": "רמת הרשאה", - "expiresInHours": "פג תוקף בעוד (שעות)", - "noExpiryPlaceholder": "השאר ריק ללא תפוגה", - "shareBtn": "לַחֲלוֹק", - "currentAccess": "גישה נוכחית", - "typeHeader": "סוּג", - "targetHeader": "יַעַד", - "permissionHeader": "רְשׁוּת", - "grantedByHeader": "הוענק על ידי", - "expiresHeader": "פג תוקף", - "noAccessEntries": "עדיין אין רשומות גישה.", - "expiredLabel": "פג תוקף", - "neverLabel": "לְעוֹלָם לֹא", - "revokeBtn": "לְבַטֵל", - "cancelBtn": "לְבַטֵל", - "savingBtn": "חִסָכוֹן...", - "updateHostBtn": "עדכון מארח", - "addHostBtn": "הוסף מארח" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "חיפוש מארחים, פקודות או הגדרות...", - "quickActions": "פעולות מהירות", - "hostManager": "מנהל מארח", - "hostManagerDesc": "ניהול, הוספה או עריכה של מארחים", - "addNewHost": "הוסף מארח חדש", - "addNewHostDesc": "רשום מארח חדש", - "adminSettings": "הגדרות מנהל מערכת", - "adminSettingsDesc": "הגדרת העדפות מערכת ומשתמשים", - "userProfile": "פרופיל משתמש", - "userProfileDesc": "ניהול החשבון וההעדפות שלך", - "addCredential": "הוסף אישור", - "addCredentialDesc": "אחסון מפתחות או סיסמאות SSH", - "recentActivity": "פעילות אחרונה", - "serversAndHosts": "שרתים ומארחים", - "noHostsFound": "לא נמצאו מארחים התואמים ל-\"{{search}}\"", - "links": "קישורים", - "navigate": "ניווט", - "select": "לִבחוֹר", - "toggleWith": "החלף עם" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "חלונית {{index}} - ריקה", - "noTabAssigned": "לא הוקצתה לשונית", - "focusedPane": "חלונית פעילה" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "אין קשרים", - "noConnectionsDesc": "פתח מסוף, מנהל קבצים או שולחן עבודה מרוחק כדי לראות חיבורים כאן", - "connectedFor": "מחובר עבור {{duration}}", - "connected": "מְחוּבָּר", - "disconnected": "מְנוּתָק", - "closeTab": "סגור את הכרטיסייה", - "closeConnection": "קשר קרוב", - "forgetTab": "לִשְׁכּוֹחַ", - "removeBackground": "לְהַסִיר", - "reconnect": "התחבר מחדש", - "reopenTab": "פתיחה מחדש", - "sectionOpen": "לִפְתוֹחַ", - "sectionBackground": "רֶקַע", - "backgroundDesc": "החיבור נשמר למשך 30 דקות לאחר הניתוק וניתן להתחבר מחדש.", - "persisted": "נשמר ברקע", - "expiresIn": "פג תוקף בעוד {{duration}}", - "search": "חיפוש חיבורים...", - "noSearchResults": "אין קשרים התואמים את החיפוש שלך" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "מתחבר לסשן {{type}}...", - "connectionError": "שגיאת חיבור", - "connectionFailed": "החיבור נכשל", - "failedToConnect": "נכשל בקבלת אסימון החיבור", - "hostNotFound": "המארח לא נמצא", - "noHostSelected": "לא נבחר מארח", - "reconnect": "התחבר מחדש", - "retry": "נסה שוב", - "guacdUnavailable": "שירות שולחן עבודה מרוחק (guacd) אינו זמין. אנא ודא ש-guacd פועל, נגיש ומוגדר כראוי בהגדרות המנהל.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (מסך נעילה)", - "winKey": "מפתח חלונות", - "ctrl": "קונטרול", - "alt": "אלט", - "shift": "מִשׁמֶרֶת", - "win": "לְנַצֵחַ", - "stickyActive": "{{key}} (נעול - לחץ לשחרור)", - "stickyInactive": "{{key}} (לחץ כדי לנעול)", - "esc": "לִברוֹחַ", - "tab": "טאב", - "home": "בַּיִת", - "end": "סוֹף", - "pageUp": "עמוד למעלה", - "pageDown": "עמוד למטה", - "arrowUp": "חץ למעלה", - "arrowDown": "חץ למטה", - "arrowLeft": "חץ שמאלה", - "arrowRight": "חץ ימינה", - "fnToggle": "מקשי פונקציה", - "reconnect": "חיבור מחדש של הפעלת המושב", - "collapse": "כיווץ סרגל הכלים", - "expand": "הרחב את סרגל הכלים", - "dragHandle": "גרור כדי למקם מחדש" + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "התחברות למארח", - "clear": "בָּרוּר", - "paste": "לְהַדבִּיק", - "reconnect": "התחבר מחדש", - "connectionLost": "החיבור אבד", - "connected": "מְחוּבָּר", - "clipboardWriteFailed": "ההעתקה ללוח נכשלה. ודא שהדף מוגש דרך HTTPS או localhost.", - "clipboardReadFailed": "הקריאה מהלוח נכשלה. ודא שהרשאות הלוח ניתנות.", - "clipboardHttpWarning": "הדבקה דורשת HTTPS. השתמשו ב-Ctrl+Shift+V או הגשת Termix דרך HTTPS.", - "unknownError": "אירעה שגיאה לא ידועה", - "websocketError": "שגיאת חיבור WebSocket", - "connecting": "מְקַשֵׁר...", - "noHostSelected": "לא נבחר מארח", - "reconnecting": "מתחבר מחדש... ({{attempt}}/{{max}})", - "reconnected": "התחבר מחדש בהצלחה", - "tmuxSessionCreated": "נוצרה סשן tmux: {{name}}", - "tmuxSessionAttached": "סשן tmux מצורף: {{name}}", - "tmuxUnavailable": "tmux אינו מותקן על המארח המרוחק, חוזר למעטפת סטנדרטית", - "tmuxSessionPickerTitle": "מפגשי tmux", - "tmuxSessionPickerDesc": "נמצאו הפעלות tmux קיימות במארח זה. בחר אחת לחיבור מחדש או צור הפעלות חדשה.", - "tmuxWindows": "חלונות", - "tmuxWindowCount": "חלון {{count}}", - "tmuxAttached": "לקוחות מצורפים", - "tmuxAttachedCount": "{{count}} מצורף", - "tmuxLastActivity": "פעילות אחרונה", - "tmuxTimeJustNow": "זֶה עַתָה", - "tmuxTimeMinutes": "לפני {{count}}דקות", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", "tmuxTimeHours": "{{count}}h ago", "tmuxTimeDays": "{{count}}d ago", - "tmuxCreateNew": "התחל סשן חדש", - "tmuxCopyHint": "התאם את הבחירה ולחץ על Enter כדי להעתיק ללוח", - "tmuxDetach": "ניתוק מסשן tmux", - "tmuxDetached": "מנותק מסשן tmux", - "maxReconnectAttemptsReached": "הגעת למספר המקסימלי של ניסיונות חיבור מחדש", - "closeTab": "לִסְגוֹר", - "connectionTimeout": "זמן קצוב לחיבור", - "terminalTitle": "טרמינל - {{host}}", - "terminalWithPath": "טרמינל - {{host}}:{{path}}", - "runTitle": "ריצה {{command}} - {{host}}", - "totpRequired": "נדרש אימות דו-שלבי", - "totpCodeLabel": "קוד אימות", - "totpVerify": "לְאַמֵת", - "warpgateAuthRequired": "נדרש אימות Warpgate", - "warpgateSecurityKey": "מפתח אבטחה", - "warpgateAuthUrl": "כתובת URL לאימות", - "warpgateOpenBrowser": "פתח בדפדפן", - "warpgateContinue": "סיימתי את האימות", - "opksshAuthRequired": "נדרש אימות OPKSSH", - "opksshAuthDescription": "השלם את האימות בדפדפן שלך כדי להמשיך. סשן זה יישאר תקף למשך 24 שעות.", - "opksshOpenBrowser": "פתח את הדפדפן כדי לאמת", - "opksshWaitingForAuth": "ממתין לאימות בדפדפן...", - "opksshAuthenticating": "מעבד אימות...", - "opksshTimeout": "הזמן שהוקצב לאימות הסתיים. אנא נסה שוב.", - "opksshAuthFailed": "האימות נכשל. אנא בדוק את פרטי הגישה שלך ונסה שוב.", - "opksshSignInWith": "התחבר באמצעות {{provider}}", - "sudoPasswordPopupTitle": "להכניס סיסמה?", - "websocketAbnormalClose": "החיבור נסגר באופן בלתי צפוי. ייתכן שהדבר נובע מבעיית פרוקסי הפוך או מבעיית תצורת SSL. אנא בדוק את יומני השרת.", - "connectionLogTitle": "יומן חיבור", - "connectionLogCopy": "העתקת יומנים ללוח", - "connectionLogEmpty": "אין עדיין יומני חיבור", - "connectionLogWaiting": "ממתין ליומני חיבור...", - "connectionLogCopied": "יומני חיבור הועתקו ללוח", - "connectionLogCopyFailed": "נכשלה העתקת יומנים ללוח", - "connectionRejected": "החיבור נדחה על ידי השרת. אנא בדוק את האימות ותצורת הרשת שלך.", - "hostKeyRejected": "אימות מפתח מארח SSH נדחה. החיבור בוטל.", - "sessionTakenOver": "הסשן נפתח בכרטיסייה אחרת. מתחבר מחדש..." + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "לא נבחר מארח", - "initializingEditor": "מאתחל עורך...", - "file": "קוֹבֶץ", - "folder": "תיקייה", - "uploadFile": "העלאת קובץ", - "downloadFile": "הורדה", - "extractArchive": "ארכיון חילוץ", - "extractingArchive": "מחלץ {{name}}...", - "archiveExtractedSuccessfully": "{{name}} חולץ בהצלחה", - "extractFailed": "החילוץ נכשל", - "compressFile": "דחיסת קובץ", - "compressFiles": "דחיסת קבצים", - "compressFilesDesc": "דחיסת {{count}} פריטים לתוך ארכיון", - "archiveName": "שם הארכיון", - "enterArchiveName": "הזן שם ארכיון...", - "compressionFormat": "פורמט דחיסה", - "selectedFiles": "קבצים נבחרים", - "andMoreFiles": "ועוד {{count}}...", - "compress": "לִדחוֹס", - "compressingFiles": "דחיסת {{count}} פריטים לתוך {{name}}...", - "filesCompressedSuccessfully": "{{name}} נוצר בהצלחה", - "compressFailed": "הדחיסה נכשלה", - "edit": "לַעֲרוֹך", - "preview": "תצוגה מקדימה", - "previous": "קוֹדֵם", - "next": "הַבָּא", - "pageXOfY": "עמוד {{current}} מתוך {{total}}", - "zoomOut": "התקרבות", - "zoomIn": "לְהִתְמַקֵד", - "newFile": "קובץ חדש", - "newFolder": "תיקייה חדשה", - "rename": "שינוי שם", - "uploading": "מעלה...", - "uploadingFile": "מעלה {{name}}...", - "fileName": "שם הקובץ", - "folderName": "שם התיקייה", - "fileUploadedSuccessfully": "הקובץ \"{{name}}\" הועלה בהצלחה", - "failedToUploadFile": "העלאת הקובץ נכשלה", - "fileDownloadedSuccessfully": "הקובץ \"{{name}}\" הורד בהצלחה", - "failedToDownloadFile": "הורדת הקובץ נכשלה", - "fileCreatedSuccessfully": "הקובץ \"{{name}}\" נוצר בהצלחה", - "folderCreatedSuccessfully": "תיקיית \"{{name}}\" נוצרה בהצלחה", - "failedToCreateItem": "יצירת הפריט נכשלה", - "operationFailed": "הפעולה {{operation}} נכשלה עבור {{name}}: {{error}}", - "failedToResolveSymlink": "נכשל בפענוח הסימבילינק", - "itemsDeletedSuccessfully": "{{count}} פריטים נמחקו בהצלחה", - "failedToDeleteItems": "מחיקת הפריטים נכשלה", - "sudoPasswordRequired": "נדרשת סיסמת מנהל", - "enterSudoPassword": "הזן את סיסמת הסודו כדי להמשיך בפעולה זו", - "sudoPassword": "סיסמת סודו", - "sudoOperationFailed": "פעולת הסודו נכשלה", - "sudoAuthFailed": "אימות סודו נכשל", - "dragFilesToUpload": "שחררו קבצים כאן כדי להעלות", - "emptyFolder": "תיקייה זו ריקה", - "searchFiles": "חיפוש קבצים...", - "upload": "העלאה", - "selectHostToStart": "בחר מארח כדי להתחיל בניהול קבצים", - "sshRequiredForFileManager": "מנהל הקבצים דורש SSH. שרת זה אינו מופעל באמצעות SSH.", - "failedToConnect": "נכשלה ההתחברות ל-SSH", - "failedToLoadDirectory": "טעינת הספרייה נכשלה", - "noSSHConnection": "אין חיבור SSH זמין", - "copy": "לְהַעְתִיק", - "cut": "גְזִירָה", - "paste": "לְהַדבִּיק", - "copyPath": "העתק נתיב", - "copyPaths": "העתקת נתיבים", - "delete": "לִמְחוֹק", - "properties": "נכסים", - "refresh": "לְרַעֲנֵן", - "downloadFiles": "הורד {{count}} קבצים לדפדפן", - "copyFiles": "העתקת {{count}} פריטים", - "cutFiles": "גזור {{count}} פריטים", - "deleteFiles": "מחיקת {{count}} פריטים", - "filesCopiedToClipboard": "{{count}} פריטים הועתקו ללוח", - "filesCutToClipboard": "{{count}} פריטים נחתכו ללוח", - "pathCopiedToClipboard": "הנתיב הועתק ללוח", - "pathsCopiedToClipboard": "נתיבים {{count}} הועתקו ללוח", - "failedToCopyPath": "נכשלה העתקת הנתיב ללוח", - "movedItems": "הועבר {{count}} פריטים", - "failedToDeleteItem": "מחיקת הפריט נכשלה", - "itemRenamedSuccessfully": "שם השינוי של {{type}} הצליח", - "failedToRenameItem": "שינוי שם הפריט נכשל", - "download": "הורדה", - "permissions": "הרשאות", - "size": "גוֹדֶל", - "modified": "שונה", - "path": "נָתִיב", - "confirmDelete": "האם אתה בטוח שאתה רוצה למחוק {{name}}?", - "permissionDenied": "ההרשאה נדחתה", - "serverError": "שגיאת שרת", - "fileSavedSuccessfully": "הקובץ נשמר בהצלחה", - "failedToSaveFile": "שמירת הקובץ נכשלה", - "confirmDeleteSingleItem": "האם אתה בטוח שברצונך למחוק לצמיתות את \"{{name}}\"?", - "confirmDeleteMultipleItems": "האם אתה בטוח שברצונך למחוק לצמיתות {{count}} פריטים?", - "confirmDeleteMultipleItemsWithFolders": "האם אתה בטוח שברצונך למחוק לצמיתות את הפריטים {{count}} ? זה כולל תיקיות ותוכן שלהן.", - "confirmDeleteFolder": "האם אתה בטוח שברצונך למחוק לצמיתות את התיקייה \"{{name}}\" ואת כל תוכנה?", - "permanentDeleteWarning": "לא ניתן לבטל פעולה זו. הפריט/ים יימחקו לצמיתות מהשרת.", - "recent": "אחרונים", - "pinned": "מוצמד", - "folderShortcuts": "קיצורי דרך לתיקיות", - "failedToReconnectSSH": "נכשל בחיבור מחדש של סשן SSH", - "openTerminalHere": "פתח את הטרמינל כאן", - "run": "לָרוּץ", - "openTerminalInFolder": "פתח את הטרמינל בתיקייה זו", - "openTerminalInFileLocation": "פתיחת מסוף במיקום הקובץ", - "runningFile": "ריצה - {{file}}", - "onlyRunExecutableFiles": "יכול להריץ רק קבצי הפעלה", - "directories": "מדריכים", - "removedFromRecentFiles": "הוסר \"{{name}}\" מהקבצים האחרונים", - "removeFailed": "ההסרה נכשלה", - "unpinnedSuccessfully": "\"{{name}}\" בוטל בהצלחה", - "unpinFailed": "ביטול ההצמדה נכשל", - "removedShortcut": "קיצור הדרך \"{{name}}\" הוסר", - "removeShortcutFailed": "הסרת קיצור הדרך נכשלה", - "clearedAllRecentFiles": "ניקה את כל הקבצים האחרונים", - "clearFailed": "ניקוי נכשל", - "removeFromRecentFiles": "הסר מהקבצים האחרונים", - "clearAllRecentFiles": "נקה את כל הקבצים האחרונים", - "unpinFile": "ביטול הצמדת קובץ", - "removeShortcut": "הסר קיצור דרך", - "pinFile": "קובץ הצמדה", - "addToShortcuts": "הוסף לקיצורי דרך", - "pasteFailed": "ההדבקה נכשלה", - "noUndoableActions": "אין פעולות שניתן לבטל", - "undoCopySuccess": "פעולת העתקה שבוטלה: מחיקת {{count}} קבצים שהועתקו", - "undoCopyFailedDelete": "ביטול נכשל: לא ניתן היה למחוק קבצים שהועתקו", - "undoCopyFailedNoInfo": "ביטול נכשל: לא ניתן היה למצוא את פרטי הקובץ שהועתק", - "undoMoveSuccess": "פעולת העברה שבוטלה: העבירו {{count}} קבצים חזרה למיקום המקורי", - "undoMoveFailedMove": "ביטול נכשל: לא ניתן היה להעביר קבצים בחזרה", - "undoMoveFailedNoInfo": "ביטול נכשל: לא ניתן היה למצוא מידע על הקובץ שהועבר", - "undoDeleteNotSupported": "לא ניתן לבטל את פעולת המחיקה: הקבצים נמחקו לצמיתות מהשרת", - "undoTypeNotSupported": "סוג פעולת ביטול לא נתמך", - "undoOperationFailed": "פעולת הביטול נכשלה", - "unknownError": "שגיאה לא ידועה", - "confirm": "לְאַשֵׁר", - "find": "לִמצוֹא...", - "replace": "לְהַחלִיף", - "downloadInstead": "הורד במקום זאת", - "keyboardShortcuts": "קיצורי מקלדת", - "searchAndReplace": "חיפוש והחלפה", - "editing": "עֲרִיכָה", - "search": "לְחַפֵּשׂ", - "findNext": "מצא את הבא", - "findPrevious": "מצא את הקודם", - "save": "לְהַצִיל", - "selectAll": "בחר הכל", - "undo": "לְבַטֵל", - "redo": "לַעֲשׂוֹת שׁוּב", - "moveLineUp": "הזזת שורה למעלה", - "moveLineDown": "הזזת שורה למטה", - "toggleComment": "החלף/הפעל תגובה", - "autoComplete": "השלמה אוטומטית", - "imageLoadError": "טעינת התמונה נכשלה", - "startTyping": "התחל להקליד...", - "unknownSize": "גודל לא ידוע", - "fileIsEmpty": "הקובץ ריק", - "largeFileWarning": "אזהרת קובץ גדול", - "largeFileWarningDesc": "קובץ זה הוא בגודל {{size}} , דבר שעלול לגרום לבעיות ביצועים בעת פתיחה כטקסט.", - "fileNotFoundAndRemoved": "הקובץ \"{{name}}\" לא נמצא והוסר מהקבצים האחרונים/המוצמדים", - "failedToLoadFile": "טעינת הקובץ נכשלה: {{error}}", - "serverErrorOccurred": "אירעה שגיאת שרת. אנא נסה שוב מאוחר יותר.", - "autoSaveFailed": "השמירה האוטומטית נכשלה", - "fileAutoSaved": "קובץ נשמר אוטומטית", - "moveFileFailed": "נכשל בהזזת {{name}}", - "moveOperationFailed": "פעולת ההעברה נכשלה", - "canOnlyCompareFiles": "ניתן להשוות רק שני קבצים", - "comparingFiles": "השוואת קבצים: {{file1}} ו- {{file2}}", - "dragFailed": "פעולת הגרירה נכשלה", - "filePinnedSuccessfully": "הקובץ \"{{name}}\" הוצמד בהצלחה", - "pinFileFailed": "נכשל בהצמדת הקובץ", - "fileUnpinnedSuccessfully": "הקובץ \"{{name}}\" נותק בהצלחה", - "unpinFileFailed": "נכשל ניתוק הקובץ", - "shortcutAddedSuccessfully": "קיצור הדרך לתיקייה \"{{name}}\" נוסף בהצלחה", - "addShortcutFailed": "הוספת קיצור דרך נכשלה", - "operationCompletedSuccessfully": "{{operation}} {{count}} פריטים הצליחו", - "operationCompleted": "{{operation}} {{count}} פריטים", - "downloadFileSuccess": "הקובץ {{name}} הורד בהצלחה", - "downloadFileFailed": "ההורדה נכשלה", - "moveTo": "מעבר אל {{name}}", - "diffCompareWith": "השוואת הבדלים עם {{name}}", - "dragOutsideToDownload": "גרור את הקבצים אל מחוץ לחלון כדי להוריד אותם ({{count}})", - "newFolderDefault": "תיקייה חדשה", - "newFileDefault": "קובץ חדש.txt", - "successfullyMovedItems": "הועבר בהצלחה {{count}} פריטים אל {{target}}", - "move": "מַהֲלָך", - "searchInFile": "חיפוש בקובץ (Ctrl+F)", - "showKeyboardShortcuts": "הצג קיצורי מקלדת", - "startWritingMarkdown": "התחל לכתוב את תוכן ההנחה שלך...", - "loadingFileComparison": "טוען השוואת קבצים...", - "reload": "לִטעוֹן מִחָדָשׁ", - "compare": "לְהַשְׁווֹת", - "sideBySide": "זֶה בְּצַד זֶה", - "inline": "מוטבע", - "fileComparison": "השוואת קבצים: {{file1}} לעומת {{file2}}", - "fileTooLarge": "קובץ גדול מדי: {{error}}", - "sshConnectionFailed": "חיבור SSH נכשל. אנא בדוק את החיבור שלך אל {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "טעינת הקובץ נכשלה: {{error}}", - "connecting": "מְקַשֵׁר...", - "connectedSuccessfully": "התחבר בהצלחה", - "totpVerificationFailed": "אימות TOTP נכשל", - "warpgateVerificationFailed": "אימות Warpgate נכשל", - "authenticationFailed": "האימות נכשל", - "verificationCodePrompt": "קוד אימות:", - "changePermissions": "שינוי הרשאות", - "currentPermissions": "הרשאות נוכחיות", - "owner": "בַּעַל", - "group": "קְבוּצָה", - "others": "אחרים", - "read": "לִקְרוֹא", - "write": "לִכתוֹב", - "execute": "לְבַצֵעַ", - "permissionsChangedSuccessfully": "ההרשאות שונו בהצלחה", - "failedToChangePermissions": "שינוי ההרשאות נכשל", - "name": "שֵׁם", - "sortByName": "שֵׁם", - "sortByDate": "תאריך שינוי", - "sortBySize": "גוֹדֶל", - "ascending": "עוֹלֶה", - "descending": "יורד", - "root": "שׁוֹרֶשׁ", - "new": "חָדָשׁ", - "sortBy": "מיין לפי", - "items": "פריטים", - "selected": "נִבחָר", - "editor": "עוֹרֵך", - "octal": "אוקטלי", - "storage": "אִחסוּן", - "disk": "דִיסק", - "used": "מְשׁוּמָשׁ", - "of": "שֶׁל", - "toggleSidebar": "הפעלה/כיבוי סרגל צד", - "cannotLoadPdf": "לא ניתן לטעון את ה-PDF", - "pdfLoadError": "אירעה שגיאה בטעינת קובץ PDF זה.", - "loadingPdf": "טוען PDF...", - "loadingPage": "טוען דף..." + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "העתק למארח…", - "moveToHost": "מעבר למארח…", - "copyItemsToHost": "העתק את הפריטים {{count}} לאירוח…", - "moveItemsToHost": "העבר {{count}} פריטים לאירוח…", - "noHostsConnected": "אין מארחי מנהל קבצים אחרים זמינים.", - "noHostsConnectedHint": "הוסף מארח SSH נוסף כאשר מנהל הקבצים מופעל במנהל המארחים.", - "selectDestinationHost": "בחר מארח יעד", - "destinationPath": "נתיב היעד", - "recentDestinations": "יעדים אחרונים", - "collapseRecentDestinations": "כיווץ יעדים אחרונים", - "expandRecentDestinations": "הרחב יעדים אחרונים", - "browseFolders": "עיון בתיקיות יעד", - "browseDestination": "עיון או הזנת נתיב", - "confirmCopy": "לְהַעְתִיק", - "confirmMove": "מַהֲלָך", - "transferring": "מעביר…", - "compressing": "דחיסת…", - "extracting": "מחלץ…", - "transferringItems": "העברת {{current}} מתוך {{total}} פריטים…", - "transferSuccess": "ההעברה הושלמה", - "transferError": "ההעברה נכשלה", - "transferPartial": "ההעברה הושלמה עם שגיאות {{count}}", - "transferPartialHint": "לא ניתן היה להעביר: {{paths}}", - "itemsSummary": "פריטים {{count}}", - "destMustBeDirectory": "היעד חייב להיות ספרייה עבור העברות מרובות פריטים", - "selectThisFolder": "בחר תיקייה זו", - "browsePathWillBeCreated": "תיקייה זו עדיין לא קיימת. היא תיווצר כאשר ההעברה תתחיל.", - "browsePathError": "לא ניתן היה לפתוח נתיב זה במארח היעד.", - "goUp": "לַעֲלוֹת", - "copyFolderToHost": "העתקת תיקייה למארח…", - "moveFolderToHost": "העברת תיקייה למארח…", - "hostReady": "מוּכָן", - "hostConnecting": "חיבור…", - "hostDisconnected": "לא מחובר", - "hostAuthRequired": "נדרש אימות - פתח תחילה את מנהל הקבצים במארח זה", - "hostConnectionFailed": "החיבור נכשל", - "metricsTitle": "זמני העברה", - "metricsPrepare": "הכנת יעד: {{duration}}", - "metricsCompress": "דחיסה במקור: {{duration}}", - "metricsHopSourceRead": "מקור → שרת: {{throughput}}", - "metricsHopDestSftpWrite": "שרת → יעד (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "שרת → יעד (מקומי): {{throughput}}", - "metricsTransfer": "מקצה לקצה: {{throughput}} ({{duration}})", - "metricsExtract": "חילוץ ביעד: {{duration}}", - "metricsSourceDelete": "הסר מהמקור: {{duration}}", - "metricsTotal": "סה\"כ: {{duration}}", - "progressCompressing": "דחיסה במארח המקור…", - "progressExtracting": "מחלץ ביעד…", - "progressTransferring": "העברת נתונים…", - "progressReconnecting": "מתחבר מחדש…", - "parallelSegmentsLabel": "נתיבי מעבר מקבילים", - "parallelSegmentsOption": "נתיבים {{count}}", - "parallelSegmentsHint": "קבצים גדולים מחולקים לחתיכות של 256 מגה-בייט. מספר נתיבים משתמשים בחיבורים נפרדים (כמו התחלת מספר העברות) לקבלת תפוקה כוללת גבוהה יותר.", - "progressTotalSpeed": "סה\"כ {{speed}} (נתיבים{{lanes}})", - "progressTransferringItems": "העברת קבצים ({{current}} מתוך {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "קבצים {{current}} / {{total}}", - "sourceNotDeletedPartial": "קבצי המקור נשמרו (העברה חלקית)", - "jumpHostLimitation": "שני המארחים חייבים להיות נגישים משרת Termix. ניתוב ישיר ממארח למארח אינו נתמך.", - "cancel": "לְבַטֵל", - "methodLabel": "שיטת העברה", - "methodAuto": "אוטומטי", - "methodTar": "ארכיון טאר", - "methodItemSftp": "SFTP לפי קובץ", - "methodAutoHint": "בוחר SFTP לפי קובץ או tar בהתבסס על ספירת הקבצים, גודלם ויכולת הדחיסה. קבצים בודדים תמיד משתמשים ב-SFTP בסטרימינג.", - "methodTarHint": "דחיסה במקור, העברת ארכיון אחד, חילוץ ביעד. דורש tar בשני מארחי יוניקס.", - "methodItemSftpHint": "העבר כל קובץ בנפרד דרך SFTP. עובד על כל המארחים כולל Windows.", - "methodPreviewLoading": "חישוב שיטת ההעברה…", - "methodPreviewError": "לא ניתן היה להציג תצוגה מקדימה של שיטת ההעברה. השרת עדיין יבחר שיטה כשתתחיל.", - "methodPreviewWillUseTar": "אשתמש ב: ארכיון Tar", - "methodPreviewWillUseItemSftp": "ישתמש ב: SFTP לפי קובץ", - "methodPreviewScanSummary": "{{fileCount}} קבצים, {{totalSize}} סה\"כ (נסרק במארח המקור).", - "methodItemSftpLimitation": "כל קובץ משתמש באותו זרם SFTP כעותק של קובץ בודד, אחד אחרי השני. ההתקדמות משולבת על פני כל הקבצים, כך שהסרגל נע לאט במהלך קבצים גדולים.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "בחרת SFTP לפי קובץ.", - "user_tar": "בחרת בארכיון tar.", - "tar_unavailable": "Tar אינו זמין באחד המארחים או בשניהם - במקום זאת ייעשה שימוש ב-SFTP לפי קובץ.", - "windows_host": "מחשב מארח של Windows מעורב - tar אינו בשימוש.", - "auto_multi_large": "אוטומטי: קבצים מרובים כולל קובץ גדול ({{largestSize}}) עם נתונים הניתנים לדחיסה - tar מאחד את הנתונים להעברה אחת.", - "auto_single_large_in_archive": "אוטומטי: קובץ אחד גדול ({{largestSize}}) בקבוצה זו - SFTP לכל קובץ.", - "auto_many_incompressible": "אוטומטי: נתונים לרוב בלתי ניתנים לדחיסה - SFTP לכל קובץ.", - "auto_many_files": "אוטומטי: קבצים רבים ({{fileCount}}) — tar מפחית את התקורה של כל קובץ.", - "auto_default": "אוטומטי: SFTP לכל קובץ עבור קבוצה זו." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "לְבַטֵל", - "progressCancelling": "מבטל…", - "progressStalled": "אָבוּס", - "resumedHint": "התחבר מחדש להעברה פעילה שהתחילה בחלון אחר.", - "transferCancelled": "ההעברה בוטלה", - "transferCancelledCopyHint": "ייתכן שיישארו קבצים חלקיים ביעד.", - "transferCancelledMoveHint": "ייתכן שיישארו קבצים חלקיים ביעד. קבצי המקור לא הוסרו.", - "cleanupDestFiles": "יעד ניקוי", - "cleanupDestFilesSuccess": "הוסרו קבצים חלקיים מהיעד", - "cleanupDestFilesPartial": "לא ניתן היה להסיר חלק מהקבצים", - "cleanupDestFilesNothing": "אין מה לנקות ביעד", - "cleanupDestFilesError": "הניקוי נכשל", - "retryTransfer": "נסה שוב", - "retryTransferError": "ניסיון חוזר נכשל", - "transferFailedRetryHint": "נתונים חלקיים נשמרו ביעד. ניסיון חוזר יתחדש כאשר החיבור יחזור." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "אין מנהרות SSH", - "createFirstTunnelMessage": "עדיין לא יצרת מנהרות SSH. הגדר חיבורי מנהרות במנהל המארח כדי להתחיל.", - "connected": "מְחוּבָּר", - "disconnected": "מְנוּתָק", - "connecting": "מְקַשֵׁר...", - "error": "שְׁגִיאָה", - "canceling": "מבטל...", - "connect": "לְחַבֵּר", - "disconnect": "לְנַתֵק", - "cancel": "לְבַטֵל", - "port": "נָמָל", - "localPort": "נמל מקומי", - "remotePort": "יציאה מרוחקת", - "currentHostPort": "יציאת המארח הנוכחית", - "endpointPort": "יציאת נקודת קצה", - "bindIp": "IP מקומי", - "endpointSshConfig": "תצורת SSH של נקודת קצה", - "endpointSshHost": "מארח SSH של נקודת קצה", - "endpointSshHostPlaceholder": "בחר מארח מוגדר", - "endpointSshHostRequired": "בחר מארח SSH של נקודת קצה עבור כל מנהרת לקוח.", - "attempt": "ניסיון {{current}} של {{max}}", - "nextRetryIn": "ניסיון חוזר הבא בעוד {{seconds}} שניות", - "clientTunnels": "מנהרות לקוח", - "clientTunnel": "מנהרת לקוח", - "addClientTunnel": "הוסף מנהרת לקוח", - "noClientTunnels": "לא הוגדרו מנהרות לקוח בשולחן עבודה זה.", - "tunnelName": "שם המנהרה", - "remoteHost": "מארח מרוחק", - "autoStart": "הפעלה אוטומטית", - "clientAutoStartDesc": "מתחיל כאשר לקוח שולחן עבודה זה נפתח ונשאר מחובר.", - "clientManualStartDesc": "השתמשו בפקודות התחל ועצירה משורה זו. טרמיקס לא יפתח אותה אוטומטית.", - "clientRemoteServerNote": "ייתכן שהעברת נתונים מרחוק תדרוש את AllowTcpForwarding ו-GatewayPorts בשרת ה-SSH של נקודת הקצה. היציאה המרוחקת נסגרת כאשר שולחן עבודה זה מתנתק.", - "clientTunnelStarted": "מנהרת הלקוחות התחילה", - "clientTunnelStopped": "מנהרת הלקוח נעצרה", - "tunnelTestSucceeded": "מבחן המנהרה הצליח", - "tunnelTestFailed": "מבחן המנהרה נכשל", - "localSaved": "מנהרות הלקוח נשמרו", - "localSaveError": "נכשלה שמירת מנהרות הלקוח המקומיות", - "invalidBindIp": "כתובת IP מקומית חייבת להיות כתובת IPv4 תקפה.", - "invalidLocalTargetIp": "כתובת ה-IP המקומית של היעד חייבת להיות כתובת IPv4 חוקית.", - "invalidLocalPort": "היציאה המקומית חייבת להיות בין 1 ל-65535.", - "invalidRemotePort": "היציאה המרוחקת חייבת להיות בין 1 ל-65535.", - "invalidLocalTargetPort": "יציאת היעד המקומית חייבת להיות בין 1 ל-65535.", - "invalidEndpointPort": "יציאת נקודת הקצה חייבת להיות בין 1 ל-65535.", - "duplicateAutoStartBind": "רק מנהרת לקוח אחת עם הפעלה אוטומטית יכולה להשתמש ב- {{bind}}.", - "manualControlError": "עדכון מצב המנהרה נכשל.", - "active": "פָּעִיל", - "start": "הַתחָלָה", - "stop": "לְהַפְסִיק", - "test": "מִבְחָן", - "type": "סוג המנהרה", - "typeLocal": "מקומי (-L)", - "typeRemote": "מרחוק (-R)", - "typeDynamic": "דינמי (-D)", - "typeServerLocalDesc": "מהמארח הנוכחי לנקודת הקצה.", - "typeServerRemoteDesc": "נקודת הקצה חזרה למארח הנוכחי.", - "typeClientLocalDesc": "מחשב מקומי לנקודת קצה.", - "typeClientRemoteDesc": "נקודת הקצה חזרה למחשב המקומי.", - "typeClientDynamicDesc": "גרביים במחשב מקומי.", - "typeDynamicDesc": "העברת תעבורת SOCKS5 CONNECT דרך SSH", - "forwardDescriptionServerLocal": "מארח נוכחי {{sourcePort}} → נקודת קצה {{endpointPort}}.", - "forwardDescriptionServerRemote": "נקודת קצה {{endpointPort}} → מארח נוכחי {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS על המארח הנוכחי {{sourcePort}}.", - "forwardDescriptionClientLocal": "מקומי {{sourcePort}} → מרוחק {{endpointPort}}.", - "forwardDescriptionClientRemote": "מרוחק {{sourcePort}} → מקומי {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS בפורט המקומי {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → גרביים דרך {{endpoint}}", - "autoNameClientLocal": "מקומי {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → מקומי {{localPort}}", - "autoNameClientDynamic": "גרביים {{localPort}} דרך {{endpoint}}", - "route": "מַסלוּל:", - "lastStarted": "התחיל לאחרונה", - "lastTested": "נבדק לאחרונה", - "lastError": "השגיאה האחרונה", - "maxRetries": "מקסימום ניסיונות חוזרים", - "maxRetriesDescription": "מספר מקסימלי של ניסיונות ניסיון חוזר.", - "retryInterval": "מרווח זמן לניסיון חוזר (שניות)", - "retryIntervalDescription": "זמן המתנה בין ניסיונות חוזרים.", - "local": "מְקוֹמִי", - "remote": "מְרוּחָק", - "destination": "יַעַד", - "host": "מְאָרֵחַ", - "mode": "מצב", - "noHostSelected": "לא נבחר מארח", - "working": "עובד..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "מעבד", - "memory": "זֵכֶר", - "disk": "דִיסק", - "network": "רֶשֶׁת", - "uptime": "זמן פעולה", - "processes": "תהליכים", - "available": "זָמִין", - "free": "לְשַׁחְרֵר", - "connecting": "מְקַשֵׁר...", - "connectionFailed": "נכשלה ההתחברות לשרת", - "naCpus": "מעבדים לא רלוונטיים", - "cpuCores_one": "{{count}} ליבה", - "cpuCores_other": "{{count}} ליבות", - "cpuUsage": "שימוש במעבד", - "memoryUsage": "שימוש בזיכרון", - "diskUsage": "שימוש בדיסק", - "failedToFetchHostConfig": "נכשל באחזור תצורת המארח", - "serverOffline": "שרת לא מקוון", - "cannotFetchMetrics": "לא ניתן לאחזר מדדים משרת לא מקוון", - "totpFailed": "אימות TOTP נכשל", - "noneAuthNotSupported": "סטטיסטיקות השרת אינן תומכות בסוג אימות 'ללא'.", - "load": "לִטעוֹן", - "systemInfo": "מידע מערכת", - "hostname": "שם מארח", - "operatingSystem": "מַעֲרֶכֶת הַפעָלָה", - "kernel": "גַרעִין", - "seconds": "שניות", - "networkInterfaces": "ממשקי רשת", - "noInterfacesFound": "לא נמצאו ממשקי רשת", - "noProcessesFound": "לא נמצאו תהליכים", - "loginStats": "סטטיסטיקות כניסה ל-SSH", - "noRecentLoginData": "אין נתוני התחברות אחרונים", - "executingQuickAction": "מבצע {{name}}...", - "quickActionSuccess": "{{name}} הושלם בהצלחה", - "quickActionFailed": "{{name}} נכשל", - "quickActionError": "נכשל בביצוע {{name}}", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "יציאות האזנה", - "protocol": "פּרוֹטוֹקוֹל", - "port": "נָמָל", - "address": "כְּתוֹבֶת", - "process": "תַהֲלִיך", - "noData": "אין נתוני יציאות האזנה" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "חומת אש", - "inactive": "לֹא פָּעִיל", - "policy": "מְדִינִיוּת", - "rules": "כללים", - "noData": "אין נתוני חומת אש זמינים", - "action": "פְּעוּלָה", - "protocol": "פרוטו", - "port": "נָמָל", - "source": "מָקוֹר", - "anywhere": "בְּכָל מָקוֹם" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "טען ממוצע", - "swap": "לְהַחלִיף", - "architecture": "אַדְרִיכָלוּת", - "refresh": "לְרַעֲנֵן", - "retry": "נסה שוב" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "ניהול SSH ושולחן עבודה מרוחק באחסון עצמי", - "loginTitle": "התחברות לטרמיקס", - "registerTitle": "צור חשבון", - "forgotPassword": "שכחת סיסמה?", - "rememberMe": "זכור את המכשיר למשך 30 יום (כולל TOTP)", - "noAccount": "אין לך חשבון?", - "hasAccount": "כבר יש לך חשבון?", - "twoFactorAuth": "אימות דו-שלבי", - "enterCode": "הזן קוד אימות", - "backupCode": "או להשתמש בקוד גיבוי", - "verifyCode": "אימות קוד", - "redirectingToApp": "מפנה לאפליקציה...", - "sshAuthenticationRequired": "נדרש אימות SSH", - "sshNoKeyboardInteractive": "אימות אינטראקטיבי באמצעות מקלדת אינו זמין", - "sshAuthenticationFailed": "האימות נכשל", - "sshAuthenticationTimeout": "פסק זמן לאימות", - "sshNoKeyboardInteractiveDescription": "השרת אינו תומך באימות אינטראקטיבי באמצעות מקלדת. אנא ספק את הסיסמה או מפתח ה-SSH שלך.", - "sshAuthFailedDescription": "האישורים שסופקו היו שגויים. אנא נסה שוב עם אישורי גישה תקפים.", - "sshTimeoutDescription": "ניסיון האימות הסתיים. אנא נסה שוב.", - "sshProvideCredentialsDescription": "אנא ספק את פרטי ה-SSH שלך כדי להתחבר לשרת זה.", - "sshPasswordDescription": "הזן את הסיסמה עבור חיבור SSH זה.", - "sshKeyPasswordDescription": "אם מפתח ה-SSH שלך מוצפן, הזן את סיסמתך כאן.", - "passphraseRequired": "נדרשת סיסמה", - "passphraseRequiredDescription": "מפתח ה-SSH מוצפן. אנא הזן את סיסמת הגישה כדי לפתוח אותו.", - "back": "בְּחֲזָרָה", - "firstUser": "משתמש ראשון", - "firstUserMessage": "אתה המשתמש הראשון ותהפוך למנהל. תוכל לצפות בהגדרות מנהל המערכת בתפריט הנפתח של המשתמשים בסרגל הצד. אם אתה חושב שזו טעות, בדוק את יומני ה-docker או צור בעיה ב-GitHub.", - "external": "חִיצוֹנִי", - "loginWithExternal": "התחברות עם ספק חיצוני", - "loginWithExternalDesc": "התחבר באמצעות ספק הזהויות החיצוני שתצורתו נקבעה", - "externalNotSupportedInElectron": "אימות חיצוני אינו נתמך עדיין באפליקציית Electron. אנא השתמש בגרסת האינטרנט לצורך כניסה ל-OIDC.", - "resetPasswordButton": "איפוס סיסמה", - "sendResetCode": "שלח קוד איפוס", - "resetCodeDesc": "הזן את שם המשתמש שלך כדי לקבל קוד איפוס סיסמה. הקוד יירשם ביומני המכולה של docker.", - "resetCode": "איפוס קוד", - "verifyCodeButton": "אימות קוד", - "enterResetCode": "הזן את הקוד בן 6 הספרות מיומני המכולה של docker עבור המשתמש:", - "newPassword": "סיסמה חדשה", - "confirmNewPassword": "אשר סיסמה", - "enterNewPassword": "הזן את הסיסמה החדשה שלך עבור המשתמש:", - "signUp": "הרשמה", - "desktopApp": "אפליקציית שולחן עבודה", - "loggingInToDesktopApp": "כניסה לאפליקציית שולחן העבודה", - "loadingServer": "טוען שרת...", - "dataLossWarning": "איפוס הסיסמה שלך בדרך זו ימחק את כל מארחי ה-SSH השמורים, פרטי הגישה ונתונים מוצפנים אחרים. לא ניתן לבטל פעולה זו. השתמש באפשרות זו רק אם שכחת את הסיסמה שלך ואינך מחובר.", - "authenticationDisabled": "אימות מושבת", - "authenticationDisabledDesc": "כל שיטות האימות מושבתות כעת. אנא צור קשר עם מנהל המערכת שלך.", - "attemptsRemaining": "{{count}} ניסיונות נותרו" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "אימות מפתח מארח SSH", - "keyChangedWarning": "מפתח מארח SSH שונה", - "firstConnectionTitle": "בפעם הראשונה שמתחברת למארח הזה", - "firstConnectionDescription": "לא ניתן לקבוע את האותנטיות של מארח זה. ודא שטביעת האצבע תואמת את מה שאתה מצפה.", - "keyChangedDescription": "מפתח המארח של שרת זה השתנה מאז החיבור האחרון שלך. זה יכול להצביע על בעיית אבטחה.", - "previousKey": "מפתח קודם", - "newFingerprint": "טביעת אצבע חדשה", - "fingerprint": "טְבִיעַת אֶצבָּעוֹת", - "verifyInstructions": "אם אתה בוטח במארח זה, לחץ על קבל כדי להמשיך ולשמור את טביעת האצבע הזו לחיבורים עתידיים.", - "securityWarning": "אזהרת אבטחה", - "acceptAndContinue": "קבל והמשך", - "acceptNewKey": "קבל את המפתח החדש והמשך" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "לא ניתן היה להתחבר למסד הנתונים", - "unknownError": "שגיאה לא ידועה", - "loginFailed": "הכניסה נכשלה", - "failedPasswordReset": "נכשלה הפעלת איפוס הסיסמה", - "failedVerifyCode": "נכשל אימות קוד האיפוס", - "failedCompleteReset": "נכשל השלמת איפוס הסיסמה", - "invalidTotpCode": "קוד TOTP לא חוקי", - "failedOidcLogin": "נכשל בהתחלת הכניסה ל-OIDC", - "silentSigninOidcUnavailable": "התבקשה כניסה שקטה, אך כניסה ל-OIDC אינה זמינה.", - "failedUserInfo": "נכשלה קבלת פרטי המשתמש לאחר הכניסה", - "oidcAuthFailed": "אימות OIDC נכשל", - "invalidAuthUrl": "כתובת URL לא חוקית להרשאה התקבלה מה-backend", - "requiredField": "שדה זה נדרש", - "minLength": "אורך מינימלי הוא {{min}}", - "passwordMismatch": "הסיסמאות אינן תואמות", - "passwordLoginDisabled": "כניסה באמצעות שם משתמש/סיסמה מושבתת כעת", - "sessionExpired": "הסשן פג תוקפו - אנא התחבר שוב", - "totpRateLimited": "קצב מוגבל: יותר מדי ניסיונות אימות TOTP. אנא נסה שוב מאוחר יותר.", - "totpRateLimitedWithTime": "קצב מוגבל: יותר מדי ניסיונות אימות TOTP. אנא המתן {{time}} שניות לפני שתנסה שוב.", - "resetCodeRateLimited": "קצב מוגבל: יותר מדי ניסיונות אימות. אנא נסה שוב מאוחר יותר.", - "resetCodeRateLimitedWithTime": "קצב מוגבל: יותר מדי ניסיונות אימות. אנא המתן {{time}} שניות לפני שתנסה שוב.", - "authTokenSaveFailed": "נכשלה שמירת אסימון האימות", - "failedToLoadServer": "טעינת השרת נכשלה" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "רישום חשבון חדש מושבת כעת על ידי מנהל. אנא התחבר או צור קשר עם מנהל.", - "userNotAllowed": "החשבון שלך אינו מורשה להירשם. אנא צור קשר עם מנהל מערכת.", - "databaseConnectionFailed": "נכשלה ההתחברות לשרת מסד הנתונים", - "resetCodeSent": "איפוס קוד שנשלח ליומני Docker", - "codeVerified": "הקוד אומת בהצלחה", - "passwordResetSuccess": "איפוס הסיסמה בהצלחה", - "loginSuccess": "הכניסה הצליחה", - "registrationSuccess": "ההרשמה הצליחה" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "מנהרות שולחן עבודה מקומיות המכוונות למארחי SSH שהוגדרו.", - "c2sTunnelPresets": "קביעות מוגדרות מראש של מנהרת לקוח", - "c2sTunnelPresetsDesc": "שמור את רשימת המנהרות המקומית של לקוח שולחן עבודה זה כקביעה מוגדרת מראש של שרת בעל שם, או טען קביעה מוגדרת מראש בחזרה ללקוח זה.", - "c2sTunnelPresetsUnavailable": "הגדרות קבועות מראש של מנהרת לקוח זמינות רק בלקוח שולחן העבודה.", - "c2sPresetName": "שם מוגדר מראש", - "c2sPresetNamePlaceholder": "שם קבוע מראש של הלקוח", - "c2sPresetToLoad": "הגדרה מראש לטעינה", - "c2sNoPresetSelected": "לא נבחרה הגדרה מוגדרת מראש", - "c2sNoPresets": "לא נשמרו הגדרות קבועות מראש", - "c2sLoadPreset": "לִטעוֹן", - "c2sCurrentLocalConfig": "מנהרה/ות לקוח מקומיות {{count}} מוגדרות בשולחן עבודה זה.", - "c2sPresetSyncNote": "קביעות מוגדרות מראש הן תמונות בזק מפורשות; טעינת אחת מהן מחליפה את רשימת מנהרות הלקוחות המקומיות של לקוח שולחן העבודה הזה.", - "c2sPresetSaved": "מנהרת הלקוח מוגדרת מראש נשמרה", - "c2sPresetLoaded": "מנהרת לקוח מוגדרת מראש נטענת באופן מקומי", - "c2sPresetRenamed": "שם של פריסט של מנהרת הלקוח", - "c2sPresetDeleted": "מנהרת הלקוח מוגדרת מראש נמחקה", - "c2sPresetLoadError": "נכשלה טעינת הגדרות קבועות מראש של מנהרת הלקוח" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "שָׂפָה", - "keyPassword": "סיסמת מפתח", - "pastePrivateKey": "הדבק את המפתח הפרטי שלך כאן...", - "localListenerHost": "127.0.0.1 (האזנה מקומית)", - "localTargetHost": "127.0.0.1 (היעד במחשב זה)", - "socksListenerHost": "127.0.0.1 (מאזין SOCKS)", - "enterPassword": "הזן את הסיסמה שלך", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "לוּחַ מַחווָנִים", - "loading": "טוען לוח מחוונים...", - "github": "גיטהאב", - "support": "תְמִיכָה", - "discord": "מַחֲלוֹקֶת", - "serverOverview": "סקירת שרת", - "version": "גִרְסָה", - "upToDate": "מעודכן", - "updateAvailable": "עדכון זמין", - "beta": "בטא", - "uptime": "זמן פעולה", - "database": "מסד נתונים", - "healthy": "בָּרִיא", - "error": "שְׁגִיאָה", - "totalHosts": "סך כל המארחים", - "totalTunnels": "סך המנהרות", - "totalCredentials": "סך כל האישורים", - "recentActivity": "פעילות אחרונה", - "reset": "אִתחוּל", - "loadingRecentActivity": "טוען פעילות אחרונה...", - "noRecentActivity": "אין פעילות אחרונה", - "quickActions": "פעולות מהירות", - "addHost": "הוסף מארח", - "addCredential": "הוסף אישור", - "adminSettings": "הגדרות מנהל מערכת", - "userProfile": "פרופיל משתמש", - "serverStats": "סטטיסטיקות שרת", - "loadingServerStats": "טוען סטטיסטיקות שרת...", - "noServerData": "אין נתוני שרת זמינים", - "cpu": "מעבד", - "ram": "אַיִל", - "customizeLayout": "התאמה אישית של לוח המחוונים", - "dashboardSettings": "הגדרות לוח המחוונים", - "enableDisableCards": "הפעלה/השבתה של כרטיסים", - "resetLayout": "איפוס לברירת מחדל", - "serverOverviewCard": "סקירת שרת", - "recentActivityCard": "פעילות אחרונה", - "networkGraphCard": "גרף הרשת", - "networkGraph": "גרף הרשת", - "quickActionsCard": "פעולות מהירות", - "serverStatsCard": "סטטיסטיקות שרת", - "panelMain": "רָאשִׁי", - "panelSide": "צַד", - "justNow": "זֶה עַתָה" + "title": "Dashboard", + "loading": "Loading dashboard...", + "github": "GitHub", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "יַצִיב", - "hostsOnline": "מארחים אונליין", - "activeTunnels": "מנהרות פעילות", - "registerNewServer": "רישום שרת חדש", - "storeSshKeysOrPasswords": "אחסון מפתחות או סיסמאות SSH", - "manageUsersAndRoles": "ניהול משתמשים ותפקידים", - "manageYourAccount": "ניהול החשבון שלך", - "hostStatus": "סטטוס מארח", - "noHostsConfigured": "לא הוגדרו מארחים", - "online": "באינטרנט", - "offline": "לא מקוון", - "onlineLower": "באינטרנט", - "nodes": "צמתים {{count}}", - "add": "לְהוֹסִיף:", - "commandPalette": "לוח פקודות", - "done": "נַעֲשָׂה", - "editModeInstructions": "גרור כרטיסים כדי לשנות את הסדר · גרור את מפריד העמודות כדי לשנות את גודל העמודות · גרור את הקצה התחתון של הכרטיס כדי לשנות את גודל הגובה שלו · אשפה כדי להסיר", - "empty": "רֵיק", - "clear": "בָּרוּר" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "הוסף מארח", - "addGroup": "הוסף קבוצה", - "addLink": "הוסף קישור", - "zoomIn": "לְהִתְמַקֵד", - "zoomOut": "התקרבות", - "resetView": "איפוס תצוגה", - "selectHost": "בחר מארח", - "chooseHost": "בחר מארח...", - "parentGroup": "קבוצת הורים", - "noGroup": "אין קבוצה", - "groupName": "שם הקבוצה", - "color": "צֶבַע", - "source": "מָקוֹר", - "target": "יַעַד", - "moveToGroup": "העבר לקבוצה", - "selectGroup": "בחירת קבוצה...", - "addConnection": "הוסף חיבור", - "hostDetails": "פרטי המארח", - "removeFromGroup": "הסר מהקבוצה", - "addHostHere": "הוסף מארח כאן", - "editGroup": "עריכת קבוצה", - "delete": "לִמְחוֹק", - "add": "לְהוֹסִיף", - "create": "לִיצוֹר", - "move": "מַהֲלָך", - "connect": "לְחַבֵּר", - "createGroup": "צור קבוצה", - "selectSourcePlaceholder": "בחר מקור...", - "selectTargetPlaceholder": "בחר יעד...", - "invalidFile": "קובץ לא חוקי", - "hostAlreadyExists": "המארח כבר נמצא בטופולוגיה", - "connectionExists": "החיבור כבר קיים", - "unknown": "לֹא יְדוּעַ", - "name": "שֵׁם", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "סטָטוּס", - "failedToAddNode": "נכשל בהוספת הצומת", - "sourceDifferentFromTarget": "המקור והיעד חייבים להיות שונים", - "exportJSON": "ייצוא JSON", - "importJSON": "ייבוא JSON", - "terminal": "מָסוֹף", - "fileManager": "מנהל הקבצים", - "tunnel": "מִנהָרָה", - "docker": "דוקר", - "serverStats": "סטטיסטיקות שרת", - "noNodes": "עדיין אין צמתים" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker אינו מופעל עבור מארח זה", - "validating": "מאמת את Docker...", - "connecting": "מְקַשֵׁר...", - "error": "שְׁגִיאָה", - "version": "דוקר {{version}}", - "connectionFailed": "נכשלה ההתחברות ל-Docker", - "containerStarted": "מיכל {{name}} הופעל", - "failedToStartContainer": "נכשל בהפעלת המכולה {{name}}", - "containerStopped": "מיכל {{name}} נעצר", - "failedToStopContainer": "נכשלה עצירת המכולה {{name}}", - "containerRestarted": "המיכל {{name}} הופעל מחדש", - "failedToRestartContainer": "נכשלה ההפעלה מחדש של המכולה {{name}}", - "containerPaused": "מיכל {{name}} הושהה", - "containerUnpaused": "השהייתה של המיכל {{name}} בוטלה", - "failedToTogglePauseContainer": "נכשל הניסיון להחליף בין מצב השהייה עבור המכולה {{name}}", - "containerRemoved": "המיכל {{name}} הוסר", - "failedToRemoveContainer": "נכשלה הסרת המיכל {{name}}", - "image": "תְמוּנָה", - "ports": "נמלים", - "noPorts": "אין יציאות", - "start": "הַתחָלָה", - "confirmRemoveContainer": "האם אתה בטוח שברצונך להסיר את המכולה '{{name}}'? לא ניתן לבטל פעולה זו.", - "runningContainerWarning": "אזהרה: מיכל זה פועל כעת. הסרתו תעצור את המיכל תחילה.", - "loadingContainers": "טוען מכולות...", - "manager": "מנהל דוקר", - "autoRefresh": "רענון אוטומטי", - "timestamps": "חותמות זמן", - "lines": "קווים", - "filterLogs": "סנן יומני רישום...", - "refresh": "לְרַעֲנֵן", - "download": "הורדה", - "clear": "בָּרוּר", - "logsDownloaded": "יומני הרישום הורדו בהצלחה", - "last50": "50 האחרונים", - "last100": "100 האחרונים", - "last500": "500 האחרונים", - "last1000": "1000 האחרונים", - "allLogs": "כל היומנים", - "noLogsMatching": "אין יומנים התואמים ל-\"{{query}}\"", - "noLogsAvailable": "אין יומנים זמינים", - "noContainersFound": "לא נמצאו מכולות", - "noContainersFoundHint": "אין מכולות Docker זמינות במארח זה", - "searchPlaceholder": "חיפוש מכולות...", - "allStatuses": "כל הסטטוסים", - "stateRunning": "רִיצָה", - "statePaused": "מושהה", - "stateExited": "יצא", - "stateRestarting": "הפעלה מחדש", - "noContainersMatchFilters": "אין מכולות התואמות את המסננים שלך", - "noContainersMatchFiltersHint": "נסה להתאים את קריטריוני החיפוש או הסינון שלך", - "failedToFetchStats": "נכשלה אחזור סטטיסטיקות המכולה", - "containerNotRunning": "המכולה לא פועלת", - "startContainerToViewStats": "הפעל את המכולה כדי להציג נתונים סטטיסטיים", - "loadingStats": "טוען סטטיסטיקות...", - "errorLoadingStats": "שגיאה בטעינת סטטיסטיקות", - "noStatsAvailable": "אין סטטיסטיקות זמינות", - "cpuUsage": "שימוש במעבד", - "current": "נוֹכְחִי", - "memoryUsage": "שימוש בזיכרון", - "networkIo": "קלט/פלט של הרשת", - "input": "קֶלֶט", - "output": "תְפוּקָה", - "blockIo": "בלוק קלט/פלט", - "read": "לִקְרוֹא", - "write": "לִכתוֹב", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "מידע על המכולה", - "name": "שֵׁם", - "id": "תְעוּדַת זֶהוּת", - "state": "מְדִינָה", - "containerMustBeRunning": "יש לפעול על מנת לגשת למסוף.", - "verificationCodePrompt": "הזן קוד אימות", - "totpVerificationFailed": "אימות TOTP נכשל. אנא נסה שוב.", - "warpgateVerificationFailed": "אימות Warpgate נכשל. אנא נסה שוב.", - "connectedTo": "מחובר אל {{containerName}}", - "disconnected": "מְנוּתָק", - "consoleError": "שגיאת קונסולה", - "errorMessage": "שגיאה: {{message}}", - "failedToConnect": "נכשל החיבור למכולה", - "console": "לְנַחֵם", - "selectShell": "בחר מעטפת", - "bash": "לַחֲבוֹט", - "sh": "ש", - "ash": "אֵפֶר", - "connect": "לְחַבֵּר", - "disconnect": "לְנַתֵק", - "notConnected": "לא מחובר", - "clickToConnect": "לחץ על התחבר כדי להתחיל סשן מעטפת", - "connectingTo": "מתחבר אל {{containerName}}...", - "containerNotFound": "המיכל לא נמצא", - "backToList": "חזרה לרשימה", - "logs": "יומני רישום", - "stats": "סטטיסטיקות", - "consoleTab": "לְנַחֵם", - "startContainerToAccess": "הפעל את המכולה כדי לגשת לקונסולה" + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "כְּלָלִי", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "משתמשים", - "sectionSessions": "מפגשים", - "sectionRoles": "תפקידים", - "sectionDatabase": "מסד נתונים", - "sectionApiKeys": "מפתחות API", - "allowRegistration": "אפשר רישום משתמש", - "allowRegistrationDesc": "אפשרו למשתמשים חדשים להירשם באופן עצמאי", - "allowPasswordLogin": "אפשר כניסה עם סיסמה", - "allowPasswordLoginDesc": "שם משתמש/סיסמה להתחברות", - "oidcAutoProvision": "הקצאה אוטומטית של OIDC", - "oidcAutoProvisionDesc": "יצירה אוטומטית של חשבונות עבור משתמשי OIDC גם כאשר ההרשמה מושבתת", - "allowPasswordReset": "אפשר איפוס סיסמה", - "allowPasswordResetDesc": "איפוס קוד דרך יומני Docker", - "sessionTimeout": "פסק זמן של סשן", - "hours": "שעות", - "sessionTimeoutRange": "מינימום שעה · מקסימום 720 שעות", - "monitoringDefaults": "ניטור ברירות מחדל", - "statusCheck": "בדיקת סטטוס", - "metrics": "מדדים", - "sec": "שניות", - "logLevel": "רמת יומן", - "enableGuacamole": "הפעל גוואקמולי", - "enableGuacamoleDesc": "שולחן עבודה מרוחק RDP/VNC", - "guacdUrl": "כתובת אתר של guacd", - "oidcDescription": "הגדר את OpenID Connect עבור SSO. שדות המסומנים ב-* הם שדות חובה.", - "oidcClientId": "מזהה לקוח", - "oidcClientSecret": "סוד הלקוח", - "oidcAuthUrl": "כתובת אתר להרשאה", - "oidcIssuerUrl": "כתובת URL של המנפיק", - "oidcTokenUrl": "כתובת אתר של אסימון", - "oidcUserIdentifier": "נתיב מזהה המשתמש", - "oidcDisplayName": "נתיב שם התצוגה", - "oidcScopes": "טווחים", - "oidcUserinfoUrl": "עקיפת כתובת URL של פרטי משתמש", - "oidcAllowedUsers": "משתמשים מורשים", - "oidcAllowedUsersDesc": "אימייל אחד בכל שורה. יש להשאיר ריק כדי לאפשר את כולם.", - "removeOidc": "לְהַסִיר", - "usersCount": "{{count}} משתמשים", - "createUser": "לִיצוֹר", - "newRole": "תפקיד חדש", - "roleName": "שֵׁם", - "roleDisplayName": "שם תצוגה", - "roleDescription": "תֵאוּר", - "rolesCount": "תפקידים {{count}}", - "createRole": "לִיצוֹר", - "creating": "יוצר...", - "exportDatabase": "ייצוא מסד נתונים", - "exportDatabaseDesc": "הורד גיבוי של כל המארחים, האישורים וההגדרות", - "export": "יְצוּא", - "exporting": "מייצא...", - "importDatabase": "ייבוא מסד נתונים", - "importDatabaseDesc": "שחזור מקובץ גיבוי .sqlite", - "importDatabaseSelected": "נבחר: {{name}}", - "selectFile": "בחר קובץ", - "changeFile": "לְשַׁנוֹת", - "import": "יְבוּא", - "importing": "מייבא...", - "apiKeysCount": "מקשים {{count}}", - "newApiKey": "מפתח API חדש", - "apiKeyCreatedWarning": "המפתח נוצר - העתק אותו עכשיו, הוא לא יוצג שוב.", - "apiKeyName": "שֵׁם", - "apiKeyUser": "מִשׁתַמֵשׁ", - "apiKeySelectUser": "בחר משתמש...", - "apiKeyExpiresAt": "פג תוקף ב", - "createKey": "צור מפתח", - "apiKeyNoExpiry": "אין תפוגה", - "revokedBadge": "בוטל", - "authTypeDual": "אימות כפול", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "מְקוֹמִי", - "adminStatusAdministrator": "מְנַהֵל", - "adminStatusRegularUser": "משתמש רגיל", - "adminBadge": "אדמין", - "systemBadge": "מערכת", - "customBadge": "מִנְהָג", - "youBadge": "אַתָה", - "sessionsActive": "{{count}} פעיל", - "sessionActive": "פעיל: {{time}}", - "sessionExpires": "תוקף: {{time}}", - "revokeAll": "כֹּל", - "revokeAllSessionsSuccess": "כל ההפעלות עבור המשתמש בוטלו", - "revokeAllSessionsFailed": "נכשלה ביטול ההפעלות", - "revokeSessionFailed": "ביטול ההפעלה נכשל", - "addRole": "הוסף תפקיד", - "noCustomRoles": "לא הוגדרו תפקידים מותאמים אישית", - "removeRoleFailed": "הסרת התפקיד נכשלה", - "assignRoleFailed": "הקצאת התפקיד נכשלה", - "deleteRoleFailed": "מחיקת התפקיד נכשלה", - "userAdminAccess": "מְנַהֵל", - "userAdminAccessDesc": "גישה מלאה לכל הגדרות המנהל", - "userRoles": "תפקידים", - "revokeAllUserSessions": "ביטול כל ההפעלות", - "revokeAllUserSessionsDesc": "כפיית כניסה מחדש בכל המכשירים", - "revoke": "לְבַטֵל", - "deleteUserWarning": "מחיקת משתמש זה היא לצמיתות.", - "deleteUser": "מחק {{username}}", - "deleting": "מוחק...", - "deleteUserFailed": "מחיקת המשתמש נכשלה", - "deleteUserSuccess": "המשתמש \"{{username}}\" נמחק", - "deleteRoleSuccess": "התפקיד \"{{name}}\" נמחק", - "revokeKeySuccess": "המפתח \"{{name}}\" בוטל", - "revokeKeyFailed": "ביטול המפתח נכשל", - "copiedToClipboard": "הועתק ללוח", - "done": "נַעֲשָׂה", - "createUserTitle": "צור משתמש", - "createUserDesc": "צור חשבון מקומי חדש.", - "createUserUsername": "שם משתמש", - "createUserPassword": "סִיסמָה", - "createUserPasswordHint": "מינימום 6 תווים.", - "createUserEnterUsername": "הזן שם משתמש", - "createUserEnterPassword": "הזן סיסמה", - "createUserSubmit": "צור משתמש", - "editUserTitle": "ניהול משתמש: {{username}}", - "editUserDesc": "עריכת תפקידים, סטטוס מנהל, הפעלות והגדרות חשבון.", - "editUserUsername": "שם משתמש", - "editUserAuthType": "סוג אימות", - "editUserAdminStatus": "סטטוס מנהל", - "editUserUserId": "מזהה משתמש", - "linkAccountTitle": "קישור OIDC לחשבון סיסמה", - "linkAccountDesc": "מזג את חשבון OIDC {{username}} עם חשבון מקומי קיים.", - "linkAccountWarningTitle": "זה יעשה את הדברים הבאים:", - "linkAccountEffect1": "מחיקת חשבון OIDC בלבד", - "linkAccountEffect2": "הוסף שם משתמש של OIDC לחשבון היעד", - "linkAccountEffect3": "אפשר כניסה באמצעות OIDC וסיסמה", - "linkAccountTargetUsername": "שם משתמש יעד", - "linkAccountTargetPlaceholder": "הזן את שם המשתמש של החשבון המקומי שאליו ברצונך לקשר", - "linkAccounts": "קישור חשבונות", - "linkAccountSuccess": "חשבון OIDC מקושר אל \"{{username}}\"", - "linkAccountFailed": "קישור חשבון OIDC נכשל", - "linkAccountInProgress": "מְקַשֵׁר...", - "saving": "חִסָכוֹן...", - "updateRegistrationFailed": "עדכון הגדרות הרישום נכשל", - "updatePasswordLoginFailed": "נכשל עדכון הגדרות הכניסה באמצעות סיסמה", - "updateOidcAutoProvisionFailed": "נכשל עדכון הגדרת הקצאת הנתונים האוטומטית של OIDC", - "updatePasswordResetFailed": "נכשל עדכון הגדרת איפוס הסיסמה", - "sessionTimeoutRange2": "זמן הקצוב לסשן חייב להיות בין שעה ל-720 שעות", - "sessionTimeoutSaved": "פסק זמן של סשן נשמר", - "sessionTimeoutSaveFailed": "נכשלה שמירת זמן קצוב של סשן", - "monitoringIntervalInvalid": "ערכי מרווח לא חוקיים", - "monitoringSaved": "הגדרות הניטור נשמרו", - "monitoringSaveFailed": "שמירת הגדרות הניטור נכשלה", - "guacamoleSaved": "הגדרות הגוואקמולי נשמרו", - "guacamoleSaveFailed": "שמירת הגדרות הגוואקמולי נכשלה", - "guacamoleUpdateFailed": "נכשל עדכון הגדרת הגוואקמולי", - "logLevelUpdateFailed": "נכשל עדכון רמת היומן", - "oidcSaved": "תצורת OIDC נשמרה", - "oidcSaveFailed": "שמירת תצורת OIDC נכשלה", - "oidcRemoved": "תצורת OIDC הוסרה", - "oidcRemoveFailed": "הסרת תצורת OIDC נכשלה", - "createUserRequired": "נדרשים שם משתמש וסיסמה", - "createUserPasswordTooShort": "הסיסמה חייבת להיות באורך של לפחות 6 תווים", - "createUserSuccess": "משתמש \"{{username}}\" נוצר", - "createUserFailed": "יצירת המשתמש נכשלה", - "updateAdminStatusFailed": "נכשל עדכון סטטוס מנהל המערכת", - "allSessionsRevoked": "כל הפעילויות בוטלו", - "revokeSessionsFailed": "נכשלה ביטול ההפעלות", - "createRoleRequired": "חובה להזין שם ושם תצוגה", - "createRoleSuccess": "התפקיד \"{{name}}\" נוצר", - "createRoleFailed": "יצירת התפקיד נכשלה", - "apiKeyNameRequired": "נדרש שם מפתח", - "apiKeyUserRequired": "נדרש מזהה משתמש", - "apiKeyCreatedSuccess": "מפתח ה-API \"{{name}}\" נוצר", - "apiKeyCreateFailed": "נכשלה יצירת מפתח ה-API", - "exportSuccess": "מסד הנתונים יוצא בהצלחה", - "exportFailed": "ייצוא מסד הנתונים נכשל", - "importSelectFile": "אנא בחר קובץ תחילה", - "importCompleted": "ייבוא הושלם: {{total}} פריטים יובאו, {{skipped}} דילג", - "importFailed": "ייבוא נכשל: {{error}}", - "importError": "ייבוא מסד הנתונים נכשל" + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "מְאָרֵחַ", - "hostPlaceholder": "192.168.1.1 או example.com", - "portLabel": "נָמָל", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "שם משתמש", - "usernamePlaceholder": "שם משתמש", - "authLabel": "אישור", - "passwordLabel": "סִיסמָה", - "passwordPlaceholder": "סִיסמָה", - "privateKeyLabel": "מפתח פרטי", - "privateKeyPlaceholder": "הדבק מפתח פרטי...", - "credentialLabel": "תְעוּדָה", - "credentialPlaceholder": "בחר אישור שמור", - "connectToTerminal": "התחברות לטרמינל", - "connectToFiles": "התחברות לקבצים" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "לא נבחר טרמינל", - "noTerminalSelectedHint": "פתח כרטיסיית מסוף SSH כדי להציג את היסטוריית הפקודות שלה", - "searchPlaceholder": "היסטוריית חיפוש...", - "clearAll": "נקה הכל", - "noHistoryEntries": "אין רשומות היסטוריה", - "trackingDisabled": "מעקב אחר היסטוריה מושבת", - "trackingDisabledHint": "הפעל זאת בהגדרות הפרופיל שלך כדי להקליט פקודות." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "רישום מפתח", - "recordToTerminals": "הקלטה למסופים", - "selectAll": "כֹּל", - "selectNone": "אַף לֹא אֶחָד", - "noTerminalTabsOpen": "אין לשוניות מסוף פתוחות", - "selectTerminalsAbove": "בחר טרמינלים למעלה", - "broadcastInputPlaceholder": "הקלד כאן כדי לשדר הקשות מקלדת...", - "stopRecording": "הפסקת ההקלטה", - "startRecording": "התחל הקלטה", - "settingsTitle": "הגדרות", - "enableRightClickCopyPaste": "הפעלת העתקה/הדבקה באמצעות לחיצה ימנית" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "מַעֲרָך", - "selectLayoutAbove": "בחר פריסה למעלה", - "selectLayoutHint": "בחר כמה חלוניות להציג", - "panesTitle": "חלוניות", - "openTabsTitle": "פתח כרטיסיות", - "dragTabsHint": "גררו כרטיסיות לחלוניות למעלה, או השתמשו באפשרות 'הקצאה מהירה'", - "dropHere": "זרוק כאן", - "emptyPane": "רֵיק", - "dashboard": "לוּחַ מַחווָנִים", - "clearSplitScreen": "נקה מסך מפוצל", - "quickAssign": "הקצאה מהירה", - "alreadyAssigned": "חלונית {{index}}", - "splitTab": "פיצול טאב", - "addToSplit": "הוסף לפיצול", - "removeFromSplit": "הסר מפיצול", - "assignToPane": "הקצאה לחלונית" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "צור קטע", - "createSnippetDescription": "צור קטע פקודה חדש לביצוע מהיר", - "nameLabel": "שֵׁם", - "namePlaceholder": "לדוגמה, הפעל מחדש את Nginx", - "descriptionLabel": "תֵאוּר", - "descriptionPlaceholder": "תיאור אופציונלי", - "optional": "אופציונלי", - "folderLabel": "תיקייה", - "noFolder": "אין תיקייה (ללא קטגוריה)", - "commandLabel": "פְּקוּדָה", - "commandPlaceholder": "לדוגמה, sudo systemctl הפעל מחדש את nginx", - "cancel": "לְבַטֵל", - "createSnippetButton": "צור קטע", - "createFolderTitle": "צור תיקייה", - "createFolderDescription": "ארגנו את הקטעים שלכם בתיקיות", - "folderNameLabel": "שם התיקייה", - "folderNamePlaceholder": "לדוגמה, פקודות מערכת, סקריפטים של Docker", - "folderColorLabel": "צבע התיקייה", - "folderIconLabel": "סמל תיקייה", - "previewLabel": "תצוגה מקדימה", - "folderNameFallback": "שם התיקייה", - "createFolderButton": "צור תיקייה", - "targetTerminals": "טרמינלים של טארגט", - "selectAll": "כֹּל", - "selectNone": "אַף לֹא אֶחָד", - "noTerminalTabsOpen": "אין לשוניות מסוף פתוחות", - "searchPlaceholder": "חיפוש קטעי טקסט...", - "newSnippet": "קטע חדש", - "newFolder": "תיקייה חדשה", - "run": "לָרוּץ", - "noSnippetsInFolder": "אין קטעי טקסט בתיקייה זו", - "uncategorized": "ללא קטגוריה", - "editSnippetTitle": "עריכת קטע", - "editSnippetDescription": "עדכן את קטע הפקודה הזה", - "saveSnippetButton": "שמירת שינויים", - "createSuccess": "קטע הקוד נוצר בהצלחה", - "createFailed": "נכשלה יצירת קטע הקוד", - "updateSuccess": "קטע הקוד עודכן בהצלחה", - "updateFailed": "נכשל עדכון הקטע", - "deleteFailed": "מחיקת קטע הקוד נכשלה", - "folderCreateSuccess": "תיקייה נוצרה בהצלחה", - "folderCreateFailed": "יצירת התיקייה נכשלה", - "editFolderTitle": "עריכת תיקייה", - "editFolderDescription": "שינוי שם או שינוי מראה של תיקייה זו", - "saveFolderButton": "שמירת שינויים", - "editFolder": "עריכת תיקייה", - "deleteFolder": "מחיקת תיקייה", - "folderDeleteSuccess": "תיקייה \"{{name}}\" נמחקה", - "folderDeleteFailed": "מחיקת התיקייה נכשלה", - "folderEditSuccess": "התיקייה עודכנה בהצלחה", - "folderEditFailed": "עדכון התיקייה נכשל", - "confirmRunMessage": "להפעיל את \"{{name}}\"?", - "confirmRunButton": "לָרוּץ", - "runSuccess": "הריץ את \"{{name}}\" בטרמינל/ים {{count}}", - "copySuccess": "הועתק \"{{name}}\" ללוח", - "shareTitle": "שתף קטע", - "shareUser": "מִשׁתַמֵשׁ", - "shareRole": "תַפְקִיד", - "selectUser": "בחר משתמש...", - "selectRole": "בחר תפקיד...", - "shareSuccess": "קטע הטקסט שותף בהצלחה", - "shareFailed": "נכשל שיתוף הקטע", - "revokeSuccess": "הגישה בוטלה", - "revokeFailed": "ביטול הגישה נכשל", - "currentAccess": "גישה נוכחית", - "shareLoadError": "טעינת נתוני השיתוף נכשלה", - "loading": "טְעִינָה...", - "close": "לִסְגוֹר", - "reorderFailed": "שמירת סדר הקטעים נכשלה" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "חֶשְׁבּוֹן", - "sectionAppearance": "הוֹפָעָה", - "sectionSecurity": "בִּטָחוֹן", - "sectionApiKeys": "מפתחות API", - "sectionC2sTunnels": "מנהרות C2S", - "usernameLabel": "שם משתמש", - "roleLabel": "תַפְקִיד", - "roleAdministrator": "מְנַהֵל", - "authMethodLabel": "שיטת אימות", - "authMethodLocal": "מְקוֹמִי", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "עַל", - "twoFaOff": "כבוי", - "versionLabel": "גִרְסָה", - "deleteAccount": "מחיקת חשבון", - "deleteAccountDescription": "מחיקת החשבון שלך לצמיתות", - "deleteButton": "לִמְחוֹק", - "deleteAccountPermanent": "פעולה זו היא קבועה ולא ניתן לבטלה.", - "deleteAccountWarning": "כל ההפעלות, המארחים, האישורים וההגדרות יימחקו לצמיתות.", - "confirmPasswordDeletePlaceholder": "הזן את הסיסמה שלך כדי לאשר", - "languageLabel": "שָׂפָה", - "themeLabel": "נוֹשֵׂא", - "fontSizeLabel": "גודל גופן", - "accentColorLabel": "צבע מבטא", - "settingsTerminal": "מָסוֹף", - "commandAutocomplete": "השלמה אוטומטית של הפקודה", - "commandAutocompleteDesc": "הצג השלמה אוטומטית בעת הקלדה", - "historyTracking": "מעקב אחר היסטוריה", - "historyTrackingDesc": "פקודות מסוף מעקב", - "syntaxHighlighting": "הדגשת תחביר", - "syntaxHighlightingDesc": "הדגש את פלט הטרמינל", - "commandPalette": "לוח פקודות", - "commandPaletteDesc": "הפעל קיצור מקלדת", - "reopenTabsOnLogin": "פתח מחדש כרטיסיות בעת הכניסה", - "reopenTabsOnLoginDesc": "שחזר את הכרטיסיות הפתוחות שלך בעת כניסה או רענון הדף, אפילו ממכשיר אחר", - "confirmTabClose": "אישור סגירת כרטיסייה", - "confirmTabCloseDesc": "שאל לפני סגירת כרטיסיות הטרמינל", - "settingsSidebar": "סרגל צד", - "showHostTags": "הצג תגיות מארח", - "showHostTagsDesc": "הצגת תגיות ברשימת המארחים", - "hostTrayOnClick": "לחץ להרחבת פעולות מארח", - "hostTrayOnClickDesc": "הצג תמיד כפתורי חיבור; לחץ כדי להרחיב את אפשרויות הניהול במקום לרחף מעל העכבר", - "pinAppRail": "אפליקציית Pin Rail", - "pinAppRailDesc": "השאר את פס האפליקציה של הצד השמאלי פתוח תמיד במקום להרחיב בעת ריחוף", - "settingsSnippets": "קטעי טקסט", - "foldersCollapsed": "תיקיות מכווצות", - "foldersCollapsedDesc": "כיווץ תיקיות כברירת מחדל", - "confirmExecution": "אישור ביצוע", - "confirmExecutionDesc": "אשר לפני הרצת קטעי טקסט", - "settingsUpdates": "עדכונים", - "disableUpdateChecks": "השבת בדיקות עדכונים", - "disableUpdateChecksDesc": "תפסיקו לבדוק עדכונים", - "totpAuthenticator": "מאמת TOTP", - "totpEnabled": "2FA מופעל", - "totpDisabled": "הוסף אבטחת התחברות נוספת", - "disable": "השבת", - "enable": "לְאַפשֵׁר", - "setupTotp": "הגדר TOTP", - "qrCode": "קוד QR", - "totpInstructions": "סרוק את קוד ה-QR או הזן את הקוד הסודי באפליקציית האימות שלך, לאחר מכן הזן את הקוד בן 6 הספרות", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "לְאַמֵת", - "changePassword": "שינוי סיסמה", - "currentPasswordLabel": "סיסמה נוכחית", - "currentPasswordPlaceholder": "סיסמה נוכחית", - "newPasswordLabel": "סיסמה חדשה", - "newPasswordPlaceholder": "סיסמה חדשה", - "confirmPasswordLabel": "אשר סיסמה חדשה", - "confirmPasswordPlaceholder": "אשר סיסמה חדשה", - "updatePassword": "עדכון סיסמה", - "createApiKeyTitle": "צור מפתח API", - "createApiKeyDescription": "צור מפתח API חדש עבור גישה תכנותית.", - "apiKeyNameLabel": "שֵׁם", - "apiKeyNamePlaceholder": "לדוגמה צינור CI", - "expiryDateLabel": "תאריך תפוגה", - "optional": "אופציונלי", - "cancel": "לְבַטֵל", - "createKey": "צור מפתח", - "apiKeyCount": "מקשים {{count}}", - "newKey": "מפתח חדש", - "noApiKeys": "עדיין אין מפתחות API.", - "apiKeyActive": "פָּעִיל", - "apiKeyUsageHint": "כלול את המפתח שלך ב-", - "apiKeyUsageHintHeader": "כּוֹתֶרֶת.", - "apiKeyPermissionsHint": "מפתחות יורשים את ההרשאות של המשתמש היוצר.", - "roleUser": "מִשׁתַמֵשׁ", - "authMethodDual": "אימות כפול", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "נכשל בהתחלת הגדרת TOTP", - "totpEnter6Digits": "הזן קוד בן 6 ספרות", - "totpEnabledSuccess": "אימות דו-שלבי מופעל", - "totpInvalidCode": "קוד לא תקין, אנא נסה שוב", - "totpDisableInputRequired": "הזן את קוד ה-TOTP או הסיסמה שלך", - "totpDisabledSuccess": "אימות דו-שלבי מושבת", - "totpDisableFailed": "נכשל בהשבתת 2FA", - "totpDisableTitle": "השבת 2FA", - "totpDisablePlaceholder": "הזן קוד או סיסמה של TOTP", - "totpDisableConfirm": "השבת 2FA", - "totpContinueVerify": "המשך לאימות", - "totpVerifyTitle": "אימות קוד", - "totpBackupTitle": "קודי גיבוי", - "totpDownloadBackup": "הורד קודי גיבוי", - "done": "נַעֲשָׂה", - "secretCopied": "סוד הועתק ללוח", - "apiKeyNameRequired": "נדרש שם מפתח", - "apiKeyCreated": "מפתח ה-API \"{{name}}\" נוצר", - "apiKeyCreateFailed": "נכשלה יצירת מפתח ה-API", - "apiKeyUser": "מִשׁתַמֵשׁ", - "apiKeyExpires": "פג תוקף", - "apiKeyRevoked": "מפתח ה-API \"{{name}}\" בוטל", - "apiKeyRevokeFailed": "נכשל בביטול מפתח ה-API", - "passwordFieldsRequired": "נדרשות סיסמאות נוכחיות וחדשות", - "passwordMismatch": "הסיסמאות אינן תואמות", - "passwordTooShort": "הסיסמה חייבת להיות באורך של לפחות 6 תווים", - "passwordUpdated": "הסיסמה עודכנה בהצלחה", - "passwordUpdateFailed": "עדכון הסיסמה נכשל", - "deletePasswordRequired": "נדרשת סיסמה כדי למחוק את החשבון שלך", - "deleteFailed": "מחיקת החשבון נכשלה", - "deleting": "מוחק...", - "colorPickerTooltip": "פתח את בורר הצבעים", - "themeSystem": "מַעֲרֶכֶת", - "themeLight": "אוֹר", - "themeDark": "כֵּהֶה", - "themeDracula": "דרקולה", - "themeCatppuccin": "קאטפוצ'ין", - "themeNord": "נורד", - "themeSolarized": "סולארי", - "themeTokyoNight": "ליל טוקיו", - "themeOneDark": "אחד כהה", - "themeGruvbox": "גראבבוקס" + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/hi_IN.json b/src/ui/locales/translated/hi_IN.json index 4eb57bb7..9bc5af32 100644 --- a/src/ui/locales/translated/hi_IN.json +++ b/src/ui/locales/translated/hi_IN.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "फ़ोल्डर", - "folder": "फ़ोल्डर", - "password": "पासवर्ड", - "key": "चाबी", - "sshPrivateKey": "एसएसएच निजी कुंजी", - "upload": "अपलोड करें", - "keyPassword": "कुंजी पासवर्ड", - "sshKey": "एसएसएच कुंजी", - "uploadPrivateKeyFile": "निजी कुंजी फ़ाइल अपलोड करें", - "searchCredentials": "खोज क्रेडेंशियल...", - "addCredential": "क्रेडेंशियल जोड़ें", - "caCertificate": "सीए प्रमाणपत्र (-cert.pub)", - "caCertificateDescription": "वैकल्पिक: सीए द्वारा हस्ताक्षरित प्रमाणपत्र फ़ाइल अपलोड करें या पेस्ट करें (उदाहरण के लिए id_ed25519-cert.pub)। यह तब आवश्यक है जब आपका एसएसएच सर्वर प्रमाणपत्र-आधारित प्रमाणीकरण का उपयोग करता हो।", - "uploadCertFile": "-cert.pub फ़ाइल अपलोड करें", - "clearCert": "स्पष्ट", - "certLoaded": "प्रमाणपत्र लोड हो गया", - "certPublicKeyLabel": "सीए प्रमाणपत्र", - "certTypeLabel": "सर्टिफिकेट टाइप", - "pasteOrUploadCert": "-cert.pub प्रमाणपत्र पेस्ट करें या अपलोड करें...", - "hasCaCert": "सीए प्रमाणपत्र प्राप्त है", - "noCaCert": "सीए प्रमाणपत्र की आवश्यकता नहीं है" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "अलर्ट लोड करने में विफल", - "failedToDismissAlert": "अलर्ट को खारिज करने में विफल" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "सर्वर कॉन्फ़िगरेशन", - "description": "अपने बैकएंड सेवाओं से कनेक्ट करने के लिए टर्मिक्स सर्वर यूआरएल को कॉन्फ़िगर करें।", - "serverUrl": "सर्वर यूआरएल", - "enterServerUrl": "कृपया सर्वर URL दर्ज करें", - "saveFailed": "कॉन्फ़िगरेशन सहेजने में विफल", - "saveError": "कॉन्फ़िगरेशन सहेजने में त्रुटि", - "saving": "सहेजा जा रहा है...", - "saveConfig": "कॉन्फ़िगरेशन सहेजें", - "helpText": "वह URL दर्ज करें जहां आपका टर्मिक्स सर्वर चल रहा है (उदाहरण के लिए, http://localhost:30001 या https://your-server.com)", - "changeServer": "सर्वर बदलें", - "mustIncludeProtocol": "सर्वर URL की शुरुआत http:// या https:// से होनी चाहिए।", - "allowInvalidCertificate": "अमान्य प्रमाणपत्र की अनुमति दें", - "allowInvalidCertificateDesc": "इसका उपयोग केवल विश्वसनीय सेल्फ-होस्टेड सर्वरों के लिए करें जिनमें सेल्फ-साइन किए गए या आईपी-एड्रेस प्रमाणपत्र हों।", - "useEmbedded": "स्थानीय सर्वर का उपयोग करें", - "embeddedDesc": "Termix को बिल्ट-इन लोकल सर्वर के साथ चलाएं (रिमोट सर्वर की आवश्यकता नहीं है)", - "embeddedConnecting": "स्थानीय सर्वर से कनेक्ट हो रहा है...", - "embeddedNotReady": "स्थानीय सर्वर अभी तैयार नहीं है। कृपया थोड़ी देर प्रतीक्षा करें और पुनः प्रयास करें।", - "localServer": "स्थानीय सर्वर" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "संस्करण जाँच त्रुटि", - "checkFailed": "अपडेट की जाँच करने में विफल", - "upToDate": "ऐप अपडेटेड है", - "currentVersion": "आप संस्करण {{version}} चला रहे हैं", - "updateAvailable": "उपलब्ध अद्यतन", - "newVersionAvailable": "एक नया संस्करण उपलब्ध है! आप {{current}}चला रहे हैं, लेकिन {{latest}} भी उपलब्ध है।", - "betaVersion": "बीटा संस्करण", - "betaVersionDesc": "आप {{current}}चला रहे हैं, जो नवीनतम स्थिर रिलीज़ {{latest}} से नया है।", - "releasedOn": "{{date}} को जारी किया गया", - "downloadUpdate": "अपडेट डाउनलोड करें", - "checking": "अपडेट के लिए जांच कर रहा है...", - "checkUpdates": "अद्यतन के लिए जाँच", - "checkingUpdates": "अपडेट के लिए जांच कर रहा है...", - "updateRequired": "अद्यतन आवश्यक है" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "बंद करना", - "minimize": "छोटा करना", - "online": "ऑनलाइन", - "offline": "ऑफलाइन", - "continue": "जारी रखना", - "maintenance": "रखरखाव", - "degraded": "अपमानित", - "error": "गलती", - "warning": "चेतावनी", - "unsavedChanges": "असहेजित परिवर्तन", - "dismiss": "नकार देना", - "loading": "लोड हो रहा है...", - "optional": "वैकल्पिक", - "connect": "जोड़ना", - "copied": "कॉपी किया गया", - "connecting": "कनेक्ट हो रहा है...", - "updateAvailable": "उपलब्ध अद्यतन", - "appName": "टर्मिक्स", - "openInNewTab": "वेब टेब में खोलें", - "noReleases": "कोई रिलीज़ नहीं", - "updatesAndReleases": "अपडेट और रिलीज़", - "newVersionAvailable": "एक नया संस्करण ({{version}}) उपलब्ध है।", - "failedToFetchUpdateInfo": "अद्यतन जानकारी प्राप्त करने में विफल", - "preRelease": "पूर्व-रिलीज़", - "noReleasesFound": "कोई रिलीज़ नहीं मिली।", - "cancel": "रद्द करना", - "username": "उपयोगकर्ता नाम", - "login": "लॉग इन करें", - "register": "पंजीकरण करवाना", - "password": "पासवर्ड", - "confirmPassword": "पासवर्ड की पुष्टि कीजिये", - "back": "पीछे", - "save": "बचाना", - "saving": "सहेजा जा रहा है...", - "delete": "मिटाना", - "rename": "नाम बदलें", - "edit": "संपादन करना", - "add": "जोड़ना", - "confirm": "पुष्टि करना", - "no": "नहीं", - "or": "या", - "next": "अगला", - "previous": "पहले का", - "refresh": "ताज़ा करना", - "language": "भाषा", - "checking": "जाँच चल रही है...", - "checkingDatabase": "डेटाबेस कनेक्शन की जाँच की जा रही है...", - "checkingAuthentication": "प्रमाणीकरण की जाँच की जा रही है...", - "backendReconnected": "सर्वर कनेक्शन बहाल हो गया", - "connectionDegraded": "सर्वर कनेक्शन टूट गया, पुनः प्राप्त किया जा रहा है…", - "reload": "पुनः लोड करें", - "remove": "निकालना", - "create": "बनाएं", - "update": "अद्यतन", - "copy": "प्रतिलिपि", - "copyFailed": "क्लिपबोर्ड पर कॉपी करने में विफल", - "maximize": "अधिकतम", - "restore": "पुनर्स्थापित करना", - "of": "का" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "घर", - "terminal": "टर्मिनल", - "docker": "डाक में काम करनेवाला मज़दूर", - "tunnels": "सुरंगों", - "fileManager": "फ़ाइल मैनेजर", - "serverStats": "सर्वर आँकड़े", - "admin": "व्यवस्थापक", - "userProfile": "उपयोगकर्ता रूपरेखा", - "splitScreen": "स्प्लिट स्क्रीन", - "confirmClose": "क्या आप इस सक्रिय सत्र को बंद करना चाहते हैं?", - "close": "बंद करना", - "cancel": "रद्द करना", - "sshManager": "एसएसएच प्रबंधक", - "cannotSplitTab": "इस टैब को विभाजित नहीं किया जा सकता", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "पासवर्ड कॉपी करें", - "copySudoPassword": "सूडो पासवर्ड कॉपी करें", - "passwordCopied": "पासवर्ड क्लिपबोर्ड पर कॉपी हो गया है", - "noPasswordAvailable": "कोई पासवर्ड उपलब्ध नहीं है", - "failedToCopyPassword": "पासवर्ड कॉपी करने में विफल", - "refreshTab": "कनेक्शन रीफ़्रेश करें", - "openFileManager": "फ़ाइल प्रबंधक खोलें", - "dashboard": "डैशबोर्ड", - "networkGraph": "नेटवर्क ग्राफ", - "quickConnect": "त्वरित कनेक्ट", - "sshTools": "एसएसएच उपकरण", - "history": "इतिहास", - "hosts": "मेजबान", - "snippets": "स्निपेट्स", - "hostManager": "मेजबान प्रबंधक", - "credentials": "साख", - "connections": "कनेक्शन", - "roleAdministrator": "प्रशासक", - "roleUser": "उपयोगकर्ता" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "मेजबान", - "noHosts": "कोई एसएसएच होस्ट नहीं", - "retry": "पुन: प्रयास करें", - "refresh": "ताज़ा करना", - "optional": "वैकल्पिक", - "downloadSample": "नमूना डाउनलोड करें", - "failedToDeleteHost": "{{name}} को हटाने में विफल", - "importSkipExisting": "आयात करें (मौजूदा को छोड़ दें)", - "connectionDetails": "कनेक्शन विवरण", - "ssh": "एसएसएच", - "telnet": "टेलनेट", - "remoteDesktop": "दूरवर्ती डेस्कटॉप", - "port": "पत्तन", - "username": "उपयोगकर्ता नाम", - "folder": "फ़ोल्डर", - "tags": "टैग", - "pin": "नत्थी करना", - "addHost": "होस्ट जोड़ें", - "editHost": "होस्ट संपादित करें", - "cloneHost": "क्लोन होस्ट", - "enableTerminal": "टर्मिनल सक्षम करें", - "enableTunnel": "सुरंग सक्षम करें", - "enableFileManager": "फ़ाइल प्रबंधक को सक्षम करें", - "enableDocker": "डॉकर को सक्षम करें", - "defaultPath": "डिफ़ॉल्ट पथ", - "connection": "संबंध", - "upload": "अपलोड करें", - "authentication": "प्रमाणीकरण", - "password": "पासवर्ड", - "key": "चाबी", - "credential": "क्रेडेंशियल", - "none": "कोई नहीं", - "sshPrivateKey": "एसएसएच निजी कुंजी", - "keyType": "कुंजी प्रकार", - "uploadFile": "फ़ाइल अपलोड करें", - "tabGeneral": "सामान्य", - "tabSsh": "एसएसएच", - "tabRdp": "आरडीपी", - "tabVnc": "वीएनसी", - "tabTunnels": "सुरंगों", - "tabDocker": "डाक में काम करनेवाला मज़दूर", - "tabFiles": "फ़ाइलें", - "tabStats": "आँकड़े", - "tabTelnet": "टेलनेट", - "tabSharing": "शेयरिंग", - "tabAuthentication": "प्रमाणीकरण", - "terminal": "टर्मिनल", - "tunnel": "सुरंग", - "fileManager": "फ़ाइल मैनेजर", - "serverStats": "सर्वर आँकड़े", - "status": "स्थिति", - "folderRenamed": "फ़ोल्डर \"{{oldName}}\" का नाम बदलकर \"{{newName}}\" सफलतापूर्वक कर दिया गया है", - "failedToRenameFolder": "फ़ोल्डर का नाम बदलने में विफल", - "movedToFolder": "\"{{folder}} \" पर स्थानांतरित किया गया", - "editHostTooltip": "होस्ट संपादित करें", - "statusChecks": "स्थिति जांच", - "metricsCollection": "मैट्रिक्स संग्रह", - "metricsInterval": "मैट्रिक्स संग्रह अंतराल", - "metricsIntervalDesc": "सर्वर सांख्यिकी कितनी बार एकत्र करें (5 सेकंड - 1 घंटा)", - "behavior": "व्यवहार", - "themePreview": "थीम पूर्वावलोकन", - "theme": "विषय", - "fontFamily": "फुहारा परिवार", - "fontSize": "फ़ॉन्ट आकार", - "letterSpacing": "पत्र अंतराल", - "lineHeight": "ऊंची लाईन", - "cursorStyle": "कर्सर शैली", - "cursorBlink": "कर्सर ब्लिंक", - "scrollbackBuffer": "स्क्रॉलबैक बफर", - "bellStyle": "घंटी शैली", - "rightClickSelectsWord": "दायाँ क्लिक करने पर वर्ड का चयन होता है", - "fastScrollModifier": "फास्ट स्क्रॉल मॉडिफायर", - "fastScrollSensitivity": "तेज़ स्क्रॉल संवेदनशीलता", - "sshAgentForwarding": "एसएसएच एजेंट फ़ॉरवर्डिंग", - "backspaceMode": "बैकस्पेस मोड", - "startupSnippet": "स्टार्टअप स्निपेट", - "selectSnippet": "स्निपेट का चयन करें", - "forceKeyboardInteractive": "फ़ोर्स कीबोर्ड-इंटरैक्टिव", - "overrideCredentialUsername": "क्रेडेंशियल उपयोगकर्ता नाम को ओवरराइड करें", - "overrideCredentialUsernameDesc": "क्रेडेंशियल के उपयोगकर्ता नाम के बजाय ऊपर निर्दिष्ट उपयोगकर्ता नाम का उपयोग करें।", - "jumpHostChain": "जंप होस्ट चेन", - "portKnocking": "पोर्ट नॉकिंग", - "addKnock": "पोर्ट जोड़ें", - "addProxyNode": "नोड जोड़ें", - "proxyNode": "प्रॉक्सी नोड", - "proxyType": "प्रॉक्सी प्रकार", - "quickActions": "त्वरित कार्रवाइयां", - "sudoPasswordAutoFill": "सूडो पासवर्ड ऑटो-फिल", - "sudoPassword": "सूडो पासवर्ड", - "keepaliveInterval": "कीपअलाइव अंतराल (मिलीसेकंड)", - "moshCommand": "MOSH कमांड", - "environmentVariables": "पर्यावरण चर", - "addVariable": "चर जोड़ें", - "docker": "डाक में काम करनेवाला मज़दूर", - "copyTerminalUrl": "टर्मिनल यूआरएल कॉपी करें", - "copyFileManagerUrl": "फ़ाइल मैनेजर यूआरएल कॉपी करें", - "copyRemoteDesktopUrl": "रिमोट डेस्कटॉप यूआरएल कॉपी करें", - "failedToConnect": "कंसोल से कनेक्ट करने में विफल", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "start": "शुरू", - "enableStatusCheck": "स्थिति जांच सक्षम करें", - "enableMetrics": "मैट्रिक्स सक्षम करें", - "bulkUpdateFailed": "बल्क अपडेट विफल रहा", - "selectAll": "सबका चयन करें", - "deselectAll": "सबको अचयनित करो", - "protocols": "प्रोटोकॉल", - "secureShell": "सुरक्षित खोल", - "virtualNetwork": "वर्चुअल नेटवर्क", - "unencryptedShell": "अनएन्क्रिप्टेड शेल", - "addressIp": "पता / आईपी", - "friendlyName": "मित्रवत नाम", - "folderAndAdvanced": "फ़ोल्डर और उन्नत", - "privateNotes": "निजी नोट्स", - "privateNotesPlaceholder": "इस सर्वर के बारे में विवरण...", - "pinToTop": "शीर्ष पर पिन करें", - "pinToTopDesc": "इस होस्ट को हमेशा सूची में सबसे ऊपर दिखाएं", - "portKnockingSequence": "पोर्ट नॉकिंग अनुक्रम", - "addKnockBtn": "खटखटाहट जोड़ें", - "noPortKnocking": "पोर्ट नॉकिंग कॉन्फ़िगर नहीं किया गया है।", - "knockPort": "नॉक पोर्ट", - "protocol": "शिष्टाचार", - "delayAfterMs": "विलंब (मिलीसेकंड में)", - "useSocks5Proxy": "SOCKS5 प्रॉक्सी का उपयोग करें", - "useSocks5ProxyDesc": "प्रॉक्सी सर्वर के माध्यम से रूट कनेक्शन", - "proxyHost": "छद्म मेजबान", - "proxyPort": "प्रॉक्सी पोर्ट", - "proxyUsername": "प्रॉक्सी उपयोगकर्ता नाम", - "proxyPassword": "प्रॉक्सी पासवर्ड", - "proxySingleMode": "एकल प्रॉक्सी", - "proxyChainMode": "प्रॉक्सी श्रृंखला", - "you": "आप", - "jumpHostChainLabel": "जंप होस्ट चेन", - "addJumpBtn": "जंप जोड़ें", - "noJumpHosts": "कोई जंप होस्ट कॉन्फ़िगर नहीं किया गया है।", - "selectAServer": "एक सर्वर चुनें...", - "sshPort": "एसएसएच पोर्ट", - "authMethod": "प्रमाणीकरण विधि", - "storedCredential": "संग्रहीत क्रेडेंशियल", - "selectACredential": "एक क्रेडेंशियल चुनें...", - "keyTypeLabel": "कुंजी प्रकार", - "keyTypeAuto": "ऑटो का पता लगाने", - "keyPasteTab": "पेस्ट करें", - "keyUploadTab": "अपलोड करें", - "keyFileLoaded": "कुंजी फ़ाइल लोड हो गई", - "keyUploadClick": ".pem / .key / .ppk फ़ाइलें अपलोड करने के लिए क्लिक करें", - "clearKey": "साफ़ कुंजी", - "keySaved": "SSH कुंजी सहेजी गई", - "keyReplaceNotice": "इसे बदलने के लिए नीचे एक नई कुंजी पेस्ट करें", - "keyPassphraseSaved": "पासफ़्रेज़ सहेज लिया गया है, बदलने के लिए टाइप करें", - "replaceKey": "चाबी बदलें", - "forceKeyboardInteractiveLabel": "फोर्स कीबोर्ड इंटरेक्टिव", - "forceKeyboardInteractiveShortDesc": "कुंजी मौजूद होने पर भी पासवर्ड को मैन्युअल रूप से दर्ज करने के लिए बाध्य करें", - "terminalAppearance": "टर्मिनल उपस्थिति", - "colorTheme": "रंग थीम", - "fontFamilyLabel": "फुहारा परिवार", - "fontSizeLabel": "फ़ॉन्ट आकार", - "cursorStyleLabel": "कर्सर शैली", - "letterSpacingPx": "अक्षरों के बीच की दूरी (px)", - "lineHeightLabel": "ऊंची लाईन", - "bellStyleLabel": "घंटी शैली", - "backspaceModeLabel": "बैकस्पेस मोड", - "cursorBlinking": "कर्सर का झपकना", - "cursorBlinkingDesc": "टर्मिनल कर्सर के लिए ब्लिंकिंग एनीमेशन सक्षम करें", - "rightClickSelectsWordLabel": "दाएँ क्लिक करने पर Word का चयन होता है", - "rightClickSelectsWordShortDesc": "कर्सर के नीचे वाले शब्द को दाएँ क्लिक करके चुनें", - "behaviorAndAdvanced": "व्यवहार और उन्नत", - "scrollbackBufferLabel": "स्क्रॉलबैक बफर", - "scrollbackMaxLines": "इतिहास में रखी जाने वाली पंक्तियों की अधिकतम संख्या", - "sshAgentForwardingLabel": "एसएसएच एजेंट फ़ॉरवर्डिंग", - "sshAgentForwardingShortDesc": "अपनी स्थानीय SSH कुंजी इस होस्ट को भेजें", - "enableAutoMosh": "ऑटो-मोश को सक्षम करें", - "enableAutoMoshDesc": "यदि उपलब्ध हो तो SSH के बजाय Mosh को प्राथमिकता दें।", - "enableAutoTmux": "ऑटो-टीमक्स सक्षम करें", - "enableAutoTmuxDesc": "tmux सेशन को स्वचालित रूप से लॉन्च करें या उससे जुड़ें", - "sudoPasswordAutoFillLabel": "सूडो पासवर्ड ऑटो-फिल", - "sudoPasswordAutoFillShortDesc": "पूछे जाने पर स्वचालित रूप से sudo पासवर्ड प्रदान करें", - "sudoPasswordLabel": "सूडो पासवर्ड", - "environmentVariablesLabel": "पर्यावरण चर", - "addVariableBtn": "चर जोड़ें", - "noEnvVars": "कोई पर्यावरण चर कॉन्फ़िगर नहीं किया गया है।", - "fastScrollModifierLabel": "फास्ट स्क्रॉल मॉडिफायर", - "fastScrollSensitivityLabel": "तेज़ स्क्रॉल संवेदनशीलता", - "moshCommandLabel": "मोश कमांड", - "startupSnippetLabel": "स्टार्टअप स्निपेट", - "keepaliveIntervalLabel": "कीपअलाइव अंतराल (सेकंड)", - "maxKeepaliveMisses": "मैक्स कीपअलाइव चूक", - "tunnelSettings": "टनल सेटिंग्स", - "enableTunneling": "टनलिंग सक्षम करें", - "enableTunnelingDesc": "इस होस्ट के लिए SSH टनल कार्यक्षमता सक्षम करें", - "serverTunnelsSection": "सर्वर टनल", - "addTunnelBtn": "सुरंग जोड़ें", - "noTunnelsConfigured": "कोई सुरंगें कॉन्फ़िगर नहीं की गई हैं।", - "tunnelLabel": "सुरंग {{number}}", - "tunnelType": "सुरंग प्रकार", - "tunnelModeLocalDesc": "स्थानीय पोर्ट को रिमोट सर्वर (या उससे पहुंच योग्य होस्ट) पर स्थित पोर्ट पर फॉरवर्ड करें।", - "tunnelModeRemoteDesc": "रिमोट सर्वर पर मौजूद पोर्ट को अपनी मशीन के लोकल पोर्ट पर फॉरवर्ड करें।", - "tunnelModeDynamicDesc": "डायनामिक पोर्ट फॉरवर्डिंग के लिए लोकल पोर्ट पर SOCKS5 प्रॉक्सी बनाएं।", - "sameHost": "यह होस्ट (प्रत्यक्ष सुरंग)", - "endpointHost": "एंडपॉइंट होस्ट", - "endpointPort": "एंडपॉइंट पोर्ट", - "bindHost": "होस्ट को बांधें", - "sourcePort": "स्रोत पोर्ट", - "maxRetries": "अधिकतम पुनः प्रयास", - "retryIntervalS": "पुनः प्रयास अंतराल (सेकंड)", - "autoStartLabel": "ऑटो स्टार्ट", - "autoStartDesc": "होस्ट लोड होने पर यह टनल स्वचालित रूप से कनेक्ट हो जाएगी", - "tunnelConnecting": "सुरंग जोड़ने वाली...", - "tunnelDisconnected": "सुरंग का संपर्क टूट गया", - "failedToConnectTunnel": "जोडने में विफल", - "failedToDisconnectTunnel": "डिस्कनेक्ट करने में विफल", - "dockerIntegration": "डॉकर एकीकरण", - "enableDockerMonitor": "डॉकर को सक्षम करें", - "enableDockerMonitorDesc": "डॉकर के माध्यम से इस होस्ट पर कंटेनरों की निगरानी और प्रबंधन करें।", - "enableFileManagerMonitor": "फ़ाइल प्रबंधक को सक्षम करें", - "enableFileManagerMonitorDesc": "SFTP के माध्यम से इस होस्ट पर फ़ाइलों को ब्राउज़ करें और प्रबंधित करें।", - "defaultPathLabel": "डिफ़ॉल्ट पथ", - "fileManagerPathHint": "इस होस्ट के लिए फ़ाइल मैनेजर लॉन्च होने पर खुलने वाली निर्देशिका।", - "statusChecksLabel": "स्थिति जांच", - "enableStatusChecks": "स्थिति जांच सक्षम करें", - "enableStatusChecksDesc": "उपलब्धता की पुष्टि करने के लिए समय-समय पर इस होस्ट को पिंग करें।", - "useGlobalInterval": "वैश्विक अंतराल का उपयोग करें", - "useGlobalIntervalDesc": "सर्वर-व्यापी स्थिति जांच अंतराल के साथ ओवरराइड करें", - "checkIntervalS": "जाँच अंतराल(ओं)", - "checkIntervalDesc": "प्रत्येक कनेक्टिविटी पिंग के बीच सेकंड का अंतर", - "metricsCollectionLabel": "मैट्रिक्स संग्रह", - "enableMetricsLabel": "मैट्रिक्स सक्षम करें", - "enableMetricsDesc": "इस होस्ट से CPU, RAM, डिस्क और नेटवर्क उपयोग की जानकारी एकत्र करें।", - "useGlobalMetrics": "वैश्विक अंतराल का उपयोग करें", - "useGlobalMetricsDesc": "सर्वर-व्यापी मेट्रिक्स अंतराल के साथ ओवरराइड करें", - "metricsIntervalS": "मैट्रिक्स अंतराल (सेकंड)", - "metricsIntervalDesc2": "मीट्रिक स्नैपशॉट के बीच सेकंड", - "visibleWidgets": "दृश्यमान विजेट", - "cpuUsageLabel": "सीपीयू उपयोग", - "cpuUsageDesc": "सीपीयू प्रतिशत, लोड औसत, स्पार्कलाइन ग्राफ", - "memoryLabel": "स्मृति प्रयोग", - "memoryDesc": "रैम उपयोग, स्वैप, कैश किया गया", - "storageLabel": "डिस्क उपयोग", - "storageDesc": "प्रति माउंट पॉइंट डिस्क उपयोग", - "networkLabel": "नेटवर्क इंटरफेस", - "networkDesc": "इंटरफ़ेस सूची और बैंडविड्थ", - "uptimeLabel": "अपटाइम", - "uptimeDesc": "सिस्टम अपटाइम और बूट समय", - "systemInfoLabel": "व्यवस्था की सूचना", - "systemInfoDesc": "ऑपरेटिंग सिस्टम, कर्नेल, होस्टनाम, आर्किटेक्चर", - "recentLoginsLabel": "हाल की लॉगिन", - "recentLoginsDesc": "सफल और असफल लॉगिन इवेंट", - "topProcessesLabel": "शीर्ष प्रक्रियाएँ", - "topProcessesDesc": "पीआईडी, सीपीयू%, एमईएम%, कमांड", - "listeningPortsLabel": "श्रवण पोर्ट", - "listeningPortsDesc": "प्रक्रिया और स्थिति के साथ पोर्ट खोलें", - "firewallLabel": "फ़ायरवॉल", - "firewallDesc": "फ़ायरवॉल, ऐपआर्मर, SELinux स्थिति", - "quickActionsLabel": "त्वरित कार्रवाइयां", - "quickActionsToolbar": "त्वरित क्रियाएं सर्वर सांख्यिकी टूलबार में बटन के रूप में दिखाई देती हैं, जिन्हें एक क्लिक में निष्पादित किया जा सकता है।", - "noQuickActions": "अभी तक कोई त्वरित कार्रवाई नहीं की गई है।", - "buttonLabel": "बटन लेबल", - "selectSnippetPlaceholder": "अंश का चयन करें...", - "addActionBtn": "कार्रवाई जोड़ें", - "hostSharedSuccessfully": "होस्ट ने सफलतापूर्वक साझा किया", - "failedToShareHost": "होस्ट साझा करने में विफल", - "accessRevoked": "पहुँच रद्द कर दी गई", - "failedToRevokeAccess": "पहुँच रद्द करने में विफल", - "cancelBtn": "रद्द करना", - "savingBtn": "सहेजा जा रहा है...", - "addHostBtn": "होस्ट जोड़ें", - "hostUpdated": "होस्ट अपडेट किया गया", - "hostCreated": "होस्ट ने बनाया", - "failedToSave": "होस्ट को सहेजने में विफल", - "credentialUpdated": "क्रेडेंशियल अपडेट किया गया", - "credentialCreated": "क्रेडेंशियल बनाया गया", - "failedToSaveCredential": "क्रेडेंशियल सहेजने में विफल", - "backToHosts": "मेज़बानों पर वापस जाएँ", - "backToCredentials": "क्रेडेंशियल्स पर वापस जाएँ", - "pinned": "पिन की गई", - "noHostsFound": "कोई होस्ट नहीं मिला", - "tryDifferentTerm": "कोई दूसरा शब्द आजमाएँ", - "addFirstHost": "शुरुआत करने के लिए अपना पहला होस्ट जोड़ें", - "noCredentialsFound": "कोई क्रेडेंशियल नहीं मिला", - "addCredentialBtn": "क्रेडेंशियल जोड़ें", - "updateCredentialBtn": "क्रेडेंशियल अपडेट करें", - "features": "विशेषताएँ", - "noFolder": "(कोई फ़ोल्डर नहीं)", - "deleteSelected": "मिटाना", - "exitSelection": "निकास चयन", - "importSkip": "आयात करें (मौजूदा को छोड़ दें)", - "importOverwrite": "आयात (ओवरराइट)", - "collapseBtn": "गिर जाना", - "importExportBtn": "आयात / निर्यात", - "hostStatusesRefreshed": "होस्ट की स्थिति रीफ्रेश हो गई है", - "failedToRefreshHosts": "होस्ट को रीफ़्रेश करने में विफल", - "movedHostTo": "{{host}} को \"{{folder}} \" में स्थानांतरित किया गया", - "failedToMoveHost": "होस्ट को स्थानांतरित करने में विफल", - "folderRenamedTo": "फ़ोल्डर का नाम बदलकर \"{{name}} \" कर दिया गया है", - "deletedFolder": "फ़ोल्डर \"{{name}} \" हटा दिया गया", - "failedToDeleteFolder": "फ़ोल्डर को हटाने में विफल", - "deleteAllInFolder": "\"{{name}}\" में सभी होस्ट हटाएँ? इसे पूर्ववत नहीं किया जा सकता है।", - "deletedHost": "हटा दिया गया {{name}}", - "copiedToClipboard": "क्लिपबोर्ड पर कॉपी हो गया", - "terminalUrlCopied": "टर्मिनल यूआरएल कॉपी हो गया", - "fileManagerUrlCopied": "फ़ाइल मैनेजर यूआरएल कॉपी हो गया", - "tunnelUrlCopied": "टनल यूआरएल कॉपी किया गया", - "dockerUrlCopied": "डॉकर यूआरएल कॉपी किया गया", - "serverStatsUrlCopied": "सर्वर स्टैट्स यूआरएल कॉपी किया गया", - "rdpUrlCopied": "आरडीपी यूआरएल कॉपी हो गया", - "vncUrlCopied": "VNC URL कॉपी हो गया", - "telnetUrlCopied": "टेलनेट यूआरएल कॉपी हो गया", - "remoteDesktopUrlCopied": "रिमोट डेस्कटॉप यूआरएल कॉपी हो गया", - "expandActions": "कार्यों का विस्तार करें", - "collapseActions": "कार्यों को संक्षिप्त करें", - "wakeOnLanAction": "लैन पर जागो", - "wakeOnLanSuccess": "मैजिक पैकेट {{name}} को भेजा गया", - "wakeOnLanError": "मैजिक पैकेट भेजने में विफल", - "cloneHostAction": "क्लोन होस्ट", - "copyAddress": "पता कॉपी करें", - "copyLink": "लिंक की प्रतिलिपि करें", - "copyTerminalUrlAction": "टर्मिनल यूआरएल कॉपी करें", - "copyFileManagerUrlAction": "फ़ाइल मैनेजर यूआरएल कॉपी करें", - "copyTunnelUrlAction": "टनल यूआरएल कॉपी करें", - "copyDockerUrlAction": "डॉकर यूआरएल कॉपी करें", - "copyServerStatsUrlAction": "सर्वर सांख्यिकी यूआरएल कॉपी करें", - "copyRdpUrlAction": "आरडीपी यूआरएल कॉपी करें", - "copyVncUrlAction": "VNC URL कॉपी करें", - "copyTelnetUrlAction": "टेलनेट यूआरएल कॉपी करें", - "copyRemoteDesktopUrlAction": "रिमोट डेस्कटॉप यूआरएल कॉपी करें", - "deleteCredentialConfirm": "क्रेडेंशियल \"{{name}} \" हटाएं?", - "deletedCredential": "हटा दिया गया {{name}}", - "deploySSHKeyTitle": "एसएसएच कुंजी तैनात करें", - "deployingBtn": "तैनाती जारी है...", - "deployBtn": "तैनात करना", - "failedToDeployKey": "कुंजी तैनात करने में विफल", - "deleteHostsConfirm": "{{count}} होस्ट{{plural}}को हटाएँ? इसे पूर्ववत नहीं किया जा सकता।", - "movedToRoot": "रूट में स्थानांतरित किया गया", - "failedToMoveHosts": "होस्ट को स्थानांतरित करने में विफल", - "enableTerminalFeature": "टर्मिनल सक्षम करें", - "disableTerminalFeature": "टर्मिनल को अक्षम करें", - "enableFilesFeature": "फ़ाइलें सक्षम करें", - "disableFilesFeature": "फ़ाइलें अक्षम करें", - "enableTunnelsFeature": "सुरंगों को सक्षम करें", - "disableTunnelsFeature": "सुरंगों को निष्क्रिय करें", - "enableDockerFeature": "डॉकर को सक्षम करें", - "disableDockerFeature": "डॉकर को अक्षम करें", - "addTagsPlaceholder": "टैगों को जोड़ें...", - "authDetails": "प्रमाणीकरण विवरण", - "credType": "प्रकार", - "generateKeyPairDesc": "एक नया कुंजी युग्म उत्पन्न करें, निजी और सार्वजनिक दोनों कुंजियाँ स्वचालित रूप से भर दी जाएंगी।", - "generatingKey": "जनरेट हो रहा है...", - "generateLabel": "जनरेट {{label}}", - "uploadFileBtn": "फ़ाइल अपलोड करें", - "keyPassphraseOptional": "कुंजी पासफ़्रेज़ (वैकल्पिक)", - "sshPublicKeyOptional": "एसएसएच सार्वजनिक कुंजी (वैकल्पिक)", - "publicKeyGenerated": "जनित सार्वजनिक कुंजी", - "failedToGeneratePublicKey": "सार्वजनिक कुंजी प्राप्त करने में विफल", - "publicKeyCopied": "सार्वजनिक कुंजी कॉपी कर ली गई", - "keyPairGenerated": "{{label}} कुंजी युग्म उत्पन्न हुआ", - "failedToGenerateKeyPair": "कुंजी युग्म उत्पन्न करने में विफल", - "searchHostsPlaceholder": "होस्ट, पते, टैग खोजें…", - "searchCredentialsPlaceholder": "खोज क्रेडेंशियल…", - "refreshBtn": "ताज़ा करना", - "addTag": "टैगों को जोड़ें...", - "deleteConfirmBtn": "मिटाना", - "tunnelRequirementsText": "SSH सर्वर के लिए /etc/ssh/sshd_config में GatewayPorts yes, AllowTcpForwarding yes और PermitRootLogin yes सेट होना आवश्यक है।", - "deleteHostConfirm": "\"{{name}} \" को हटाएँ?", - "enableAtLeastOneProtocol": "प्रमाणीकरण और कनेक्शन सेटिंग्स को कॉन्फ़िगर करने के लिए ऊपर दिए गए प्रोटोकॉल में से कम से कम एक को सक्षम करें।", - "keyPassphrase": "कुंजी पासफ़्रेज़", - "connectBtn": "जोड़ना", - "disconnectBtn": "डिस्कनेक्ट", - "basicInformation": "मूल जानकारी", - "authDetailsSection": "प्रमाणीकरण विवरण", - "credTypeLabel": "प्रकार", - "hostsTab": "मेजबान", - "credentialsTab": "साख", - "selectMultiple": "एकाधिक का चयन करें", - "selectHosts": "होस्ट चुनें", - "connectionLabel": "संबंध", - "authenticationLabel": "प्रमाणीकरण", - "generateKeyPairTitle": "कुंजी युग्म उत्पन्न करें", - "generateKeyPairDescription": "एक नया कुंजी युग्म उत्पन्न करें, निजी और सार्वजनिक दोनों कुंजियाँ स्वचालित रूप से भर दी जाएंगी।", - "generateFromPrivateKey": "निजी कुंजी से उत्पन्न करें", - "refreshBtn2": "ताज़ा करना", - "exitSelectionTitle": "निकास चयन", - "exportAll": "सभी निर्यात करें", - "addHostBtn2": "होस्ट जोड़ें", - "addCredentialBtn2": "क्रेडेंशियल जोड़ें", - "checkingHostStatuses": "होस्ट की स्थिति की जाँच की जा रही है...", - "pinnedSection": "पिन की गई", - "hostsExported": "होस्ट सफलतापूर्वक निर्यात हो गए", - "exportFailed": "होस्ट निर्यात करने में विफल", - "sampleDownloaded": "नमूना फ़ाइल डाउनलोड हो गई", - "failedToDeleteCredential2": "क्रेडेंशियल हटाने में विफल", - "noFolderOption": "(कोई फ़ोल्डर नहीं)", - "nSelected": "{{count}} चयनित", - "featuresMenu": "विशेषताएँ", - "moveMenu": "कदम", - "cancelSelection": "रद्द करना", - "deployDialogDesc": "होस्ट के authorized_keys पर {{name}} तैनात करें।", - "targetHostLabel": "लक्ष्य मेजबान", - "selectHostOption": "एक होस्ट का चयन करें...", - "keyDeployedSuccess": "कुंजी सफलतापूर्वक तैनात की गई", - "failedToDeployKey2": "कुंजी तैनात करने में विफल", - "deletedCount": "हटाए गए {{count}} होस्ट", - "failedToDeleteCount": "{{count}} होस्ट को हटाने में विफल", - "duplicatedHost": "डुप्लिकेट \"{{name}}\"", - "failedToDuplicateHost": "होस्ट को डुप्लिकेट करने में विफल", - "updatedCount": "अपडेट किए गए {{count}} होस्ट", - "friendlyNameLabel": "मित्रवत नाम", - "descriptionLabel": "विवरण", - "loadingHost": "होस्ट लोड हो रहा है...", - "loadingHosts": "होस्ट लोड हो रहे हैं...", - "loadingCredentials": "क्रेडेंशियल लोड हो रहे हैं...", - "noHostsYet": "अभी तक कोई होस्ट नहीं है", - "noHostsMatchSearch": "आपकी खोज से मेल खाने वाले कोई होस्ट नहीं हैं", - "hostNotFound": "होस्ट नहीं मिला", - "searchHosts": "होस्ट खोजें...", - "sortHosts": "होस्ट को क्रमबद्ध करें", - "sortDefault": "डिफ़ॉल्ट ऑर्डर", - "sortNameAsc": "नाम (अ से जेड तक)", - "sortNameDesc": "नाम (Z → A)", - "sortIpAsc": "आईपी पता (आरोही)", - "sortIpDesc": "आईपी पता (विवरण)", - "sortOnlineFirst": "ऑनलाइन फर्स्ट", - "sortOfflineFirst": "ऑफ़लाइन प्रथम", - "sortPinnedFirst": "पहले पिन किया गया", - "filterHosts": "होस्ट को फ़िल्टर करें", - "filterClearAll": "फ़िल्टर साफ़ करें", - "filterStatusGroup": "स्थिति", - "filterOnline": "ऑनलाइन", - "filterOffline": "ऑफलाइन", - "filterPinned": "पिन की गई", - "filterAuthGroup": "प्राधिकरण प्रकार", - "filterAuthPassword": "पासवर्ड", - "filterAuthKey": "एसएसएच कुंजी", - "filterAuthCredential": "क्रेडेंशियल", - "filterAuthNone": "कोई नहीं", - "filterAuthOpkssh": "ओपीकेएसएच", - "filterProtocolGroup": "शिष्टाचार", - "filterProtocolSsh": "एसएसएच", - "filterProtocolRdp": "आरडीपी", - "filterProtocolVnc": "वीएनसी", - "filterProtocolTelnet": "टेलनेट", - "filterFeaturesGroup": "विशेषताएँ", - "filterFeatureTerminal": "टर्मिनल", - "filterFeatureFileManager": "फ़ाइल मैनेजर", - "filterFeatureTunnel": "सुरंग", - "filterFeatureDocker": "डाक में काम करनेवाला मज़दूर", - "filterTagsGroup": "टैग", - "shareHost": "होस्ट साझा करें", - "shareHostTitle": "शेयर करें: {{name}}", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", + "ssh": "SSH", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", + "tabSsh": "SSH", + "tabTerminal": "Terminal", + "tabRdp": "RDP", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", + "filterProtocolSsh": "SSH", + "filterProtocolRdp": "RDP", + "filterProtocolVnc": "VNC", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "इस होस्ट को शेयरिंग सक्षम करने के लिए क्रेडेंशियल का उपयोग करना होगा। पहले होस्ट को संपादित करें और एक क्रेडेंशियल असाइन करें।" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "संबंध", - "authentication": "प्रमाणीकरण", - "connectionSettings": "कनेक्शन सेटिंग्स", - "displaySettings": "प्रदर्शन सेटिंग्स", - "audioSettings": "श्रव्य विन्यास", - "rdpPerformance": "आरडीपी प्रदर्शन", - "deviceRedirection": "डिवाइस पुनर्निर्देशन", - "session": "सत्र", - "gateway": "द्वार", - "remoteApp": "रिमोटऐप", - "clipboard": "क्लिपबोर्ड", - "sessionRecording": "सत्र रिकॉर्डिंग", - "wakeOnLan": "लैन पर जागो", - "vncSettings": "वीएनसी सेटिंग्स", - "terminalSettings": "टर्मिनल सेटिंग्स", - "rdpPort": "आरडीपी पोर्ट", - "username": "उपयोगकर्ता नाम", - "password": "पासवर्ड", - "domain": "कार्यक्षेत्र", - "securityMode": "सुरक्षा मोड", - "colorDepth": "रंग की गहराई", - "width": "चौड़ाई", - "height": "ऊंचाई", - "dpi": "डीपीआई", - "resizeMethod": "आकार बदलने की विधि", - "clientName": "ग्राहक नाम", - "initialProgram": "प्रारंभिक कार्यक्रम", - "serverLayout": "सर्वर लेआउट", - "timezone": "समय क्षेत्र", - "gatewayHostname": "गेटवे होस्टनाम", - "gatewayPort": "गेटवे पोर्ट", - "gatewayUsername": "गेटवे उपयोगकर्ता नाम", - "gatewayPassword": "गेटवे पासवर्ड", - "gatewayDomain": "गेटवे डोमेन", - "remoteAppProgram": "रिमोटऐप प्रोग्राम", - "workingDirectory": "कार्यकारी डाइरेक्टरी", - "arguments": "बहस", - "normalizeLineEndings": "लाइन के अंत को सामान्य करें", - "recordingPath": "रिकॉर्डिंग पथ", - "recordingName": "रिकॉर्डिंग नाम", - "macAddress": "मैक पता", - "broadcastAddress": "ब्रॉडकास्ट पता", - "udpPort": "यूडीपी पोर्ट", - "waitTimeS": "प्रतीक्षा समय (सेकंड में)", - "driveName": "ड्राइव का नाम", - "drivePath": "ड्राइव पथ", - "ignoreCertificate": "प्रमाणपत्र को अनदेखा करें", - "ignoreCertificateDesc": "सेल्फ-साइन किए गए प्रमाणपत्रों वाले होस्ट से कनेक्शन की अनुमति दें", - "forceLossless": "बल हानि रहित", - "forceLosslessDesc": "दोषरहित छवि एन्कोडिंग को लागू करें (उच्च गुणवत्ता, अधिक बैंडविड्थ)", - "disableAudio": "ऑडियो अक्षम करें", - "disableAudioDesc": "रिमोट सेशन से सभी ऑडियो म्यूट करें", - "enableAudioInput": "ऑडियो इनपुट (माइक्रोफ़ोन) चालू करें", - "enableAudioInputDesc": "स्थानीय माइक्रोफ़ोन को रिमोट सेशन पर फ़ॉरवर्ड करें", - "wallpaper": "वॉलपेपर", - "wallpaperDesc": "डेस्कटॉप वॉलपेपर दिखाएं (इसे बंद करने से प्रदर्शन बेहतर होता है)", - "theming": "थीम", - "themingDesc": "दृश्य थीम और शैलियों को सक्षम करें", - "fontSmoothing": "आकर्षक फ़ॉन्ट", - "fontSmoothingDesc": "क्लियरटाइप फ़ॉन्ट रेंडरिंग सक्षम करें", - "fullWindowDrag": "पूरी विंडो को ड्रैग करें", - "fullWindowDragDesc": "खींचते समय विंडो की सामग्री दिखाएँ", - "desktopComposition": "डेस्कटॉप कंपोजीशन", - "desktopCompositionDesc": "एयरो ग्लास प्रभाव सक्षम करें", - "menuAnimations": "मेनू एनिमेशन", - "menuAnimationsDesc": "मेनू फेड और स्लाइड एनिमेशन सक्षम करें", - "disableBitmapCaching": "बिटमैप कैशिंग को अक्षम करें", - "disableBitmapCachingDesc": "बिटमैप कैश बंद करें (इससे तकनीकी गड़बड़ियों को ठीक करने में मदद मिल सकती है)", - "disableOffscreenCaching": "ऑफस्क्रीन कैशिंग को अक्षम करें", - "disableOffscreenCachingDesc": "ऑफस्क्रीन कैश बंद करें", - "disableGlyphCaching": "ग्लिफ़ कैशिंग को अक्षम करें", - "disableGlyphCachingDesc": "ग्लिफ़ कैश बंद करें", - "enableGfx": "जीएफएक्स सक्षम करें", - "enableGfxDesc": "RemoteFX ग्राफिक्स पाइपलाइन का उपयोग करें", - "enablePrinting": "मुद्रण सक्षम करें", - "enablePrintingDesc": "स्थानीय प्रिंटरों को रिमोट सेशन पर रीडायरेक्ट करें", - "enableDriveRedirection": "ड्राइव रीडायरेक्शन सक्षम करें", - "enableDriveRedirectionDesc": "रिमोट सेशन में स्थानीय फ़ोल्डर को ड्राइव के रूप में मैप करें", - "createDrivePath": "ड्राइव पथ बनाएँ", - "createDrivePathDesc": "यदि फ़ोल्डर मौजूद नहीं है तो उसे स्वचालित रूप से बना दें", - "disableDownload": "डाउनलोड अक्षम करें", - "disableDownloadDesc": "रिमोट सेशन से फ़ाइलें डाउनलोड करने से रोकें", - "disableUpload": "अपलोड अक्षम करें", - "disableUploadDesc": "रिमोट सेशन में फ़ाइलें अपलोड करने से रोकें", - "enableTouch": "टच सक्षम करें", - "enableTouchDesc": "टच इनपुट फ़ॉरवर्डिंग सक्षम करें", - "consoleSession": "कंसोल सत्र", - "consoleSessionDesc": "नए सत्र के बजाय कंसोल (सत्र 0) से कनेक्ट करें", - "sendWolPacket": "WOL पैकेट भेजें", - "sendWolPacketDesc": "कनेक्ट करने से पहले इस होस्ट को जगाने के लिए एक मैजिक पैकेट भेजें।", - "disableCopy": "कॉपी अक्षम करें", - "disableCopyDesc": "रिमोट सेशन से टेक्स्ट कॉपी करने से रोकें", - "disablePaste": "पेस्ट अक्षम करें", - "disablePasteDesc": "रिमोट सेशन में टेक्स्ट पेस्ट करने से रोकें", - "createPathIfMissing": "यदि पथ मौजूद नहीं है तो उसे बनाएं", - "createPathIfMissingDesc": "रिकॉर्डिंग डायरेक्टरी स्वचालित रूप से बनाएं", - "excludeOutput": "आउटपुट को बाहर रखें", - "excludeOutputDesc": "स्क्रीन आउटपुट रिकॉर्ड न करें (केवल मेटाडेटा)।", - "excludeMouse": "माउस को बाहर रखें", - "excludeMouseDesc": "माउस की गतिविधियों को रिकॉर्ड न करें", - "includeKeystrokes": "कीस्ट्रोक्स शामिल करें", - "includeKeystrokesDesc": "स्क्रीन आउटपुट के अलावा मूल कीस्ट्रोक्स को भी रिकॉर्ड करें", - "vncPort": "वीएनसी पोर्ट", - "vncPassword": "वीएनसी पासवर्ड", - "vncUsernameOptional": "उपयोगकर्ता नाम (वैकल्पिक)", - "vncLeaveBlank": "यदि आवश्यक न हो तो इसे खाली छोड़ दें", - "cursorMode": "कर्सर मोड", - "swapRedBlue": "लाल/नीले रंग की अदला-बदली करें", - "swapRedBlueDesc": "लाल और नीले रंग के चैनलों को आपस में बदलें (इससे कुछ रंग संबंधी समस्याएं ठीक हो जाती हैं)", - "readOnly": "केवल पढ़ने के लिए", - "readOnlyDesc": "बिना कोई इनपुट भेजे रिमोट स्क्रीन देखें", - "telnetPort": "टेलनेट पोर्ट", - "terminalType": "टर्मिनल प्रकार", - "fontName": "फ़ॉन्ट नाम", - "fontSize": "फ़ॉन्ट आकार", - "colorScheme": "रंग योजना", - "backspaceKey": "बैकस्पेस कुंजी", - "saveHostFirst": "पहले होस्ट को सेव करें।", - "sharingOptionsAfterSave": "होस्ट को सेव करने के बाद शेयरिंग के विकल्प उपलब्ध हो जाते हैं।", - "sharingLoadError": "शेयरिंग डेटा लोड करने में विफल। कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें।", - "shareHostSection": "होस्ट साझा करें", - "shareWithUser": "उपयोगकर्ता के साथ साझा करें", - "shareWithRole": "भूमिका के साथ साझा करें", - "selectUser": "उपयोगकर्ता का चयन करें", - "selectRole": "भूमिका का चयन करें", - "selectUserOption": "किसी उपयोगकर्ता का चयन करें...", - "selectRoleOption": "एक भूमिका चुनें...", - "permissionLevel": "अनुमति स्तर", - "expiresInHours": "समाप्ति तिथि (घंटे)", - "noExpiryPlaceholder": "समाप्ति तिथि से पहले खाली छोड़ दें", - "shareBtn": "शेयर करना", - "currentAccess": "वर्तमान पहुंच", - "typeHeader": "प्रकार", - "targetHeader": "लक्ष्य", - "permissionHeader": "अनुमति", - "grantedByHeader": "अनुमती देना", - "expiresHeader": "समय-सीमा समाप्त", - "noAccessEntries": "अभी तक प्रवेश के लिए कोई प्रविष्टियाँ नहीं हैं।", - "expiredLabel": "खत्म हो चुका", - "neverLabel": "कभी नहीं", - "revokeBtn": "रद्द करना", - "cancelBtn": "रद्द करना", - "savingBtn": "सहेजा जा रहा है...", - "updateHostBtn": "होस्ट को अपडेट करें", - "addHostBtn": "होस्ट जोड़ें" + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "होस्ट, कमांड या सेटिंग्स खोजें...", - "quickActions": "त्वरित कार्रवाइयां", - "hostManager": "मेजबान प्रबंधक", - "hostManagerDesc": "होस्ट को प्रबंधित करें, जोड़ें या संपादित करें", - "addNewHost": "नया होस्ट जोड़ें", - "addNewHostDesc": "नए होस्ट को पंजीकृत करें", - "adminSettings": "व्यवस्थापक सेटिंग्स", - "adminSettingsDesc": "सिस्टम प्राथमिकताएं और उपयोगकर्ता कॉन्फ़िगर करें", - "userProfile": "उपयोगकर्ता रूपरेखा", - "userProfileDesc": "अपना खाता और प्राथमिकताएं प्रबंधित करें", - "addCredential": "क्रेडेंशियल जोड़ें", - "addCredentialDesc": "SSH कुंजी या पासवर्ड संग्रहीत करें", - "recentActivity": "हाल की गतिविधि", - "serversAndHosts": "सर्वर और होस्ट", - "noHostsFound": "\"{{search}} \" से मेल खाने वाले कोई होस्ट नहीं मिले", - "links": "लिंक", - "navigate": "नेविगेट", - "select": "चुनना", - "toggleWith": "टॉगल करें" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "फलक {{index}} - खाली", - "noTabAssigned": "कोई टैब आवंटित नहीं किया गया", - "focusedPane": "सक्रिय फलक" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "कोई कनेक्शन नहीं", - "noConnectionsDesc": "यहां कनेक्शन देखने के लिए टर्मिनल, फ़ाइल मैनेजर या रिमोट डेस्कटॉप खोलें।", - "connectedFor": "{{duration}} के लिए कनेक्टेड", - "connected": "जुड़े हुए", - "disconnected": "डिस्कनेक्ट किया गया", - "closeTab": "टैब बंद करें", - "closeConnection": "घनिष्ठ संबंध", - "forgetTab": "भूल जाओ", - "removeBackground": "निकालना", - "reconnect": "रिकनेक्ट", - "reopenTab": "फिर से खोलना", - "sectionOpen": "खुला", - "sectionBackground": "पृष्ठभूमि", - "backgroundDesc": "कनेक्शन टूटने के बाद भी सेशन 30 मिनट तक जारी रहता है और दोबारा कनेक्ट किया जा सकता है।", - "persisted": "पृष्ठभूमि में बना रहा", - "expiresIn": "समाप्ति तिथि {{duration}}", - "search": "कनेक्शन खोजें...", - "noSearchResults": "आपकी खोज से कोई संबंध नहीं मिला" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "{{type}} सत्र से कनेक्ट हो रहा है...", - "connectionError": "संपर्क त्रुटि", - "connectionFailed": "कनेक्शन विफल", - "failedToConnect": "कनेक्शन टोकन प्राप्त करने में विफल", - "hostNotFound": "होस्ट नहीं मिला", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "reconnect": "रिकनेक्ट", - "retry": "पुन: प्रयास करें", - "guacdUnavailable": "रिमोट डेस्कटॉप सेवा (guacd) उपलब्ध नहीं है। कृपया सुनिश्चित करें कि guacd चल रहा है, सुलभ है और प्रशासनिक सेटिंग्स में ठीक से कॉन्फ़िगर किया गया है।", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (लॉक स्क्रीन)", - "winKey": "विंडोज़ कुंजी", - "ctrl": "कंट्रोल", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "बदलाव", - "win": "जीतना", - "stickyActive": "{{key}} (लॉक हो गया - छोड़ने के लिए क्लिक करें)", - "stickyInactive": "{{key}} (लॉक करने के लिए क्लिक करें)", - "esc": "पलायन", - "tab": "टैब", - "home": "घर", - "end": "अंत", - "pageUp": "पेज अप", - "pageDown": "पेज नीचे", - "arrowUp": "ऊपर की ओर तीर", - "arrowDown": "नीचे की ओर तीर", - "arrowLeft": "बाएँ तीर", - "arrowRight": "दाएँ तीर", - "fnToggle": "फ़ंक्शन कुंजियाँ", - "reconnect": "पुनः संपर्क सत्र", - "collapse": "टूलबार को छोटा करें", - "expand": "टूलबार का विस्तार करें", - "dragHandle": "स्थान बदलने के लिए खींचें" + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "होस्ट से कनेक्ट करें", - "clear": "स्पष्ट", - "paste": "पेस्ट करें", - "reconnect": "रिकनेक्ट", - "connectionLost": "कनेक्शन टूट गया", - "connected": "जुड़े हुए", - "clipboardWriteFailed": "क्लिपबोर्ड पर कॉपी करने में विफल। सुनिश्चित करें कि पृष्ठ HTTPS या लोकलहोस्ट पर चल रहा है।", - "clipboardReadFailed": "क्लिपबोर्ड से पढ़ने में विफलता। सुनिश्चित करें कि क्लिपबोर्ड की अनुमतियाँ दी गई हैं।", - "clipboardHttpWarning": "पेस्ट करने के लिए HTTPS आवश्यक है। Ctrl+Shift+V का उपयोग करें या HTTPS पर Termix को सर्व करें।", - "unknownError": "अज्ञात त्रुटि उत्पन्न हुई", - "websocketError": "वेबसॉकेट कनेक्शन त्रुटि", - "connecting": "कनेक्ट हो रहा है...", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "reconnecting": "पुनः कनेक्ट हो रहा है... ({{attempt}}/{{max}})", - "reconnected": "सफलतापूर्वक पुनः कनेक्ट हो गया", - "tmuxSessionCreated": "tmux सत्र बनाया गया: {{name}}", - "tmuxSessionAttached": "tmux सत्र संलग्न: {{name}}", - "tmuxUnavailable": "रिमोट होस्ट पर tmux इंस्टॉल नहीं है, इसलिए स्टैंडर्ड शेल का उपयोग किया जा रहा है।", - "tmuxSessionPickerTitle": "tmux सत्र", - "tmuxSessionPickerDesc": "इस होस्ट पर पहले से मौजूद tmux सेशन पाए गए हैं। पुनः जुड़ने के लिए किसी एक का चयन करें या नया सेशन बनाएं।", - "tmuxWindows": "विंडोज़", - "tmuxWindowCount": "{{count}} विंडो", - "tmuxAttached": "संलग्न ग्राहक", - "tmuxAttachedCount": "{{count}} संलग्न", - "tmuxLastActivity": "अंतिम गतिविधि", - "tmuxTimeJustNow": "बस अब", - "tmuxTimeMinutes": "{{count}}मीटर पहले", - "tmuxTimeHours": "{{count}}घंटे पहले", - "tmuxTimeDays": "{{count}}दिन पहले", - "tmuxCreateNew": "नया सत्र शुरू करें", - "tmuxCopyHint": "चयन को समायोजित करें और क्लिपबोर्ड पर कॉपी करने के लिए एंटर दबाएं।", - "tmuxDetach": "tmux सत्र से अलग हो जाएं", - "tmuxDetached": "tmux सत्र से अलग हो गया", - "maxReconnectAttemptsReached": "अधिकतम पुनः कनेक्शन प्रयासों की सीमा पूरी हो गई है।", - "closeTab": "बंद करना", - "connectionTimeout": "रिश्तों का समय बाहर", - "terminalTitle": "टर्मिनल - {{host}}", - "terminalWithPath": "टर्मिनल - {{host}}:{{path}}", - "runTitle": "चल रहा है {{command}} - {{host}}", - "totpRequired": "दो-कारक प्रमाणीकरण आवश्यक है", - "totpCodeLabel": "सत्यापन कोड", - "totpVerify": "सत्यापित करें", - "warpgateAuthRequired": "वॉरपगेट प्रमाणीकरण आवश्यक है", - "warpgateSecurityKey": "सुरक्षा कुंजी", - "warpgateAuthUrl": "प्रमाणीकरण यूआरएल", - "warpgateOpenBrowser": "ब्राउज़र में खोलें", - "warpgateContinue": "मैंने प्रमाणीकरण पूरा कर लिया है", - "opksshAuthRequired": "OPKSSH प्रमाणीकरण आवश्यक है", - "opksshAuthDescription": "आगे बढ़ने के लिए अपने ब्राउज़र में प्रमाणीकरण पूरा करें। यह सत्र 24 घंटे तक वैध रहेगा।", - "opksshOpenBrowser": "प्रमाणीकरण के लिए ब्राउज़र खोलें", - "opksshWaitingForAuth": "ब्राउज़र में प्रमाणीकरण की प्रतीक्षा हो रही है...", - "opksshAuthenticating": "प्रमाणीकरण प्रक्रिया जारी है...", - "opksshTimeout": "प्रमाणीकरण का समय समाप्त हो गया। कृपया पुनः प्रयास करें।", - "opksshAuthFailed": "प्रमाणीकरण विफल रहा। कृपया अपनी पहचान सत्यापित करें और पुनः प्रयास करें।", - "opksshSignInWith": "{{provider}} से साइन इन करें", - "sudoPasswordPopupTitle": "पासवर्ड डालें?", - "websocketAbnormalClose": "कनेक्शन अप्रत्याशित रूप से बंद हो गया। यह रिवर्स प्रॉक्सी या एसएसएल कॉन्फ़िगरेशन समस्या के कारण हो सकता है। कृपया सर्वर लॉग की जाँच करें।", - "connectionLogTitle": "कनेक्शन लॉग", - "connectionLogCopy": "लॉग को क्लिपबोर्ड पर कॉपी करें", - "connectionLogEmpty": "अभी तक कोई कनेक्शन लॉग उपलब्ध नहीं हैं।", - "connectionLogWaiting": "कनेक्शन लॉग की प्रतीक्षा की जा रही है...", - "connectionLogCopied": "कनेक्शन लॉग क्लिपबोर्ड पर कॉपी हो गए हैं", - "connectionLogCopyFailed": "लॉग फ़ाइलों को क्लिपबोर्ड पर कॉपी करने में विफल रहा।", - "connectionRejected": "सर्वर द्वारा कनेक्शन अस्वीकृत कर दिया गया है। कृपया अपनी प्रमाणीकरण और नेटवर्क कॉन्फ़िगरेशन की जाँच करें।", - "hostKeyRejected": "SSH होस्ट कुंजी सत्यापन अस्वीकृत। कनेक्शन रद्द।", - "sessionTakenOver": "सेशन दूसरे टैब में खुल गया था। पुनः कनेक्ट हो रहा है..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "initializingEditor": "एडिटर को आरंभ किया जा रहा है...", - "file": "फ़ाइल", - "folder": "फ़ोल्डर", - "uploadFile": "फ़ाइल अपलोड करें", - "downloadFile": "डाउनलोड करना", - "extractArchive": "संग्रह निकालें", - "extractingArchive": "{{name}} को निकालना ...", - "archiveExtractedSuccessfully": "{{name}} सफलतापूर्वक निकाला गया", - "extractFailed": "निष्कर्षण विफल रहा", - "compressFile": "फ़ाइल को संपीड़ित करें", - "compressFiles": "फ़ाइलों को संपीड़ित करें", - "compressFilesDesc": "{{count}} आइटमों को एक संग्रह में संपीड़ित करें", - "archiveName": "संग्रह नाम", - "enterArchiveName": "आर्काइव का नाम दर्ज करें...", - "compressionFormat": "संपीड़न प्रारूप", - "selectedFiles": "चयनित फ़ाइलें", - "andMoreFiles": "और {{count}} अधिक...", - "compress": "संकुचित करें", - "compressingFiles": "{{count}} आइटमों को {{name}} में संपीड़ित करना...", - "filesCompressedSuccessfully": "{{name}} सफलतापूर्वक निर्मित", - "compressFailed": "संपीड़न विफल रहा", - "edit": "संपादन करना", - "preview": "पूर्व दर्शन", - "previous": "पहले का", - "next": "अगला", - "pageXOfY": "पृष्ठ {{current}} का {{total}}", - "zoomOut": "ज़ूम आउट", - "zoomIn": "ज़ूम इन", - "newFile": "नई फ़ाइल", - "newFolder": "नया फ़ोल्डर", - "rename": "नाम बदलें", - "uploading": "अपलोड हो रहा है...", - "uploadingFile": "अपलोड हो रहा है {{name}}...", - "fileName": "फ़ाइल नाम", - "folderName": "फ़ोल्डर का नाम", - "fileUploadedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक अपलोड हो गई", - "failedToUploadFile": "फ़ाइल अपलोड करने में विफल", - "fileDownloadedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक डाउनलोड हो गई", - "failedToDownloadFile": "फ़ाइल डाउनलोड करने में विफल", - "fileCreatedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक बनाई गई", - "folderCreatedSuccessfully": "फ़ोल्डर \"{{name}}\" सफलतापूर्वक बनाया गया", - "failedToCreateItem": "आइटम बनाने में विफल", - "operationFailed": "{{operation}} ऑपरेशन {{name}}के लिए विफल रहा : {{error}}", - "failedToResolveSymlink": "सिम्लिंक को हल करने में विफल", - "itemsDeletedSuccessfully": "{{count}} आइटम सफलतापूर्वक हटा दिए गए", - "failedToDeleteItems": "आइटम हटाने में विफल", - "sudoPasswordRequired": "व्यवस्थापक पासवर्ड आवश्यक है", - "enterSudoPassword": "इस प्रक्रिया को जारी रखने के लिए sudo पासवर्ड दर्ज करें।", - "sudoPassword": "सूडो पासवर्ड", - "sudoOperationFailed": "Sudo ऑपरेशन विफल रहा", - "sudoAuthFailed": "सूडो प्रमाणीकरण विफल रहा", - "dragFilesToUpload": "फ़ाइलें अपलोड करने के लिए उन्हें यहाँ ड्रॉप करें", - "emptyFolder": "यह फ़ोल्डर खाली है", - "searchFiles": "फ़ाइलें खोजें...", - "upload": "अपलोड करें", - "selectHostToStart": "फ़ाइल प्रबंधन शुरू करने के लिए एक होस्ट चुनें", - "sshRequiredForFileManager": "फ़ाइल मैनेजर को SSH की आवश्यकता होती है। इस होस्ट पर SSH सक्षम नहीं है।", - "failedToConnect": "एसएसएच से कनेक्ट करने में विफल", - "failedToLoadDirectory": "निर्देशिका लोड करने में विफल", - "noSSHConnection": "कोई SSH कनेक्शन उपलब्ध नहीं है", - "copy": "प्रतिलिपि", - "cut": "काटना", - "paste": "पेस्ट करें", - "copyPath": "पथ कॉपी करें", - "copyPaths": "पथों की प्रतिलिपि बनाएँ", - "delete": "मिटाना", - "properties": "गुण", - "refresh": "ताज़ा करना", - "downloadFiles": "ब्राउज़र में {{count}} फ़ाइलें डाउनलोड करें", - "copyFiles": "आइटम कॉपी करें {{count}}", - "cutFiles": "{{count}} आइटम काटें", - "deleteFiles": "{{count}} आइटम हटाएं", - "filesCopiedToClipboard": "{{count}} आइटम क्लिपबोर्ड पर कॉपी किए गए", - "filesCutToClipboard": "{{count}} आइटम क्लिपबोर्ड पर सेव हो गए", - "pathCopiedToClipboard": "पथ क्लिपबोर्ड पर कॉपी हो गया", - "pathsCopiedToClipboard": "{{count}} पथ क्लिपबोर्ड पर कॉपी किए गए", - "failedToCopyPath": "क्लिपबोर्ड पर पथ कॉपी करने में विफल", - "movedItems": "{{count}} आइटम स्थानांतरित किए गए", - "failedToDeleteItem": "आइटम हटाने में विफल", - "itemRenamedSuccessfully": "{{type}} का नाम सफलतापूर्वक बदल दिया गया", - "failedToRenameItem": "आइटम का नाम बदलने में विफल", - "download": "डाउनलोड करना", - "permissions": "अनुमतियां", - "size": "आकार", - "modified": "संशोधित", - "path": "पथ", - "confirmDelete": "क्या आप वाकई {{name}} को हटाना चाहते हैं?", - "permissionDenied": "अनुमति नहीं मिली", - "serverError": "सर्वर त्रुटि", - "fileSavedSuccessfully": "फ़ाइल सफलतापूर्वक सहेज ली गई", - "failedToSaveFile": "फ़ाइल सहेजने में विफल", - "confirmDeleteSingleItem": "क्या आप वाकई \"{{name}} \" को स्थायी रूप से हटाना चाहते हैं?", - "confirmDeleteMultipleItems": "क्या आप वाकई {{count}} आइटम को स्थायी रूप से हटाना चाहते हैं?", - "confirmDeleteMultipleItemsWithFolders": "क्या आप वाकई {{count}} आइटम को स्थायी रूप से हटाना चाहते हैं? इसमें फ़ोल्डर और उनकी सामग्री शामिल है।", - "confirmDeleteFolder": "क्या आप वाकई फोल्डर \"{{name}}\" और उसकी सभी सामग्री को स्थायी रूप से हटाना चाहते हैं?", - "permanentDeleteWarning": "यह कार्रवाई पूर्ववत नहीं की जा सकती। आइटम सर्वर से स्थायी रूप से हटा दिए जाएंगे।", - "recent": "हाल ही का", - "pinned": "पिन की गई", - "folderShortcuts": "फ़ोल्डर शॉर्टकट", - "failedToReconnectSSH": "एसएसएच सत्र को पुनः कनेक्ट करने में विफल।", - "openTerminalHere": "टर्मिनल यहाँ खोलें", - "run": "दौड़ना", - "openTerminalInFolder": "इस फ़ोल्डर में टर्मिनल खोलें", - "openTerminalInFileLocation": "फ़ाइल स्थान पर टर्मिनल खोलें", - "runningFile": "दौड़ना - {{file}}", - "onlyRunExecutableFiles": "केवल निष्पादन योग्य फ़ाइलें ही चला सकते हैं", - "directories": "निर्देशिका", - "removedFromRecentFiles": "हाल की फ़ाइलों से \"{{name}}\" हटा दिया गया है", - "removeFailed": "हटाने में विफलता", - "unpinnedSuccessfully": "\"{{name}}\" को सफलतापूर्वक अनपिन कर दिया गया", - "unpinFailed": "अनपिन विफल", - "removedShortcut": "शॉर्टकट \"{{name}} \" हटा दिया गया", - "removeShortcutFailed": "शॉर्टकट हटाने में विफलता", - "clearedAllRecentFiles": "सभी हालिया फ़ाइलें हटा दी गईं", - "clearFailed": "क्लियर विफल", - "removeFromRecentFiles": "हाल की फ़ाइलों से हटाएँ", - "clearAllRecentFiles": "सभी हालिया फ़ाइलें साफ़ करें", - "unpinFile": "फ़ाइल को अनपिन करें", - "removeShortcut": "शॉर्टकट हटाएँ", - "pinFile": "पिन फ़ाइल", - "addToShortcuts": "शॉर्टकट में जोड़ें", - "pasteFailed": "पेस्ट विफल", - "noUndoableActions": "कोई भी कार्य पूर्ववत नहीं किया जा सकता", - "undoCopySuccess": "कॉपी करने की प्रक्रिया पूर्ववत की गई: कॉपी की गई {{count}} फ़ाइलें हटाई गईं", - "undoCopyFailedDelete": "पूर्ववत करने का प्रयास विफल: प्रतिलिपि की गई कोई भी फ़ाइल हटाई नहीं जा सकी", - "undoCopyFailedNoInfo": "पूर्ववत करने में विफल: प्रतिलिपि की गई फ़ाइल की जानकारी नहीं मिल सकी", - "undoMoveSuccess": "स्थानांतरण प्रक्रिया को पूर्ववत किया गया: {{count}} फ़ाइलों को वापस मूल स्थान पर स्थानांतरित कर दिया गया", - "undoMoveFailedMove": "पूर्ववत करने में विफल: कोई भी फ़ाइल वापस नहीं ले जा सका", - "undoMoveFailedNoInfo": "पूर्ववत करने में विफल: स्थानांतरित फ़ाइल की जानकारी नहीं मिल सकी", - "undoDeleteNotSupported": "डिलीट ऑपरेशन को पूर्ववत नहीं किया जा सकता: फ़ाइलें सर्वर से स्थायी रूप से हटा दी गई हैं।", - "undoTypeNotSupported": "असमर्थित पूर्ववत करें ऑपरेशन प्रकार", - "undoOperationFailed": "अनडू ऑपरेशन विफल रहा", - "unknownError": "अज्ञात त्रुटि", - "confirm": "पुष्टि करना", - "find": "खोजो...", - "replace": "प्रतिस्थापित करें", - "downloadInstead": "इसके बजाय डाउनलोड करें", - "keyboardShortcuts": "कुंजीपटल अल्प मार्ग", - "searchAndReplace": "खोजें और बदलें", - "editing": "संपादन", - "search": "खोज", - "findNext": "दूसरा खोजो", - "findPrevious": "पिछला खोजें", - "save": "बचाना", - "selectAll": "सबका चयन करें", - "undo": "पूर्ववत", - "redo": "फिर से करना", - "moveLineUp": "लाइन अप को आगे बढ़ाएं", - "moveLineDown": "लाइन को नीचे ले जाएं", - "toggleComment": "टिप्पणी को टॉगल करें", - "autoComplete": "स्वतः पूर्ण", - "imageLoadError": "छवि लोड करने में विफल", - "startTyping": "टाइप करना शुरू करें...", - "unknownSize": "अज्ञात आकार", - "fileIsEmpty": "फ़ाइल खाली है", - "largeFileWarning": "बड़ी फ़ाइल संबंधी चेतावनी", - "largeFileWarningDesc": "इस फ़ाइल का आकार {{size}} है, जिससे टेक्स्ट के रूप में खोलने पर प्रदर्शन संबंधी समस्याएँ उत्पन्न हो सकती हैं।", - "fileNotFoundAndRemoved": "फ़ाइल \"{{name}}\" नहीं मिली और इसे हाल ही में/पिन की गई फ़ाइलों से हटा दिया गया है", - "failedToLoadFile": "फ़ाइल लोड करने में विफल: {{error}}", - "serverErrorOccurred": "सर्वर में त्रुटि आ गई है। कृपया बाद में पुनः प्रयास करें।", - "autoSaveFailed": "ऑटो-सेव विफल रहा", - "fileAutoSaved": "फ़ाइल स्वतः सहेजी गई", - "moveFileFailed": "{{name}} को स्थानांतरित करने में विफल", - "moveOperationFailed": "स्थानांतरण प्रक्रिया विफल रही", - "canOnlyCompareFiles": "केवल दो फाइलों की तुलना की जा सकती है", - "comparingFiles": "फ़ाइलों की तुलना: {{file1}} और {{file2}}", - "dragFailed": "ड्रैग ऑपरेशन विफल रहा", - "filePinnedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक पिन कर दी गई", - "pinFileFailed": "फ़ाइल को पिन करने में विफल", - "fileUnpinnedSuccessfully": "फ़ाइल \"{{name}}\" सफलतापूर्वक अनपिन कर दी गई", - "unpinFileFailed": "फ़ाइल को अनपिन करने में विफल", - "shortcutAddedSuccessfully": "फ़ोल्डर शॉर्टकट \"{{name}}\" सफलतापूर्वक जोड़ दिया गया", - "addShortcutFailed": "शॉर्टकट जोड़ने में विफल", - "operationCompletedSuccessfully": "{{operation}} {{count}} आइटम सफलतापूर्वक", - "operationCompleted": "{{operation}} {{count}} आइटम", - "downloadFileSuccess": "फ़ाइल {{name}} सफलतापूर्वक डाउनलोड हो गई", - "downloadFileFailed": "डाउनलोड विफल", - "moveTo": "{{name}} पर जाएँ", - "diffCompareWith": "{{name}} के साथ अंतर की तुलना करें", - "dragOutsideToDownload": "फ़ाइलें डाउनलोड करने के लिए विंडो के बाहर खींचें ({{count}} फ़ाइलें)", - "newFolderDefault": "नया फ़ोल्डर", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} आइटम सफलतापूर्वक {{target}} में स्थानांतरित कर दिए गए हैं।", - "move": "कदम", - "searchInFile": "फ़ाइल में खोजें (Ctrl+F)", - "showKeyboardShortcuts": "कीबोर्ड शॉर्टकट दिखाएँ", - "startWritingMarkdown": "अपना मार्कडाउन कंटेंट लिखना शुरू करें...", - "loadingFileComparison": "फ़ाइल तुलना लोड हो रही है...", - "reload": "पुनः लोड करें", - "compare": "तुलना करना", - "sideBySide": "अगल बगल", - "inline": "इन - लाइन", - "fileComparison": "फ़ाइल तुलना: {{file1}} बनाम {{file2}}", - "fileTooLarge": "फ़ाइल बहुत बड़ी है: {{error}}", - "sshConnectionFailed": "SSH कनेक्शन विफल हो गया। कृपया {{name}} ({{ip}}:{{port}} ) से अपना कनेक्शन जांचें।", - "loadFileFailed": "फ़ाइल लोड करने में विफल: {{error}}", - "connecting": "कनेक्ट हो रहा है...", - "connectedSuccessfully": "सफलतापूर्वक कनेक्ट हो गया", - "totpVerificationFailed": "टीओटीपी सत्यापन विफल रहा", - "warpgateVerificationFailed": "वॉरपगेट प्रमाणीकरण विफल रहा", - "authenticationFailed": "प्रमाणीकरण विफल होना", - "verificationCodePrompt": "सत्यापन कोड:", - "changePermissions": "अनुमतियाँ बदलें", - "currentPermissions": "वर्तमान अनुमतियाँ", - "owner": "मालिक", - "group": "समूह", - "others": "अन्य", - "read": "पढ़ना", - "write": "लिखना", - "execute": "निष्पादित करना", - "permissionsChangedSuccessfully": "अनुमतियाँ सफलतापूर्वक बदल दी गईं", - "failedToChangePermissions": "अनुमतियाँ बदलने में विफल", - "name": "नाम", - "sortByName": "नाम", - "sortByDate": "डेटा संशोधित", - "sortBySize": "आकार", - "ascending": "आरोही", - "descending": "अवरोही", - "root": "जड़", - "new": "नया", - "sortBy": "इसके अनुसार क्रमबद्ध करें", - "items": "सामान", - "selected": "चयनित", - "editor": "संपादक", - "octal": "अष्टभुजाकार", - "storage": "भंडारण", - "disk": "डिस्क", - "used": "इस्तेमाल किया गया", - "of": "का", - "toggleSidebar": "साइडबार टॉगल करें", - "cannotLoadPdf": "पीडीएफ लोड नहीं हो पा रही है", - "pdfLoadError": "इस पीडीएफ फाइल को लोड करने में त्रुटि हुई।", - "loadingPdf": "पीडीएफ लोड हो रही है...", - "loadingPage": "लोडिंग पृष्ठ..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "होस्ट को कॉपी करें…", - "moveToHost": "होस्ट पर जाएँ…", - "copyItemsToHost": "आइटम को होस्ट… पर कॉपी करें {{count}}", - "moveItemsToHost": "{{count}} आइटम को होस्ट… पर ले जाएं", - "noHostsConnected": "कोई अन्य फाइल-मैनेजर होस्ट उपलब्ध नहीं है।", - "noHostsConnectedHint": "होस्ट मैनेजर में फाइल मैनेजर सक्षम करके एक और एसएसएच होस्ट जोड़ें।", - "selectDestinationHost": "गंतव्य होस्ट का चयन करें", - "destinationPath": "गंतव्य पथ", - "recentDestinations": "हाल के गंतव्य", - "collapseRecentDestinations": "हाल के गंतव्यों को संक्षिप्त करें", - "expandRecentDestinations": "हाल के गंतव्यों का विस्तार करें", - "browseFolders": "गंतव्य फ़ोल्डरों को ब्राउज़ करें", - "browseDestination": "ब्राउज़ करें या पथ दर्ज करें", - "confirmCopy": "प्रतिलिपि", - "confirmMove": "कदम", - "transferring": "स्थानांतरण…", - "compressing": "संपीड़ित करना…", - "extracting": "… को निकालना", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", "transferringItems": "Transferring {{current}} of {{total}} items…", - "transferSuccess": "स्थानांतरण पूरा हुआ", - "transferError": "स्थानांतरण विफल", - "transferPartial": "स्थानांतरण {{count}} त्रुटियों के साथ पूरा हुआ", - "transferPartialHint": "स्थानांतरण नहीं हो सका: {{paths}}", - "itemsSummary": "{{count}} आइटम", - "destMustBeDirectory": "एक से अधिक वस्तुओं के स्थानांतरण के लिए गंतव्य एक निर्देशिका होनी चाहिए।", - "selectThisFolder": "इस फ़ोल्डर का चयन करें", - "browsePathWillBeCreated": "यह फ़ोल्डर अभी मौजूद नहीं है। स्थानांतरण शुरू होने पर यह बन जाएगा।", - "browsePathError": "गंतव्य होस्ट पर यह पथ नहीं खोला जा सका।", - "goUp": "ऊपर जाना", - "copyFolderToHost": "फ़ोल्डर को होस्ट पर कॉपी करें…", - "moveFolderToHost": "फ़ोल्डर को होस्ट… पर ले जाएं", - "hostReady": "तैयार", - "hostConnecting": "कनेक्टिंग…", - "hostDisconnected": "जुड़े नहीं हैं", - "hostAuthRequired": "प्रमाणीकरण आवश्यक है — पहले इस होस्ट पर फ़ाइल मैनेजर खोलें", - "hostConnectionFailed": "कनेक्शन विफल", - "metricsTitle": "स्थानांतरण समय", - "metricsPrepare": "गंतव्य तैयार करें: {{duration}}", - "metricsCompress": "स्रोत पर संपीड़ित करें: {{duration}}", - "metricsHopSourceRead": "स्रोत → सर्वर: {{throughput}}", - "metricsHopDestSftpWrite": "सर्वर → गंतव्य (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "सर्वर → गंतव्य (स्थानीय): {{throughput}}", - "metricsTransfer": "अंत से अंत तक: {{throughput}} ({{duration}})", - "metricsExtract": "गंतव्य पर निष्कर्षण: {{duration}}", - "metricsSourceDelete": "स्रोत से हटाएँ: {{duration}}", - "metricsTotal": "कुल: {{duration}}", - "progressCompressing": "स्रोत होस्ट पर संपीड़न…", - "progressExtracting": "गंतव्य… पर निष्कर्षण", - "progressTransferring": "डेटा स्थानांतरित करना…", - "progressReconnecting": "पुनः कनेक्ट हो रहा है…", - "parallelSegmentsLabel": "समानांतर स्थानांतरण लेन", - "parallelSegmentsOption": "{{count}} लेन", - "parallelSegmentsHint": "बड़ी फाइलों को 256 एमबी के टुकड़ों में विभाजित किया जाता है। उच्च कुल थ्रूपुट के लिए कई लेन अलग-अलग कनेक्शनों का उपयोग करते हैं (जैसे कई स्थानांतरण शुरू करना)।", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", - "progressTransferringItems": "फ़ाइलें स्थानांतरित करना ({{current}} का {{total}})…", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} फ़ाइलें", - "sourceNotDeletedPartial": "मूल फाइलें सुरक्षित रखी गई हैं (आंशिक स्थानांतरण)", - "jumpHostLimitation": "टर्मिक्स सर्वर से दोनों होस्ट तक पहुंच होनी चाहिए। होस्ट-टू-होस्ट डायरेक्ट रूटिंग समर्थित नहीं है।", - "cancel": "रद्द करना", - "methodLabel": "स्थानांतरण विधि", - "methodAuto": "ऑटो", - "methodTar": "टार संग्रह", - "methodItemSftp": "प्रति-फ़ाइल SFTP", - "methodAutoHint": "यह फ़ाइल संख्या, आकार और संपीड़नीयता के आधार पर tar या प्रति-फ़ाइल SFTP का चयन करता है। एकल फ़ाइलें हमेशा स्ट्रीमिंग SFTP का उपयोग करती हैं।", - "methodTarHint": "स्रोत पर संपीड़न करें, एक संग्रह स्थानांतरित करें, गंतव्य पर निष्कर्षण करें। दोनों यूनिक्स होस्ट पर टार फ़ाइल का होना आवश्यक है।", - "methodItemSftpHint": "प्रत्येक फ़ाइल को SFTP के माध्यम से अलग-अलग स्थानांतरित करें। यह विंडोज़ सहित सभी होस्ट पर काम करता है।", - "methodPreviewLoading": "स्थानांतरण विधि की गणना…", - "methodPreviewError": "स्थानांतरण विधि का पूर्वावलोकन नहीं किया जा सका। सर्वर प्रारंभ होने पर विधि का चयन करेगा।", - "methodPreviewWillUseTar": "उपयोग करेगा: टार संग्रह", - "methodPreviewWillUseItemSftp": "उपयोग किया जाएगा: प्रति-फ़ाइल SFTP", - "methodPreviewScanSummary": "{{fileCount}} फाइलें, {{totalSize}} कुल (स्रोत होस्ट पर स्कैन किया गया)।", - "methodItemSftpLimitation": "प्रत्येक फ़ाइल एक के बाद एक, सिंगल-फ़ाइल कॉपी के रूप में एक ही SFTP स्ट्रीम का उपयोग करती है। सभी फ़ाइलों की प्रगति को एक साथ जोड़ा जाता है, इसलिए बड़ी फ़ाइलों के दौरान बार धीरे-धीरे चलता है।", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "आपने प्रति-फाइल SFTP का विकल्प चुना।", - "user_tar": "आपने tar आर्काइव चुना।", - "tar_unavailable": "एक या दोनों होस्ट पर टार उपलब्ध नहीं है — इसके बजाय प्रति-फाइल एसएफटीपी का उपयोग किया जाएगा।", - "windows_host": "इसमें विंडोज होस्ट का उपयोग किया गया है — tar का उपयोग नहीं किया गया है।", - "auto_multi_large": "ऑटो: संपीड़ित डेटा वाली एक बड़ी फ़ाइल ({{largestSize}}) सहित कई फ़ाइलें - टार बंडलों को एक ही स्थानांतरण में शामिल करता है।", - "auto_single_large_in_archive": "ऑटो: इस सेट में एक बड़ी फ़ाइल ({{largestSize}}) — प्रति-फ़ाइल SFTP.", - "auto_many_incompressible": "ऑटो: अधिकतर असंपीड़ित डेटा — प्रति-फ़ाइल SFTP।", - "auto_many_files": "ऑटो: कई फाइलें ({{fileCount}}) — टार प्रति-फाइल ओवरहेड को कम करता है।", - "auto_default": "ऑटो: इस सेट के लिए प्रति-फ़ाइल SFTP।" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "रद्द करना", - "progressCancelling": "रद्द करना…", - "progressStalled": "ठप", - "resumedHint": "किसी अन्य विंडो में शुरू किए गए सक्रिय स्थानांतरण से पुनः कनेक्ट हो गया।", - "transferCancelled": "स्थानांतरण रद्द", - "transferCancelledCopyHint": "गंतव्य स्थान पर कुछ फाइलें अधूरी रह सकती हैं।", - "transferCancelledMoveHint": "गंतव्य स्थान पर कुछ फाइलें अधूरी रह सकती हैं। स्रोत फाइलें हटाई नहीं गई हैं।", - "cleanupDestFiles": "सफाई गंतव्य", - "cleanupDestFilesSuccess": "गंतव्य स्थान से आंशिक फ़ाइलें हटा दी गईं", - "cleanupDestFilesPartial": "कुछ आंशिक फ़ाइलें हटाई नहीं जा सकीं", - "cleanupDestFilesNothing": "गंतव्य पर सफाई करने के लिए कुछ भी नहीं है।", - "cleanupDestFilesError": "सफाई विफल रही", - "retryTransfer": "पुन: प्रयास करें", - "retryTransferError": "पुनः प्रयास विफल रहा", - "transferFailedRetryHint": "गंतव्य स्थान पर आंशिक डेटा सुरक्षित रखा गया है। कनेक्शन बहाल होने पर पुनः प्रयास शुरू होगा।" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "कोई एसएसएच टनल नहीं", - "createFirstTunnelMessage": "आपने अभी तक कोई SSH टनल नहीं बनाई है। शुरुआत करने के लिए होस्ट मैनेजर में टनल कनेक्शन कॉन्फ़िगर करें।", - "connected": "जुड़े हुए", - "disconnected": "डिस्कनेक्ट किया गया", - "connecting": "कनेक्ट हो रहा है...", - "error": "गलती", - "canceling": "रद्द किया जा रहा है...", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "cancel": "रद्द करना", - "port": "पत्तन", - "localPort": "स्थानीय बंदरगाह", - "remotePort": "रिमोट पोर्ट", - "currentHostPort": "वर्तमान होस्ट पोर्ट", - "endpointPort": "एंडपॉइंट पोर्ट", - "bindIp": "स्थानीय आईपी", - "endpointSshConfig": "एंडपॉइंट एसएसएच कॉन्फ़िगरेशन", - "endpointSshHost": "एंडपॉइंट एसएसएच होस्ट", - "endpointSshHostPlaceholder": "एक कॉन्फ़िगर किए गए होस्ट का चयन करें", - "endpointSshHostRequired": "प्रत्येक क्लाइंट टनल के लिए एक एंडपॉइंट एसएसएच होस्ट का चयन करें।", - "attempt": "प्रयास {{current}} का {{max}}", - "nextRetryIn": "अगला प्रयास {{seconds}} सेकंड में होगा", - "clientTunnels": "क्लाइंट टनल", - "clientTunnel": "क्लाइंट टनल", - "addClientTunnel": "क्लाइंट टनल जोड़ें", - "noClientTunnels": "इस डेस्कटॉप पर कोई क्लाइंट टनल कॉन्फ़िगर नहीं किया गया है।", - "tunnelName": "सुरंग का नाम", - "remoteHost": "रिमोट होस्ट", - "autoStart": "ऑटो स्टार्ट", - "clientAutoStartDesc": "यह डेस्कटॉप क्लाइंट के खुलने पर शुरू होता है और कनेक्टेड रहता है।", - "clientManualStartDesc": "स्टार्ट और स्टॉप बटन का इस्तेमाल इसी पंक्ति से करें। टर्मिक्स इसे अपने आप नहीं खोलेगा।", - "clientRemoteServerNote": "रिमोट पोर्ट फॉरवर्डिंग के लिए एंडपॉइंट एसएसएच सर्वर पर AllowTcpForwarding और GatewayPorts की आवश्यकता हो सकती है। डेस्कटॉप के डिस्कनेक्ट होने पर रिमोट पोर्ट बंद हो जाता है।", - "clientTunnelStarted": "क्लाइंट टनल शुरू हो गया", - "clientTunnelStopped": "क्लाइंट टनल बंद हो गया", - "tunnelTestSucceeded": "सुरंग परीक्षण सफल रहा", - "tunnelTestFailed": "टनल परीक्षण विफल रहा", - "localSaved": "क्लाइंट टनल सहेजे गए", - "localSaveError": "स्थानीय क्लाइंट टनल को सहेजने में विफल", - "invalidBindIp": "स्थानीय आईपी एक वैध आईपीवी4 पता होना चाहिए।", - "invalidLocalTargetIp": "स्थानीय लक्ष्य आईपी एक वैध IPv4 पता होना चाहिए।", - "invalidLocalPort": "स्थानीय बंदरगाह का मान 1 और 65535 के बीच होना चाहिए।", - "invalidRemotePort": "रिमोट पोर्ट का मान 1 और 65535 के बीच होना चाहिए।", - "invalidLocalTargetPort": "स्थानीय लक्ष्य पोर्ट 1 और 65535 के बीच होना चाहिए।", - "invalidEndpointPort": "एंडपॉइंट पोर्ट 1 और 65535 के बीच होना चाहिए।", - "duplicateAutoStartBind": "केवल एक ऑटो-स्टार्ट क्लाइंट टनल {{bind}} का उपयोग कर सकता है।", - "manualControlError": "टनल की स्थिति अपडेट करने में विफल।", - "active": "सक्रिय", - "start": "शुरू", - "stop": "रुकना", - "test": "परीक्षा", - "type": "सुरंग प्रकार", - "typeLocal": "स्थानीय (-एल)", - "typeRemote": "रिमोट (-R)", - "typeDynamic": "गतिशील (-डी)", - "typeServerLocalDesc": "वर्तमान होस्ट से एंडपॉइंट तक।", - "typeServerRemoteDesc": "एंडपॉइंट वापस वर्तमान होस्ट पर।", - "typeClientLocalDesc": "स्थानीय कंप्यूटर से एंडपॉइंट तक।", - "typeClientRemoteDesc": "एंडपॉइंट वापस स्थानीय कंप्यूटर पर।", - "typeClientDynamicDesc": "स्थानीय कंप्यूटर पर SOCKS।", - "typeDynamicDesc": "SSH के माध्यम से SOCKS5 CONNECT ट्रैफ़िक को आगे भेजें", - "forwardDescriptionServerLocal": "वर्तमान होस्ट {{sourcePort}} → एंडपॉइंट {{endpointPort}}.", - "forwardDescriptionServerRemote": "एंडपॉइंट {{endpointPort}} → वर्तमान होस्ट {{sourcePort}}.", - "forwardDescriptionServerDynamic": "वर्तमान होस्ट पर मोज़े {{sourcePort}}.", - "forwardDescriptionClientLocal": "स्थानीय {{sourcePort}} → दूरस्थ {{endpointPort}}.", - "forwardDescriptionClientRemote": "रिमोट {{sourcePort}} → लोकल {{endpointPort}}.", - "forwardDescriptionClientDynamic": "स्थानीय पोर्ट {{sourcePort}} पर SOCKS .", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → मोज़े वाया {{endpoint}}", - "autoNameClientLocal": "स्थानीय {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → स्थानीय {{localPort}}", - "autoNameClientDynamic": "मोज़े {{localPort}} के माध्यम से {{endpoint}}", - "route": "मार्ग:", - "lastStarted": "आखिरी बार शुरू हुआ", - "lastTested": "अंतिम बार परीक्षण किया गया", - "lastError": "अंतिम त्रुटि", - "maxRetries": "अधिकतम पुनः प्रयास", - "maxRetriesDescription": "पुनः प्रयास करने की अधिकतम संख्या।", - "retryInterval": "पुनः प्रयास अंतराल (सेकंड)", - "retryIntervalDescription": "पुनः प्रयास करने के बीच प्रतीक्षा करने का समय।", - "local": "स्थानीय", - "remote": "दूर", - "destination": "गंतव्य", - "host": "मेज़बान", - "mode": "तरीका", - "noHostSelected": "कोई होस्ट चयनित नहीं है", - "working": "कार्यरत..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "याद", - "disk": "डिस्क", - "network": "नेटवर्क", - "uptime": "अपटाइम", - "processes": "प्रक्रियाओं", - "available": "उपलब्ध", - "free": "मुक्त", - "connecting": "कनेक्ट हो रहा है...", - "connectionFailed": "सर्वर से कनेक्ट करने में विफल", - "naCpus": "लागू नहीं सीपीयू(ओं)", - "cpuCores_one": "{{count}} कोर", - "cpuCores_other": "{{count}} कोर", - "cpuUsage": "सीपीयू उपयोग", - "memoryUsage": "स्मृति प्रयोग", - "diskUsage": "डिस्क उपयोग", - "failedToFetchHostConfig": "होस्ट कॉन्फ़िगरेशन प्राप्त करने में विफल", - "serverOffline": "सर्वर ऑफ़लाइन", - "cannotFetchMetrics": "ऑफ़लाइन सर्वर से मेट्रिक्स प्राप्त नहीं किए जा सकते", - "totpFailed": "टीओटीपी सत्यापन विफल रहा", - "noneAuthNotSupported": "सर्वर स्टैट्स 'none' प्रमाणीकरण प्रकार का समर्थन नहीं करता है।", - "load": "भार", - "systemInfo": "व्यवस्था जानकारी", - "hostname": "होस्ट का नाम", - "operatingSystem": "ऑपरेटिंग सिस्टम", - "kernel": "गुठली", - "seconds": "सेकंड", - "networkInterfaces": "नेटवर्क इंटरफेस", - "noInterfacesFound": "कोई नेटवर्क इंटरफ़ेस नहीं मिला", - "noProcessesFound": "कोई प्रक्रिया नहीं मिली", - "loginStats": "एसएसएच लॉगिन सांख्यिकी", - "noRecentLoginData": "हाल ही में लॉगिन डेटा उपलब्ध नहीं है", - "executingQuickAction": "{{name}} निष्पादित किया जा रहा है...", - "quickActionSuccess": "{{name}} सफलतापूर्वक पूरा हुआ", - "quickActionFailed": "{{name}} असफल", - "quickActionError": "{{name}} को निष्पादित करने में विफल", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "श्रवण पोर्ट", - "protocol": "शिष्टाचार", - "port": "पत्तन", - "address": "पता", - "process": "प्रक्रिया", - "noData": "कोई श्रवण पोर्ट डेटा नहीं" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "फ़ायरवॉल", - "inactive": "निष्क्रिय", - "policy": "नीति", - "rules": "नियम", - "noData": "फ़ायरवॉल से संबंधित कोई डेटा उपलब्ध नहीं है", - "action": "कार्रवाई", - "protocol": "आद्य", - "port": "पत्तन", - "source": "स्रोत", - "anywhere": "कहीं भी" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "लोड औसत", - "swap": "बदलना", - "architecture": "वास्तुकला", - "refresh": "ताज़ा करना", - "retry": "पुन: प्रयास करें" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "सेल्फ-होस्टेड एसएसएच और रिमोट डेस्कटॉप प्रबंधन", - "loginTitle": "टर्मिक्स में लॉग इन करें", - "registerTitle": "खाता बनाएं", - "forgotPassword": "पासवर्ड भूल गए?", - "rememberMe": "डिवाइस को 30 दिनों तक याद रखें (इसमें TOTP शामिल है)", - "noAccount": "क्या आपके पास खाता नहीं है?", - "hasAccount": "क्या आपके पास पहले से एक खाता मौजूद है?", - "twoFactorAuth": "दो-कारक प्रमाणीकरण", - "enterCode": "सत्यापन कोड दर्ज करें", - "backupCode": "या बैकअप कोड का उपयोग करें", - "verifyCode": "कोड सत्यापित करें", - "redirectingToApp": "ऐप पर रीडायरेक्ट किया जा रहा है...", - "sshAuthenticationRequired": "SSH प्रमाणीकरण आवश्यक है", - "sshNoKeyboardInteractive": "कीबोर्ड-इंटरैक्टिव प्रमाणीकरण अनुपलब्ध है", - "sshAuthenticationFailed": "प्रमाणीकरण विफल होना", - "sshAuthenticationTimeout": "प्रमाणीकरण समय सीमा समाप्त", - "sshNoKeyboardInteractiveDescription": "सर्वर कीबोर्ड-आधारित प्रमाणीकरण का समर्थन नहीं करता है। कृपया अपना पासवर्ड या SSH कुंजी प्रदान करें।", - "sshAuthFailedDescription": "आपके द्वारा दी गई जानकारी गलत थी। कृपया सही जानकारी के साथ पुनः प्रयास करें।", - "sshTimeoutDescription": "प्रमाणीकरण का प्रयास समय के साथ समाप्त हो गया। कृपया पुनः प्रयास करें।", - "sshProvideCredentialsDescription": "इस सर्वर से कनेक्ट करने के लिए कृपया अपने SSH क्रेडेंशियल प्रदान करें।", - "sshPasswordDescription": "इस एसएसएच कनेक्शन के लिए पासवर्ड दर्ज करें।", - "sshKeyPasswordDescription": "यदि आपकी SSH कुंजी एन्क्रिप्टेड है, तो यहां पासफ़्रेज़ दर्ज करें।", - "passphraseRequired": "पासफ़्रेज़ आवश्यक है", - "passphraseRequiredDescription": "एसएसएच कुंजी एन्क्रिप्टेड है। इसे अनलॉक करने के लिए कृपया पासफ़्रेज़ दर्ज करें।", - "back": "पीछे", - "firstUser": "पहला उपयोगकर्ता", - "firstUserMessage": "आप पहले उपयोगकर्ता हैं और आपको व्यवस्थापक बनाया जाएगा। आप साइडबार में उपयोगकर्ता ड्रॉपडाउन में व्यवस्थापक सेटिंग्स देख सकते हैं। यदि आपको लगता है कि यह कोई गलती है, तो डॉकर लॉग्स की जाँच करें या GitHub पर एक समस्या दर्ज करें।", - "external": "बाहरी", - "loginWithExternal": "बाह्य प्रदाता के साथ लॉगिन करें", - "loginWithExternalDesc": "अपने कॉन्फ़िगर किए गए बाहरी पहचान प्रदाता का उपयोग करके लॉग इन करें", - "externalNotSupportedInElectron": "इलेक्ट्रॉन ऐप में अभी तक बाहरी प्रमाणीकरण समर्थित नहीं है। कृपया OIDC लॉगिन के लिए वेब संस्करण का उपयोग करें।", - "resetPasswordButton": "पासवर्ड रीसेट", - "sendResetCode": "रीसेट कोड भेजें", - "resetCodeDesc": "पासवर्ड रीसेट कोड प्राप्त करने के लिए अपना उपयोगकर्ता नाम दर्ज करें। यह कोड डॉकर कंटेनर लॉग में दर्ज हो जाएगा।", - "resetCode": "कोड फिर ठीक करें", - "verifyCodeButton": "कोड सत्यापित करें", - "enterResetCode": "उपयोगकर्ता के लिए डॉकर कंटेनर लॉग से 6-अंकीय कोड दर्ज करें:", - "newPassword": "नया पासवर्ड", - "confirmNewPassword": "पासवर्ड की पुष्टि कीजिये", - "enterNewPassword": "उपयोगकर्ता के लिए अपना नया पासवर्ड दर्ज करें:", - "signUp": "साइन अप करें", - "desktopApp": "डेस्कटॉप ऐप", - "loggingInToDesktopApp": "डेस्कटॉप ऐप में लॉग इन करना", - "loadingServer": "सर्वर लोड हो रहा है...", - "dataLossWarning": "इस तरह से पासवर्ड रीसेट करने पर आपके सभी सेव किए गए SSH होस्ट, क्रेडेंशियल और अन्य एन्क्रिप्टेड डेटा डिलीट हो जाएंगे। इस कार्रवाई को वापस नहीं लिया जा सकता। इसका उपयोग केवल तभी करें जब आप अपना पासवर्ड भूल गए हों और लॉग इन न हों।", - "authenticationDisabled": "प्रमाणीकरण अक्षम", - "authenticationDisabledDesc": "सभी प्रमाणीकरण विधियाँ वर्तमान में निष्क्रिय हैं। कृपया अपने व्यवस्थापक से संपर्क करें।", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH होस्ट कुंजी सत्यापित करें", - "keyChangedWarning": "SSH होस्ट कुंजी बदल गई", - "firstConnectionTitle": "पहली बार इस होस्ट से कनेक्ट हो रहा है", - "firstConnectionDescription": "इस होस्ट की प्रामाणिकता स्थापित नहीं की जा सकती। कृपया सुनिश्चित करें कि फिंगरप्रिंट आपकी अपेक्षा के अनुरूप है।", - "keyChangedDescription": "आपके पिछले कनेक्शन के बाद से इस सर्वर की होस्ट कुंजी बदल गई है। यह किसी सुरक्षा समस्या का संकेत हो सकता है।", - "previousKey": "पिछली कुंजी", - "newFingerprint": "नया फिंगरप्रिंट", - "fingerprint": "अंगुली की छाप", - "verifyInstructions": "यदि आप इस होस्ट पर भरोसा करते हैं, तो जारी रखने के लिए स्वीकार करें पर क्लिक करें और भविष्य के कनेक्शनों के लिए इस फिंगरप्रिंट को सहेजें।", - "securityWarning": "सुरक्षा चेतावनी", - "acceptAndContinue": "स्वीकार करें और जारी रखें", - "acceptNewKey": "नई कुंजी स्वीकार करें और जारी रखें" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "डेटाबेस के कनेक्ट नहीं कर सके", - "unknownError": "अज्ञात त्रुटि", - "loginFailed": "लॉगिन विफल", - "failedPasswordReset": "पासवर्ड रीसेट शुरू करने में विफल", - "failedVerifyCode": "रीसेट कोड को सत्यापित करने में विफल", - "failedCompleteReset": "पासवर्ड रीसेट करने में विफलता", - "invalidTotpCode": "अमान्य टीओटीपी कोड", - "failedOidcLogin": "OIDC लॉगिन शुरू करने में विफल", - "silentSigninOidcUnavailable": "साइलेंट साइन-इन का अनुरोध किया गया था, लेकिन OIDC लॉगिन उपलब्ध नहीं है।", - "failedUserInfo": "लॉगिन के बाद उपयोगकर्ता की जानकारी प्राप्त करने में विफल रहा", - "oidcAuthFailed": "OIDC प्रमाणीकरण विफल रहा", - "invalidAuthUrl": "बैकएंड से अमान्य प्राधिकरण यूआरएल प्राप्त हुआ।", - "requiredField": "यह फ़ील्ड आवश्यक है", - "minLength": "न्यूनतम लंबाई {{min}} है", - "passwordMismatch": "सांकेतिक शब्द मेल नहीं खाते", - "passwordLoginDisabled": "उपयोगकर्ता नाम/पासवर्ड लॉगिन वर्तमान में अक्षम है", - "sessionExpired": "सत्र समाप्त हो गया है - कृपया दोबारा लॉग इन करें", - "totpRateLimited": "दर सीमित: TOTP सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया बाद में पुनः प्रयास करें।", - "totpRateLimitedWithTime": "दर सीमित: TOTP सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया पुनः प्रयास करने से पहले {{time}} सेकंड प्रतीक्षा करें।", - "resetCodeRateLimited": "दर सीमित: सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया बाद में पुनः प्रयास करें।", - "resetCodeRateLimitedWithTime": "दर सीमित: सत्यापन के बहुत अधिक प्रयास हो चुके हैं। कृपया पुनः प्रयास करने से पहले {{time}} सेकंड प्रतीक्षा करें।", - "authTokenSaveFailed": "प्रमाणीकरण टोकन सहेजने में विफल", - "failedToLoadServer": "सर्वर लोड करने में विफल" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "प्रशासक द्वारा नए खाते का पंजीकरण फिलहाल बंद कर दिया गया है। कृपया लॉग इन करें या प्रशासक से संपर्क करें।", - "userNotAllowed": "आपके खाते को पंजीकरण करने की अनुमति नहीं है। कृपया व्यवस्थापक से संपर्क करें।", - "databaseConnectionFailed": "डेटाबेस सर्वर से कनेक्ट करने में विफल।", - "resetCodeSent": "रीसेट कोड डॉकर लॉग्स को भेजा गया", - "codeVerified": "कोड सफलतापूर्वक सत्यापित हो गया", - "passwordResetSuccess": "पासवर्ड सफलतापूर्वक रीसेट हो गया", - "loginSuccess": "लॉग इन सफल", - "registrationSuccess": "सफल पंजीकरण" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "कॉन्फ़िगर किए गए SSH होस्ट को लक्षित करने वाले स्थानीय डेस्कटॉप टनल।", - "c2sTunnelPresets": "क्लाइंट टनल प्रीसेट", - "c2sTunnelPresetsDesc": "इस डेस्कटॉप क्लाइंट की स्थानीय टनल सूची को एक नामित सर्वर प्रीसेट के रूप में सहेजें, या इस क्लाइंट में एक प्रीसेट लोड करें।", - "c2sTunnelPresetsUnavailable": "क्लाइंट टनल प्रीसेट केवल डेस्कटॉप क्लाइंट में ही उपलब्ध हैं।", - "c2sPresetName": "प्रीसेट नाम", - "c2sPresetNamePlaceholder": "क्लाइंट प्रीसेट नाम", - "c2sPresetToLoad": "लोड करने के लिए पूर्व निर्धारित", - "c2sNoPresetSelected": "कोई पूर्व निर्धारित सेटिंग नहीं चुनी गई", - "c2sNoPresets": "कोई प्रीसेट सहेजा नहीं गया", - "c2sLoadPreset": "भार", - "c2sCurrentLocalConfig": "{{count}} इस डेस्कटॉप पर कॉन्फ़िगर किए गए स्थानीय क्लाइंट टनल।", - "c2sPresetSyncNote": "प्रीसेट स्पष्ट स्नैपशॉट होते हैं; इनमें से किसी एक को लोड करने से इस डेस्कटॉप क्लाइंट की स्थानीय क्लाइंट टनल सूची बदल जाती है।", - "c2sPresetSaved": "क्लाइंट टनल प्रीसेट सहेजा गया", - "c2sPresetLoaded": "क्लाइंट टनल प्रीसेट स्थानीय रूप से लोड किया गया", - "c2sPresetRenamed": "क्लाइंट टनल प्रीसेट का नाम बदल दिया गया", - "c2sPresetDeleted": "क्लाइंट टनल प्रीसेट हटा दिया गया", - "c2sPresetLoadError": "क्लाइंट टनल प्रीसेट लोड करने में विफल" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "भाषा", - "keyPassword": "कुंजी पासवर्ड", - "pastePrivateKey": "अपनी निजी कुंजी यहाँ पेस्ट करें...", - "localListenerHost": "127.0.0.1 (स्थानीय स्तर पर सुनें)", - "localTargetHost": "127.0.0.1 (इस कंप्यूटर पर लक्ष्य)", - "socksListenerHost": "127.0.0.1 (सॉक्स श्रोता)", - "enterPassword": "अपना कूटशब्द भरें", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "डैशबोर्ड", - "loading": "डैशबोर्ड लोड हो रहा है...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "सहायता", - "discord": "कलह", - "serverOverview": "सर्वर अवलोकन", - "version": "संस्करण", - "upToDate": "अप टू डेट", - "updateAvailable": "उपलब्ध अद्यतन", - "beta": "बीटा", - "uptime": "अपटाइम", - "database": "डेटाबेस", - "healthy": "स्वस्थ", - "error": "गलती", - "totalHosts": "कुल मेजबान", - "totalTunnels": "कुल सुरंगें", - "totalCredentials": "कुल प्रमाण पत्र", - "recentActivity": "हाल की गतिविधि", - "reset": "रीसेट करें", - "loadingRecentActivity": "हाल की गतिविधि लोड हो रही है...", - "noRecentActivity": "कोई हालिया गतिविधि नहीं", - "quickActions": "त्वरित कार्रवाइयां", - "addHost": "होस्ट जोड़ें", - "addCredential": "क्रेडेंशियल जोड़ें", - "adminSettings": "व्यवस्थापक सेटिंग्स", - "userProfile": "उपयोगकर्ता रूपरेखा", - "serverStats": "सर्वर आँकड़े", - "loadingServerStats": "सर्वर के आंकड़े लोड हो रहे हैं...", - "noServerData": "कोई सर्वर डेटा उपलब्ध नहीं है", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", - "ram": "टक्कर मारना", - "customizeLayout": "डैशबोर्ड को अनुकूलित करें", - "dashboardSettings": "डैशबोर्ड सेटिंग्स", - "enableDisableCards": "कार्ड सक्षम/अक्षम करें", - "resetLayout": "वितथ पर ले जाएं", - "serverOverviewCard": "सर्वर अवलोकन", - "recentActivityCard": "हाल की गतिविधि", - "networkGraphCard": "नेटवर्क ग्राफ", - "networkGraph": "नेटवर्क ग्राफ", - "quickActionsCard": "त्वरित कार्रवाइयां", - "serverStatsCard": "सर्वर आँकड़े", - "panelMain": "मुख्य", - "panelSide": "ओर", - "justNow": "बस अब" + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "स्थिर", - "hostsOnline": "ऑनलाइन होस्ट", - "activeTunnels": "सक्रिय सुरंगें", - "registerNewServer": "एक नया सर्वर पंजीकृत करें", - "storeSshKeysOrPasswords": "SSH कुंजी या पासवर्ड संग्रहीत करें", - "manageUsersAndRoles": "उपयोगकर्ताओं और भूमिकाओं का प्रबंधन करें", - "manageYourAccount": "अपना खाता प्रबंधित करें", - "hostStatus": "मेज़बान स्थिति", - "noHostsConfigured": "कोई होस्ट कॉन्फ़िगर नहीं किया गया है", - "online": "ऑनलाइन", - "offline": "ऑफलाइन", - "onlineLower": "ऑनलाइन", - "nodes": "{{count}} नोड्स", - "add": "जोड़ना:", - "commandPalette": "कमांड पैलेट", - "done": "हो गया", - "editModeInstructions": "कार्डों को खींचकर उनका क्रम बदलें · कॉलम विभाजक को खींचकर कॉलम का आकार बदलें · कार्ड के निचले किनारे को खींचकर उसकी ऊंचाई बदलें · हटाने के लिए ट्रैश में डालें", - "empty": "खाली", - "clear": "स्पष्ट" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "होस्ट जोड़ें", - "addGroup": "समूह जोड़ें", - "addLink": "लिंक जोड़ें", - "zoomIn": "ज़ूम इन", - "zoomOut": "ज़ूम आउट", - "resetView": "फिर से देख्ना", - "selectHost": "होस्ट का चयन करें", - "chooseHost": "एक मेज़बान चुनें...", - "parentGroup": "अभिभावक समूह", - "noGroup": "कोई समूह नहीं", - "groupName": "समूह नाम", - "color": "रंग", - "source": "स्रोत", - "target": "लक्ष्य", - "moveToGroup": "समूह में ले जाएं", - "selectGroup": "समूह का चयन करें...", - "addConnection": "कनेक्शन जोड़ें", - "hostDetails": "मेज़बान का विवरण", - "removeFromGroup": "समूह से हटाएँ", - "addHostHere": "यहां होस्ट जोड़ें", - "editGroup": "समूह संपादित करें", - "delete": "मिटाना", - "add": "जोड़ना", - "create": "बनाएं", - "move": "कदम", - "connect": "जोड़ना", - "createGroup": "समूह बनाना", - "selectSourcePlaceholder": "स्रोत का चयन करें...", - "selectTargetPlaceholder": "लक्ष्य का चयन करें...", - "invalidFile": "अमान्य लेख्यपत्र", - "hostAlreadyExists": "होस्ट पहले से ही टोपोलॉजी में मौजूद है", - "connectionExists": "कनेक्शन पहले से मौजूद है", - "unknown": "अज्ञात", - "name": "नाम", - "ip": "आई पी", - "status": "स्थिति", - "failedToAddNode": "नोड जोड़ने में विफल", - "sourceDifferentFromTarget": "स्रोत और लक्ष्य अलग-अलग होने चाहिए", - "exportJSON": "JSON निर्यात करें", - "importJSON": "JSON आयात करें", - "terminal": "टर्मिनल", - "fileManager": "फ़ाइल मैनेजर", - "tunnel": "सुरंग", - "docker": "डाक में काम करनेवाला मज़दूर", - "serverStats": "सर्वर आँकड़े", - "noNodes": "अभी तक कोई नोड नहीं" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "इस होस्ट के लिए डॉकर सक्षम नहीं है।", - "validating": "डॉकर का सत्यापन किया जा रहा है...", - "connecting": "कनेक्ट हो रहा है...", - "error": "गलती", - "version": "डॉकर {{version}}", - "connectionFailed": "डॉकर से कनेक्ट करने में विफल", - "containerStarted": "कंटेनर {{name}} शुरू हो गया", - "failedToStartContainer": "कंटेनर {{name}} को प्रारंभ करने में विफल", - "containerStopped": "कंटेनर {{name}} रुक गया", - "failedToStopContainer": "कंटेनर {{name}} को रोकने में विफल", - "containerRestarted": "कंटेनर {{name}} पुनः आरंभ हुआ", - "failedToRestartContainer": "कंटेनर {{name}} को पुनः आरंभ करने में विफल रहा", - "containerPaused": "कंटेनर {{name}} रुका हुआ है", - "containerUnpaused": "कंटेनर {{name}} अनपॉज़ किया गया", - "failedToTogglePauseContainer": "कंटेनर {{name}} के लिए पॉज़ स्थिति को टॉगल करने में विफल।", - "containerRemoved": "कंटेनर {{name}} हटा दिया गया", - "failedToRemoveContainer": "कंटेनर {{name}} को हटाने में विफल", - "image": "छवि", - "ports": "बंदरगाहों", - "noPorts": "कोई बंदरगाह नहीं", - "start": "शुरू", - "confirmRemoveContainer": "क्या आप वाकई कंटेनर '{{name}}' को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।", - "runningContainerWarning": "चेतावनी: यह कंटेनर वर्तमान में चल रहा है। इसे हटाने से पहले यह कंटेनर बंद हो जाएगा।", - "loadingContainers": "कंटेनर लोड हो रहे हैं...", - "manager": "डॉकर मैनेजर", - "autoRefresh": "ऑटो रिफ्रेश", - "timestamps": "मुहर", - "lines": "पंक्तियां", - "filterLogs": "लॉग फ़िल्टर करें...", - "refresh": "ताज़ा करना", - "download": "डाउनलोड करना", - "clear": "स्पष्ट", - "logsDownloaded": "लॉग सफलतापूर्वक डाउनलोड हो गए", - "last50": "पिछले 50", - "last100": "पिछले 100", - "last500": "अंतिम 500", - "last1000": "पिछले 1000", - "allLogs": "सभी लॉग", - "noLogsMatching": "\"{{query}} \" से मेल खाने वाली कोई लॉग नहीं मिली", - "noLogsAvailable": "कोई लॉग उपलब्ध नहीं हैं", - "noContainersFound": "कोई कंटेनर नहीं मिला", - "noContainersFoundHint": "इस होस्ट पर कोई डॉकर कंटेनर उपलब्ध नहीं हैं।", - "searchPlaceholder": "कंटेनर खोजें...", - "allStatuses": "सभी स्थितियाँ", - "stateRunning": "दौड़ना", - "statePaused": "रुका हुआ", - "stateExited": "बाहर निकल गया", - "stateRestarting": "पुन: प्रारंभ हो", - "noContainersMatchFilters": "आपके फ़िल्टर से कोई कंटेनर मेल नहीं खाता।", - "noContainersMatchFiltersHint": "अपनी खोज या फ़िल्टर के मानदंडों को समायोजित करने का प्रयास करें", - "failedToFetchStats": "कंटेनर सांख्यिकी प्राप्त करने में विफल", - "containerNotRunning": "कंटेनर नहीं चल रहा है", - "startContainerToViewStats": "आंकड़े देखने के लिए कंटेनर शुरू करें", - "loadingStats": "आंकड़े लोड हो रहे हैं...", - "errorLoadingStats": "सांख्यिकी लोड करने में त्रुटि", - "noStatsAvailable": "कोई आंकड़े उपलब्ध नहीं हैं", - "cpuUsage": "सीपीयू उपयोग", - "current": "मौजूदा", - "memoryUsage": "स्मृति प्रयोग", - "networkIo": "नेटवर्क I/O", - "input": "इनपुट", - "output": "उत्पादन", - "blockIo": "ब्लॉक I/O", - "read": "पढ़ना", - "write": "लिखना", - "pids": "पीआईडी", - "containerInformation": "कंटेनर जानकारी", - "name": "नाम", - "id": "पहचान", - "state": "राज्य", - "containerMustBeRunning": "कंसोल तक पहुँचने के लिए कंटेनर का चालू होना आवश्यक है।", - "verificationCodePrompt": "सत्यापन कोड दर्ज करें", - "totpVerificationFailed": "टीओटीपी सत्यापन विफल रहा। कृपया पुनः प्रयास करें।", - "warpgateVerificationFailed": "वॉरपगेट प्रमाणीकरण विफल रहा। कृपया पुनः प्रयास करें।", - "connectedTo": "{{containerName}} से जुड़ा हुआ", - "disconnected": "डिस्कनेक्ट किया गया", - "consoleError": "कंसोल त्रुटि", - "errorMessage": "त्रुटि: {{message}}", - "failedToConnect": "कंटेनर से कनेक्ट करने में विफल", - "console": "सांत्वना देना", - "selectShell": "शेल का चयन करें", - "bash": "दे घुमा के", - "sh": "श", - "ash": "राख", - "connect": "जोड़ना", - "disconnect": "डिस्कनेक्ट", - "notConnected": "जुड़े नहीं हैं", - "clickToConnect": "शेल सेशन शुरू करने के लिए कनेक्ट पर क्लिक करें", - "connectingTo": "{{containerName}} से कनेक्ट हो रहा है...", - "containerNotFound": "कंटेनर नहीं मिला", - "backToList": "सूची पर वापस जाएं", - "logs": "लॉग्स", - "stats": "आँकड़े", - "consoleTab": "सांत्वना देना", - "startContainerToAccess": "कंसोल तक पहुँचने के लिए कंटेनर को प्रारंभ करें" + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "सामान्य", - "sectionOidc": "ओआईडीसी", - "sectionUsers": "उपयोगकर्ताओं", - "sectionSessions": "सत्रों", - "sectionRoles": "भूमिकाएँ", - "sectionDatabase": "डेटाबेस", - "sectionApiKeys": "एपीआई कुंजी", - "allowRegistration": "उपयोगकर्ता पंजीकरण की अनुमति दें", - "allowRegistrationDesc": "नए उपयोगकर्ताओं को स्वयं पंजीकरण करने दें", - "allowPasswordLogin": "पासवर्ड लॉगिन की अनुमति दें", - "allowPasswordLoginDesc": "उपयोगकर्ता नाम/पासवर्ड लॉगिन", - "oidcAutoProvision": "ओआईडीसी ऑटो-प्रोविजन", - "oidcAutoProvisionDesc": "पंजीकरण अक्षम होने पर भी OIDC उपयोगकर्ताओं के लिए स्वचालित रूप से खाते बनाएँ", - "allowPasswordReset": "पासवर्ड रीसेट की अनुमति दें", - "allowPasswordResetDesc": "डॉकर लॉग के माध्यम से कोड रीसेट करें", - "sessionTimeout": "सेशन खत्म", - "hours": "घंटे", - "sessionTimeoutRange": "न्यूनतम 1 घंटा · अधिकतम 720 घंटे", - "monitoringDefaults": "मॉनिटरिंग डिफ़ॉल्ट", - "statusCheck": "स्थिति जांच", - "metrics": "मेट्रिक्स", - "sec": "सेकंड", - "logLevel": "लॉग स्तर", - "enableGuacamole": "गुआकामोल को सक्षम करें", - "enableGuacamoleDesc": "आरडीपी/वीएनसी रिमोट डेस्कटॉप", - "guacdUrl": "गुआकडी यूआरएल", - "oidcDescription": "SSO के लिए OpenID Connect को कॉन्फ़िगर करें। जिन फ़ील्ड्स पर * का निशान लगा है, उन्हें भरना अनिवार्य है।", - "oidcClientId": "क्लाइंट आईडी", - "oidcClientSecret": "ग्राहक रहस्य", - "oidcAuthUrl": "प्राधिकरण यूआरएल", - "oidcIssuerUrl": "जारीकर्ता यूआरएल", - "oidcTokenUrl": "टोकन यूआरएल", - "oidcUserIdentifier": "उपयोगकर्ता पहचानकर्ता पथ", - "oidcDisplayName": "नाम पथ प्रदर्शित करें", - "oidcScopes": "कार्यक्षेत्र", - "oidcUserinfoUrl": "उपयोगकर्ता जानकारी यूआरएल को ओवरराइड करें", - "oidcAllowedUsers": "अनुमत उपयोगकर्ता", - "oidcAllowedUsersDesc": "एक पंक्ति में एक ही ईमेल लिखें। सभी ईमेल भेजने के लिए इसे खाली छोड़ दें।", - "removeOidc": "निकालना", - "usersCount": "{{count}} उपयोगकर्ता", - "createUser": "बनाएं", - "newRole": "नयी भूमिका", - "roleName": "नाम", - "roleDisplayName": "प्रदर्शित होने वाला नाम", - "roleDescription": "विवरण", - "rolesCount": "{{count}} भूमिकाएँ", - "createRole": "बनाएं", - "creating": "सृजन...", - "exportDatabase": "डेटाबेस निर्यात करें", - "exportDatabaseDesc": "सभी होस्ट, क्रेडेंशियल और सेटिंग्स का बैकअप डाउनलोड करें", - "export": "निर्यात", - "exporting": "निर्यात हो रहा है...", - "importDatabase": "डेटाबेस आयात करें", - "importDatabaseDesc": ".sqlite बैकअप फ़ाइल से पुनर्स्थापित करें", - "importDatabaseSelected": "चयनित: {{name}}", - "selectFile": "फ़ाइल का चयन करें", - "changeFile": "परिवर्तन", - "import": "आयात", - "importing": "आयात हो रहा है...", - "apiKeysCount": "{{count}} कुंजियाँ", - "newApiKey": "नई एपीआई कुंजी", - "apiKeyCreatedWarning": "कुंजी बन गई है - इसे अभी कॉपी कर लें, यह दोबारा नहीं दिखाई देगी।", - "apiKeyName": "नाम", - "apiKeyUser": "उपयोगकर्ता", - "apiKeySelectUser": "किसी उपयोगकर्ता का चयन करें...", - "apiKeyExpiresAt": "समाप्ति तिथि", - "createKey": "कुंजी बनाएँ", - "apiKeyNoExpiry": "कोई समाप्ति नहीं", - "revokedBadge": "निरस्त किया गया", - "authTypeDual": "दोहरी प्राधिकरण", - "authTypeOidc": "ओआईडीसी", - "authTypeLocal": "स्थानीय", - "adminStatusAdministrator": "प्रशासक", - "adminStatusRegularUser": "नियमित उपयोगकर्ता", - "adminBadge": "व्यवस्थापक", - "systemBadge": "एसवाईएस", - "customBadge": "रिवाज़", - "youBadge": "आप", - "sessionsActive": "{{count}} सक्रिय", - "sessionActive": "सक्रिय: {{time}}", - "sessionExpires": "एक्सप: {{time}}", - "revokeAll": "सभी", - "revokeAllSessionsSuccess": "उपयोगकर्ता के लिए सभी सत्र रद्द कर दिए गए हैं।", - "revokeAllSessionsFailed": "सत्र रद्द करने में विफल", - "revokeSessionFailed": "सत्र रद्द करने में विफल", - "addRole": "भूमिका जोड़ें", - "noCustomRoles": "कोई कस्टम भूमिकाएँ परिभाषित नहीं हैं", - "removeRoleFailed": "भूमिका हटाने में विफल", - "assignRoleFailed": "भूमिका निर्धारित करने में विफल", - "deleteRoleFailed": "भूमिका हटाने में विफल", - "userAdminAccess": "प्रशासक", - "userAdminAccessDesc": "सभी प्रशासनिक सेटिंग्स तक पूर्ण पहुंच", - "userRoles": "भूमिकाएँ", - "revokeAllUserSessions": "सभी सत्र रद्द करें", - "revokeAllUserSessionsDesc": "सभी डिवाइसों पर पुनः लॉगिन करने के लिए बाध्य करें", - "revoke": "रद्द करना", - "deleteUserWarning": "इस उपयोगकर्ता को हटाना स्थायी है।", - "deleteUser": "{{username}} को हटाएँ", - "deleting": "हटा रहा हूँ...", - "deleteUserFailed": "उपयोगकर्ता को हटाने में विफल", - "deleteUserSuccess": "उपयोगकर्ता \"{{username}}\" हटा दिया गया", - "deleteRoleSuccess": "भूमिका \"{{name}}\" हटा दी गई", - "revokeKeySuccess": "कुंजी \"{{name}}\" निरस्त कर दी गई", - "revokeKeyFailed": "कुंजी रद्द करने में विफल", - "copiedToClipboard": "क्लिपबोर्ड पर कॉपी हो गया", - "done": "हो गया", - "createUserTitle": "उपयोगकर्ता बनाएँ", - "createUserDesc": "एक नया स्थानीय खाता बनाएं।", - "createUserUsername": "उपयोगकर्ता नाम", - "createUserPassword": "पासवर्ड", - "createUserPasswordHint": "कम से कम 6 अक्षर।", - "createUserEnterUsername": "उपयोगकर्ता नाम दर्ज करें", - "createUserEnterPassword": "पास वर्ड दर्ज करें", - "createUserSubmit": "उपयोगकर्ता बनाएँ", - "editUserTitle": "उपयोगकर्ता प्रबंधित करें: {{username}}", - "editUserDesc": "भूमिकाओं, व्यवस्थापक स्थिति, सत्रों और खाता सेटिंग्स को संपादित करें।", - "editUserUsername": "उपयोगकर्ता नाम", - "editUserAuthType": "प्राधिकरण प्रकार", - "editUserAdminStatus": "व्यवस्थापक स्थिति", - "editUserUserId": "उपयोगकर्ता पहचान", - "linkAccountTitle": "OIDC को पासवर्ड खाते से लिंक करें", - "linkAccountDesc": "OIDC खाते {{username}} को मौजूदा स्थानीय खाते के साथ मर्ज करें।", - "linkAccountWarningTitle": "यह करेगा:", - "linkAccountEffect1": "केवल OIDC खाते को हटाएँ", - "linkAccountEffect2": "लक्ष्य खाते में OIDC लॉगिन जोड़ें", - "linkAccountEffect3": "OIDC और पासवर्ड लॉगिन दोनों की अनुमति दें", - "linkAccountTargetUsername": "लक्ष्य उपयोगकर्ता नाम", - "linkAccountTargetPlaceholder": "लिंक करने के लिए स्थानीय खाते का उपयोगकर्ता नाम दर्ज करें", - "linkAccounts": "खाते लिंक करें", - "linkAccountSuccess": "\"{{username}} \" से जुड़ा OIDC खाता", - "linkAccountFailed": "OIDC खाते को लिंक करने में विफल।", - "linkAccountInProgress": "लिंक हो रहा है...", - "saving": "सहेजा जा रहा है...", - "updateRegistrationFailed": "पंजीकरण सेटिंग को अपडेट करने में विफल", - "updatePasswordLoginFailed": "पासवर्ड लॉगिन सेटिंग अपडेट करने में विफल", - "updateOidcAutoProvisionFailed": "OIDC ऑटो-प्रोविजन सेटिंग को अपडेट करने में विफलता", - "updatePasswordResetFailed": "पासवर्ड रीसेट सेटिंग को अपडेट करने में विफलता", - "sessionTimeoutRange2": "सेशन टाइमआउट 1 से 720 घंटे के बीच होना चाहिए।", - "sessionTimeoutSaved": "सेशन टाइमआउट सहेजा गया", - "sessionTimeoutSaveFailed": "सेशन टाइमआउट को सहेजने में विफल", - "monitoringIntervalInvalid": "अमान्य अंतराल मान", - "monitoringSaved": "मॉनिटरिंग सेटिंग्स सहेजी गईं", - "monitoringSaveFailed": "मॉनिटरिंग सेटिंग्स सहेजने में विफल", - "guacamoleSaved": "गुआकामोल की सेटिंग सहेज ली गई", - "guacamoleSaveFailed": "गुआकामोल की सेटिंग्स सहेजने में विफल।", - "guacamoleUpdateFailed": "गुआकामोल सेटिंग को अपडेट करने में विफलता", - "logLevelUpdateFailed": "लॉग स्तर को अपडेट करने में विफल", - "oidcSaved": "OIDC कॉन्फ़िगरेशन सहेजा गया", - "oidcSaveFailed": "OIDC कॉन्फ़िगरेशन को सहेजने में विफल", - "oidcRemoved": "OIDC कॉन्फ़िगरेशन हटा दिया गया", - "oidcRemoveFailed": "OIDC कॉन्फ़िगरेशन को हटाने में विफल", - "createUserRequired": "उपयोगकर्ता नाम और पासवर्ड आवश्यक हैं", - "createUserPasswordTooShort": "पासवर्ड कम से कम 6 अंकों का होना चाहिए", - "createUserSuccess": "उपयोगकर्ता \"{{username}}\" ने बनाया", - "createUserFailed": "उपयोगकर्ता बनाने में विफल", - "updateAdminStatusFailed": "व्यवस्थापक की स्थिति अपडेट करने में विफल", - "allSessionsRevoked": "सभी सत्र रद्द कर दिए गए", - "revokeSessionsFailed": "सत्र रद्द करने में विफल", - "createRoleRequired": "नाम और डिस्प्ले नाम आवश्यक हैं", - "createRoleSuccess": "भूमिका \"{{name}}\" बनाई गई", - "createRoleFailed": "भूमिका बनाने में विफल", - "apiKeyNameRequired": "कुंजी का नाम आवश्यक है", - "apiKeyUserRequired": "उपयोगकर्ता आईडी आवश्यक है", - "apiKeyCreatedSuccess": "एपीआई कुंजी \"{{name}}\" बनाई गई", - "apiKeyCreateFailed": "एपीआई कुंजी बनाने में विफल", - "exportSuccess": "डेटाबेस सफलतापूर्वक निर्यात हो गया", - "exportFailed": "डेटाबेस निर्यात विफल रहा", - "importSelectFile": "कृपया पहले एक फ़ाइल चुनें", - "importCompleted": "आयात पूर्ण हुआ: {{total}} आइटम आयात किए गए, {{skipped}} छोड़े गए", - "importFailed": "आयात विफल: {{error}}", - "importError": "डेटाबेस आयात विफल रहा" + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "मेज़बान", - "hostPlaceholder": "192.168.1.1 या example.com", - "portLabel": "पत्तन", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "उपयोगकर्ता नाम", - "usernamePlaceholder": "उपयोगकर्ता नाम", - "authLabel": "प्रमाणीकरण", - "passwordLabel": "पासवर्ड", - "passwordPlaceholder": "पासवर्ड", - "privateKeyLabel": "निजी चाबी", - "privateKeyPlaceholder": "निजी कुंजी पेस्ट करें...", - "credentialLabel": "क्रेडेंशियल", - "credentialPlaceholder": "सहेजे गए क्रेडेंशियल का चयन करें", - "connectToTerminal": "टर्मिनल से कनेक्ट करें", - "connectToFiles": "फ़ाइलों से कनेक्ट करें" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "कोई टर्मिनल चयनित नहीं है", - "noTerminalSelectedHint": "कमांड हिस्ट्री देखने के लिए SSH टर्मिनल टैब खोलें।", - "searchPlaceholder": "खोज इतिहास...", - "clearAll": "सभी साफ करें", - "noHistoryEntries": "कोई इतिहास प्रविष्टियाँ नहीं हैं", - "trackingDisabled": "इतिहास ट्रैकिंग अक्षम है", - "trackingDisabledHint": "कमांड रिकॉर्ड करने के लिए इसे अपनी प्रोफ़ाइल सेटिंग में सक्षम करें।" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "मुख्य रिकॉर्डिंग", - "recordToTerminals": "टर्मिनलों पर रिकॉर्ड करें", - "selectAll": "सभी", - "selectNone": "कोई नहीं", - "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं है", - "selectTerminalsAbove": "ऊपर दिए गए टर्मिनलों का चयन करें", - "broadcastInputPlaceholder": "यहां टाइप करके कीस्ट्रोक्स को प्रसारित करें...", - "stopRecording": "रिकॉर्डिंग बंद करें", - "startRecording": "रिकॉर्डिंग शुरू करें", - "settingsTitle": "सेटिंग्स", - "enableRightClickCopyPaste": "राइट-क्लिक कॉपी/पेस्ट को सक्षम करें" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "लेआउट", - "selectLayoutAbove": "ऊपर दिए गए लेआउट में से एक चुनें", - "selectLayoutHint": "प्रदर्शित होने वाले पैनलों की संख्या चुनें", - "panesTitle": "फलक", - "openTabsTitle": "टैब खोलें", - "dragTabsHint": "टैब को ऊपर दिए गए पैन में खींचें, या क्विक असाइनमेंट का उपयोग करें।", - "dropHere": "यहां छोड़ें", - "emptyPane": "खाली", - "dashboard": "डैशबोर्ड", - "clearSplitScreen": "स्प्लिट स्क्रीन साफ़ करें", - "quickAssign": "त्वरित असाइनमेंट", - "alreadyAssigned": "फलक {{index}}", - "splitTab": "स्प्लिट टैब", - "addToSplit": "स्प्लिट में जोड़ें", - "removeFromSplit": "स्प्लिट से निकालें", - "assignToPane": "फलक को असाइन करें" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "स्निपेट बनाएं", - "createSnippetDescription": "त्वरित निष्पादन के लिए एक नया कमांड स्निपेट बनाएं", - "nameLabel": "नाम", - "namePlaceholder": "उदाहरण के लिए, Nginx को रीस्टार्ट करें", - "descriptionLabel": "विवरण", - "descriptionPlaceholder": "वैकल्पिक विवरण", - "optional": "वैकल्पिक", - "folderLabel": "फ़ोल्डर", - "noFolder": "कोई फ़ोल्डर नहीं (अवर्गीकृत)", - "commandLabel": "आज्ञा", - "commandPlaceholder": "उदाहरण के लिए, sudo systemctl restart nginx", - "cancel": "रद्द करना", - "createSnippetButton": "स्निपेट बनाएं", - "createFolderTitle": "फ़ोल्डर बनाएँ", - "createFolderDescription": "अपने लेखों के अंशों को फ़ोल्डरों में व्यवस्थित करें।", - "folderNameLabel": "फ़ोल्डर का नाम", - "folderNamePlaceholder": "उदाहरण के लिए, सिस्टम कमांड, डॉकर स्क्रिप्ट", - "folderColorLabel": "फ़ोल्डर का रंग", - "folderIconLabel": "फ़ोल्डर आइकन", - "previewLabel": "पूर्व दर्शन", - "folderNameFallback": "फ़ोल्डर का नाम", - "createFolderButton": "फ़ोल्डर बनाएँ", - "targetTerminals": "लक्ष्य टर्मिनल", - "selectAll": "सभी", - "selectNone": "कोई नहीं", - "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं है", - "searchPlaceholder": "खोज के अंश...", - "newSnippet": "नया अंश", - "newFolder": "नया फ़ोल्डर", - "run": "दौड़ना", - "noSnippetsInFolder": "इस फ़ोल्डर में कोई स्निपेट नहीं हैं", - "uncategorized": "अवर्गीकृत", - "editSnippetTitle": "स्निपेट संपादित करें", - "editSnippetDescription": "इस कमांड स्निपेट को अपडेट करें", - "saveSnippetButton": "परिवर्तनों को सुरक्षित करें", - "createSuccess": "स्निपेट सफलतापूर्वक बनाया गया", - "createFailed": "स्निपेट बनाने में विफल", - "updateSuccess": "स्निपेट सफलतापूर्वक अपडेट हो गया", - "updateFailed": "स्निपेट को अपडेट करने में विफल", - "deleteFailed": "स्निपेट को हटाने में विफल", - "folderCreateSuccess": "फ़ोल्डर सफलतापूर्वक बनाया गया", - "folderCreateFailed": "फ़ोल्डर बनाने में विफल", - "editFolderTitle": "फ़ोल्डर संपादित करें", - "editFolderDescription": "इस फ़ोल्डर का नाम बदलें या इसका स्वरूप बदलें", - "saveFolderButton": "परिवर्तनों को सुरक्षित करें", - "editFolder": "फ़ोल्डर संपादित करें", - "deleteFolder": "फ़ोल्डर हटाएं", - "folderDeleteSuccess": "फ़ोल्डर \"{{name}}\" हटा दिया गया", - "folderDeleteFailed": "फ़ोल्डर को हटाने में विफल", - "folderEditSuccess": "फ़ोल्डर सफलतापूर्वक अपडेट हो गया", - "folderEditFailed": "फ़ोल्डर को अपडेट करने में विफल", - "confirmRunMessage": "\"{{name}} \" चलाएँ?", - "confirmRunButton": "दौड़ना", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", - "copySuccess": "क्लिपबोर्ड पर \"{{name}}\" कॉपी किया गया", - "shareTitle": "स्निपेट साझा करें", - "shareUser": "उपयोगकर्ता", - "shareRole": "भूमिका", - "selectUser": "किसी उपयोगकर्ता का चयन करें...", - "selectRole": "एक भूमिका चुनें...", - "shareSuccess": "अंश सफलतापूर्वक साझा किया गया", - "shareFailed": "अंश साझा करने में विफल", - "revokeSuccess": "पहुँच रद्द कर दी गई", - "revokeFailed": "पहुँच रद्द करने में विफल", - "currentAccess": "वर्तमान पहुंच", - "shareLoadError": "शेयर डेटा लोड करने में विफल", - "loading": "लोड हो रहा है...", - "close": "बंद करना", - "reorderFailed": "स्निपेट क्रम को सहेजने में विफल" + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "खाता", - "sectionAppearance": "उपस्थिति", - "sectionSecurity": "सुरक्षा", - "sectionApiKeys": "एपीआई कुंजी", - "sectionC2sTunnels": "सी2एस सुरंगें", - "usernameLabel": "उपयोगकर्ता नाम", - "roleLabel": "भूमिका", - "roleAdministrator": "प्रशासक", - "authMethodLabel": "प्रमाणीकरण विधि", - "authMethodLocal": "स्थानीय", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "पर", - "twoFaOff": "बंद", - "versionLabel": "संस्करण", - "deleteAccount": "खाता हटा दो", - "deleteAccountDescription": "अपना खाता स्थायी रूप से हटाएँ", - "deleteButton": "मिटाना", - "deleteAccountPermanent": "यह कार्रवाई स्थायी है और इसे पूर्ववत नहीं किया जा सकता है।", - "deleteAccountWarning": "सभी सेशन, होस्ट, क्रेडेंशियल और सेटिंग्स स्थायी रूप से हटा दिए जाएंगे।", - "confirmPasswordDeletePlaceholder": "पुष्टि करने के लिए अपना पासवर्ड दर्ज करें", - "languageLabel": "भाषा", - "themeLabel": "विषय", - "fontSizeLabel": "फ़ॉन्ट आकार", - "accentColorLabel": "एक्सेंट रंग", - "settingsTerminal": "टर्मिनल", - "commandAutocomplete": "कमांड ऑटो-कंप्लीट", - "commandAutocompleteDesc": "टाइप करते समय ऑटो-कंप्लीट दिखाएं", - "historyTracking": "इतिहास ट्रैकिंग", - "historyTrackingDesc": "टर्मिनल कमांड को ट्रैक करें", - "syntaxHighlighting": "वाक्य - विन्यास पर प्रकाश डालना", - "syntaxHighlightingDesc": "टर्मिनल आउटपुट को हाइलाइट करें", - "commandPalette": "कमांड पैलेट", - "commandPaletteDesc": "कीबोर्ड शॉर्टकट सक्षम करें", - "reopenTabsOnLogin": "लॉगिन करने पर टैब दोबारा खोलें", - "reopenTabsOnLoginDesc": "लॉग इन करने या पेज को रिफ्रेश करने पर, यहां तक कि किसी अन्य डिवाइस से भी, आपके खुले टैब रीस्टोर हो जाएंगे।", - "confirmTabClose": "टैब बंद करने की पुष्टि करें", - "confirmTabCloseDesc": "टर्मिनल टैब बंद करने से पहले पूछें", - "settingsSidebar": "साइड बार", - "showHostTags": "शो होस्ट टैग", - "showHostTagsDesc": "होस्ट सूची में टैग प्रदर्शित करें", - "hostTrayOnClick": "होस्ट की कार्रवाइयों का विस्तार करने के लिए क्लिक करें", - "hostTrayOnClickDesc": "कनेक्शन बटन हमेशा दिखाएं; प्रबंधन विकल्पों को विस्तारित करने के लिए होवर करने के बजाय क्लिक करें।", - "pinAppRail": "पिन ऐप रेल", - "pinAppRailDesc": "होवर करने पर विस्तार करने के बजाय, बाएँ साइडबार ऐप रेल को हमेशा विस्तारित रखें।", - "settingsSnippets": "स्निपेट्स", - "foldersCollapsed": "फ़ोल्डर बंद कर दिए गए", - "foldersCollapsedDesc": "फ़ोल्डर डिफ़ॉल्ट रूप से बंद हो जाते हैं", - "confirmExecution": "निष्पादन की पुष्टि करें", - "confirmExecutionDesc": "स्निपेट चलाने से पहले पुष्टि करें", - "settingsUpdates": "अपडेट", - "disableUpdateChecks": "अपडेट जांच अक्षम करें", - "disableUpdateChecksDesc": "अपडेट की जाँच करना बंद करें", - "totpAuthenticator": "टीओटीपी प्रमाणीकरणकर्ता", - "totpEnabled": "2FA सक्षम है", - "totpDisabled": "लॉगिन सुरक्षा को और बढ़ाएं", - "disable": "अक्षम करना", - "enable": "सक्षम", - "setupTotp": "टीओटीपी सेटअप करें", - "qrCode": "क्यू आर संहिता", - "totpInstructions": "QR कोड स्कैन करें या अपने प्रमाणीकरण ऐप में गुप्त कोड दर्ज करें, फिर 6 अंकों का कोड दर्ज करें।", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "सत्यापित करें", - "changePassword": "पासवर्ड बदलें", - "currentPasswordLabel": "वर्तमान पासवर्ड", - "currentPasswordPlaceholder": "वर्तमान पासवर्ड", - "newPasswordLabel": "नया पासवर्ड", - "newPasswordPlaceholder": "नया पासवर्ड", - "confirmPasswordLabel": "नए पासवर्ड की पुष्टि करें", - "confirmPasswordPlaceholder": "नए पासवर्ड की पुष्टि करें", - "updatePassword": "पासवर्ड अपडेट करें", - "createApiKeyTitle": "एपीआई कुंजी बनाएं", - "createApiKeyDescription": "प्रोग्रामेटिक एक्सेस के लिए एक नई एपीआई कुंजी जनरेट करें।", - "apiKeyNameLabel": "नाम", - "apiKeyNamePlaceholder": "उदाहरण के लिए सीआई पाइपलाइन", - "expiryDateLabel": "समाप्ति तिथि", - "optional": "वैकल्पिक", - "cancel": "रद्द करना", - "createKey": "कुंजी बनाएँ", - "apiKeyCount": "{{count}} कुंजियाँ", - "newKey": "नई कुंजी", - "noApiKeys": "अभी तक कोई एपीआई कुंजी उपलब्ध नहीं है।", - "apiKeyActive": "सक्रिय", - "apiKeyUsageHint": "अपनी चाबी भी साथ में रखें।", - "apiKeyUsageHintHeader": "शीर्षक।", - "apiKeyPermissionsHint": "कुंजी बनाने वाले उपयोगकर्ता की अनुमतियों को विरासत में प्राप्त करती है।", - "roleUser": "उपयोगकर्ता", - "authMethodDual": "दोहरी प्राधिकरण", - "authMethodOidc": "ओआईडीसी", - "totpSetupFailed": "TOTP सेटअप शुरू करने में विफल", - "totpEnter6Digits": "6 अंकों का कोड दर्ज करें", - "totpEnabledSuccess": "दो-कारक प्रमाणीकरण सक्षम", - "totpInvalidCode": "अवैध कोड, पुन: प्रयास करें", - "totpDisableInputRequired": "अपना TOTP कोड या पासवर्ड दर्ज करें", - "totpDisabledSuccess": "दो-कारक प्रमाणीकरण अक्षम", - "totpDisableFailed": "2FA को अक्षम करने में विफल।", - "totpDisableTitle": "2FA को अक्षम करें", - "totpDisablePlaceholder": "टीओटीपी कोड या पासवर्ड दर्ज करें", - "totpDisableConfirm": "2FA को अक्षम करें", - "totpContinueVerify": "सत्यापन जारी रखें", - "totpVerifyTitle": "कोड सत्यापित करें", - "totpBackupTitle": "बैकअप कोड", - "totpDownloadBackup": "बैकअप कोड डाउनलोड करें", - "done": "हो गया", - "secretCopied": "गुप्त कुंजी क्लिपबोर्ड पर कॉपी हो गई", - "apiKeyNameRequired": "कुंजी का नाम आवश्यक है", - "apiKeyCreated": "एपीआई कुंजी \"{{name}}\" बनाई गई", - "apiKeyCreateFailed": "एपीआई कुंजी बनाने में विफल", - "apiKeyUser": "उपयोगकर्ता", - "apiKeyExpires": "समय-सीमा समाप्त", - "apiKeyRevoked": "एपीआई कुंजी \"{{name}}\" निरस्त कर दी गई", - "apiKeyRevokeFailed": "एपीआई कुंजी रद्द करने में विफल", - "passwordFieldsRequired": "वर्तमान और नए पासवर्ड आवश्यक हैं", - "passwordMismatch": "सांकेतिक शब्द मेल नहीं खाते", - "passwordTooShort": "पासवर्ड कम से कम 6 अंकों का होना चाहिए", - "passwordUpdated": "पासवर्ड सफलतापूर्वक अद्यतन", - "passwordUpdateFailed": "पासवर्ड अपडेट करने में विफल", - "deletePasswordRequired": "अपना खाता हटाने के लिए पासवर्ड आवश्यक है।", - "deleteFailed": "खाता हटाने में विफल", - "deleting": "हटा रहा हूँ...", - "colorPickerTooltip": "कलर पिकर खोलें", - "themeSystem": "प्रणाली", - "themeLight": "रोशनी", - "themeDark": "अँधेरा", - "themeDracula": "ड्रेकुला", - "themeCatppuccin": "कैटपुचिन", - "themeNord": "नॉर्ड", - "themeSolarized": "सौर ऊर्जा से युक्त", - "themeTokyoNight": "टोक्यो नाइट", - "themeOneDark": "एक अंधेरा", - "themeGruvbox": "ग्रुवबॉक्स" + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/hu_HU.json b/src/ui/locales/translated/hu_HU.json index 370b3a65..9bc5af32 100644 --- a/src/ui/locales/translated/hu_HU.json +++ b/src/ui/locales/translated/hu_HU.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Mappák", - "folder": "Mappa", - "password": "Jelszó", - "key": "Kulcsfontosságú", - "sshPrivateKey": "SSH privát kulcs", - "upload": "Feltöltés", - "keyPassword": "Kulcs jelszó", - "sshKey": "SSH-kulcs", - "uploadPrivateKeyFile": "Privátkulcsfájl feltöltése", - "searchCredentials": "Hitelesítő adatok keresése...", - "addCredential": "Hitelesítő adat hozzáadása", - "caCertificate": "CA tanúsítvány (-cert.pub)", - "caCertificateDescription": "Opcionális: Töltse fel vagy illessze be a CA által aláírt tanúsítványfájlt (pl. id_ed25519-cert.pub). Kötelező, ha az SSH-kiszolgáló tanúsítványalapú hitelesítést használ.", - "uploadCertFile": "Feltöltés -cert.pub fájl", - "clearCert": "Világos", - "certLoaded": "Tanúsítvány betöltve", - "certPublicKeyLabel": "CA tanúsítvány", - "certTypeLabel": "Tanúsítvány típusa", - "pasteOrUploadCert": "Illesszen be vagy töltsön fel egy -cert.pub tanúsítványt...", - "hasCaCert": "CA tanúsítvánnyal rendelkezik", - "noCaCert": "Nincs CA tanúsítvány" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Nem sikerült betölteni az értesítéseket", - "failedToDismissAlert": "Nem sikerült elvetni a riasztást" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Szerver konfiguráció", - "description": "Konfigurálja a Termix szerver URL-címét a háttérszolgáltatásokhoz való csatlakozáshoz", - "serverUrl": "Szerver URL-címe", - "enterServerUrl": "Kérjük, adjon meg egy szerver URL-címét", - "saveFailed": "Nem sikerült menteni a konfigurációt", - "saveError": "Hiba a konfiguráció mentése során", - "saving": "Megtakarítás...", - "saveConfig": "Konfiguráció mentése", - "helpText": "Add meg az URL-címet, ahol a Termix szervered fut (pl. http://localhost:30001 vagy https://your-server.com)", - "changeServer": "Szerverváltás", - "mustIncludeProtocol": "A szerver URL-címének http:// vagy https:// előtaggal kell kezdődnie.", - "allowInvalidCertificate": "Érvénytelen tanúsítvány engedélyezése", - "allowInvalidCertificateDesc": "Csak megbízható, saját üzemeltetésű szerverekhez használja, amelyek önaláírt vagy IP-cím alapú tanúsítvánnyal rendelkeznek.", - "useEmbedded": "Helyi szerver használata", - "embeddedDesc": "Futtassa a Termixet a beépített helyi szerverrel (nincs szükség távoli szerverre)", - "embeddedConnecting": "Kapcsolódás a helyi szerverhez...", - "embeddedNotReady": "A helyi szerver még nem áll készen. Kérjük, várjon egy pillanatot, és próbálja újra.", - "localServer": "Helyi szerver" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Verzióellenőrzési hiba", - "checkFailed": "Nem sikerült ellenőrizni a frissítéseket", - "upToDate": "Az alkalmazás naprakész", - "currentVersion": "A(z) {{version}} verziót futtatja", - "updateAvailable": "Frissítés elérhető", - "newVersionAvailable": "Új verzió érhető el! A(z) {{current}}verziót futtatod, de a(z) {{latest}} verzió már elérhető.", - "betaVersion": "Béta verzió", - "betaVersionDesc": "A(z) {{current}}verziót futtatod, ami újabb, mint a legújabb stabil kiadás {{latest}}.", - "releasedOn": "Megjelent: {{date}}", - "downloadUpdate": "Frissítés letöltése", - "checking": "Frissítések keresése...", - "checkUpdates": "Frissítések keresése", - "checkingUpdates": "Frissítések keresése...", - "updateRequired": "Frissítés szükséges" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Közeli", - "minimize": "Minimalizálás", + "close": "Close", + "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Folytatás", - "maintenance": "Karbantartás", - "degraded": "Leromlott", - "error": "Hiba", - "warning": "Figyelmeztetés", - "unsavedChanges": "Nem mentett változtatások", - "dismiss": "Elvetés", - "loading": "Terhelés...", - "optional": "Választható", - "connect": "Csatlakozás", - "copied": "Másolva", - "connecting": "Kapcsolódás...", - "updateAvailable": "Frissítés elérhető", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Megnyitás új lapon", - "noReleases": "Nincsenek kiadások", - "updatesAndReleases": "Frissítések és kiadások", - "newVersionAvailable": "Új verzió ({{version}}) érhető el.", - "failedToFetchUpdateInfo": "Nem sikerült lekérni a frissítési információkat", - "preRelease": "Kiadás előtti", - "noReleasesFound": "Nem találhatók kiadások.", - "cancel": "Mégsem", - "username": "Felhasználónév", - "login": "Bejelentkezés", - "register": "Nyilvántartás", - "password": "Jelszó", - "confirmPassword": "Jelszó megerősítése", - "back": "Vissza", - "save": "Megtakarítás", - "saving": "Megtakarítás...", - "delete": "Töröl", - "rename": "Átnevezés", - "edit": "Szerkesztés", - "add": "Hozzáadás", - "confirm": "Megerősítés", - "no": "Nem", - "or": "VAGY", - "next": "Következő", - "previous": "Előző", - "refresh": "Frissítés", - "language": "Nyelv", - "checking": "Ellenőrzés...", - "checkingDatabase": "Adatbázis-kapcsolat ellenőrzése...", - "checkingAuthentication": "Hitelesítés ellenőrzése...", - "backendReconnected": "Szerverkapcsolat helyreállt", - "connectionDegraded": "Megszakadt a szerverkapcsolat, helyreáll a kapcsolat…", - "reload": "Újratöltés", - "remove": "Eltávolítás", - "create": "Teremt", - "update": "Frissítés", - "copy": "Másolat", - "copyFailed": "Nem sikerült a vágólapra másolni", - "maximize": "Maximalizálás", - "restore": "Visszaállítás", - "of": "a" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Otthon", - "terminal": "Terminál", - "docker": "Dokkmunkás", - "tunnels": "Alagutak", - "fileManager": "Fájlkezelő", - "serverStats": "Szerver statisztikák", - "admin": "Adminisztrátor", - "userProfile": "Felhasználói profil", - "splitScreen": "Osztott képernyő", - "confirmClose": "Bezárja ezt az aktív munkamenetet?", - "close": "Közeli", - "cancel": "Mégsem", - "sshManager": "SSH-kezelő", - "cannotSplitTab": "Ez a lap nem osztható fel", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Jelszó másolása", - "copySudoPassword": "Sudo jelszó másolása", - "passwordCopied": "Jelszó a vágólapra másolva", - "noPasswordAvailable": "Nincs elérhető jelszó", - "failedToCopyPassword": "Nem sikerült másolni a jelszót", - "refreshTab": "Kapcsolat frissítése", - "openFileManager": "Fájlkezelő megnyitása", - "dashboard": "Műszerfal", - "networkGraph": "Hálózati gráf", - "quickConnect": "Gyors csatlakozás", - "sshTools": "SSH eszközök", - "history": "Történelem", - "hosts": "Házigazdák", - "snippets": "Kódrészletek", - "hostManager": "Házigazda-kezelő", - "credentials": "Hitelesítő adatok", - "connections": "Kapcsolatok", - "roleAdministrator": "Adminisztrátor", - "roleUser": "Felhasználó" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Házigazdák", - "noHosts": "Nincsenek SSH-hosztok", - "retry": "Újrapróbálkozás", - "refresh": "Frissítés", - "optional": "Választható", - "downloadSample": "Minta letöltése", - "failedToDeleteHost": "Nem sikerült törölni a(z) {{name}} fájlt.", - "importSkipExisting": "Importálás (meglévő kihagyása)", - "connectionDetails": "Kapcsolat részletei", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Távoli asztal", - "port": "Kikötő", - "username": "Felhasználónév", - "folder": "Mappa", - "tags": "Címkék", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", "pin": "Pin", - "addHost": "Gazdagép hozzáadása", - "editHost": "Gazdagép szerkesztése", - "cloneHost": "Klónozó gazdagép", - "enableTerminal": "Terminál engedélyezése", - "enableTunnel": "Alagút engedélyezése", - "enableFileManager": "Fájlkezelő engedélyezése", - "enableDocker": "Docker engedélyezése", - "defaultPath": "Alapértelmezett elérési út", - "connection": "Kapcsolat", - "upload": "Feltöltés", - "authentication": "Hitelesítés", - "password": "Jelszó", - "key": "Kulcsfontosságú", - "credential": "Hitelesítő adat", - "none": "Egyik sem", - "sshPrivateKey": "SSH privát kulcs", - "keyType": "Kulcstípus", - "uploadFile": "Fájl feltöltése", - "tabGeneral": "Általános", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", - "tabRdp": "Vidékfejlesztési Program", + "tabTerminal": "Terminal", + "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Alagutak", - "tabDocker": "Dokkmunkás", - "tabFiles": "Fájlok", - "tabStats": "Statisztikák", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Megosztás", - "tabAuthentication": "Hitelesítés", - "terminal": "Terminál", - "tunnel": "Alagút", - "fileManager": "Fájlkezelő", - "serverStats": "Szerver statisztikák", - "status": "Állapot", - "folderRenamed": "A(z) „{{oldName}}” mappa sikeresen átnevezve erre: „{{newName}}”", - "failedToRenameFolder": "Nem sikerült átnevezni a mappát", - "movedToFolder": "Áthelyezve ide: \"{{folder}}\"", - "editHostTooltip": "Gazdagép szerkesztése", - "statusChecks": "Állapotellenőrzések", - "metricsCollection": "Metrikagyűjtemény", - "metricsInterval": "Metrikagyűjtési intervallum", - "metricsIntervalDesc": "Milyen gyakran gyűjtsünk szerverstatisztikákat (5 másodperc - 1 óra)", - "behavior": "Viselkedés", - "themePreview": "Téma előnézete", - "theme": "Téma", - "fontFamily": "Betűcsalád", - "fontSize": "Betűméret", - "letterSpacing": "Betűköz", - "lineHeight": "Vonalmagasság", - "cursorStyle": "Kurzor stílusa", - "cursorBlink": "Kurzor villogása", - "scrollbackBuffer": "Görgetési puffer", - "bellStyle": "Harang stílus", - "rightClickSelectsWord": "Jobb klikk kijelöli a szót", - "fastScrollModifier": "Gyors görgetési módosító", - "fastScrollSensitivity": "Gyors görgetési érzékenység", - "sshAgentForwarding": "SSH ügynöktovábbítás", - "backspaceMode": "Visszatörlés mód", - "startupSnippet": "Indítási kódrészlet", - "selectSnippet": "Kódrészlet kiválasztása", - "forceKeyboardInteractive": "Kényszerítse a billentyűzet interaktivitását", - "overrideCredentialUsername": "Hitelesítő adat felülírása Felhasználónév", - "overrideCredentialUsernameDesc": "Használja a fent megadott felhasználónevet a hitelesítő adat felhasználóneve helyett.", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Port hozzáadása", - "addProxyNode": "Csomópont hozzáadása", - "proxyNode": "Proxy csomópont", - "proxyType": "Proxy típusa", - "quickActions": "Gyors műveletek", - "sudoPasswordAutoFill": "Sudo jelszó automatikus kitöltése", - "sudoPassword": "Sudo jelszó", - "keepaliveInterval": "Életben tartási intervallum (ms)", - "moshCommand": "MOSH parancs", - "environmentVariables": "Környezeti változók", - "addVariable": "Változó hozzáadása", - "docker": "Dokkmunkás", - "copyTerminalUrl": "Terminál URL másolása", - "copyFileManagerUrl": "Fájlkezelő URL-címének másolása", - "copyRemoteDesktopUrl": "Távoli asztal URL-címének másolása", - "failedToConnect": "Nem sikerült csatlakozni a konzolhoz", - "connect": "Csatlakozás", - "disconnect": "Leválasztás", - "start": "Indul", - "enableStatusCheck": "Állapotellenőrzés engedélyezése", - "enableMetrics": "Metrikák engedélyezése", - "bulkUpdateFailed": "Tömeges frissítés sikertelen", - "selectAll": "Összes kijelölése", - "deselectAll": "Összes kijelölés törlése", - "protocols": "Protokollok", - "secureShell": "Biztonságos shell", - "virtualNetwork": "Virtuális hálózat", - "unencryptedShell": "Titkosítatlan shell", - "addressIp": "Cím / IP-cím", - "friendlyName": "Barátságos név", - "folderAndAdvanced": "Mappa és Speciális", - "privateNotes": "Privát jegyzetek", - "privateNotesPlaceholder": "Részletek erről a szerverről...", - "pinToTop": "Rögzítés felülre", - "pinToTopDesc": "Mindig jelenjen meg ez a hoszt a lista tetején", - "portKnockingSequence": "Port kopogás sorrend", - "addKnockBtn": "Knock hozzáadása", - "noPortKnocking": "Nincs portkopogás konfigurálva.", - "knockPort": "Knock kikötő", - "protocol": "Jegyzőkönyv", - "delayAfterMs": "Késleltetés után (ms)", - "useSocks5Proxy": "Használjon SOCKS5 proxyt", - "useSocks5ProxyDesc": "Kapcsolat átirányítása proxy szerveren keresztül", - "proxyHost": "Proxy állomás", - "proxyPort": "Proxy port", - "proxyUsername": "Proxy felhasználónév", - "proxyPassword": "Proxy jelszó", - "proxySingleMode": "Egyetlen proxy", - "proxyChainMode": "Proxy lánc", - "you": "Te", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", "jumpHostChainLabel": "Jump Host Chain", - "addJumpBtn": "Ugrás hozzáadása", - "noJumpHosts": "Nincsenek ugróhosztok konfigurálva.", - "selectAServer": "Válasszon egy szervert...", - "sshPort": "SSH-port", - "authMethod": "Aut. módszer", - "storedCredential": "Tárolt hitelesítő adatok", - "selectACredential": "Válasszon hitelesítő adatot...", - "keyTypeLabel": "Kulcstípus", - "keyTypeAuto": "Automatikus felismerés", - "keyPasteTab": "Paszta", - "keyUploadTab": "Feltöltés", - "keyFileLoaded": "Kulcsfájl betöltve", - "keyUploadClick": "Kattintson a .pem / .key / .ppk fájlok feltöltéséhez", - "clearKey": "Törlés gomb", - "keySaved": "SSH-kulcs mentve", - "keyReplaceNotice": "illesszen be egy új kulcsot alább a helyére", - "keyPassphraseSaved": "Jelszó mentve, gépelje be a módosításhoz", - "replaceKey": "Kulcs cseréje", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", - "forceKeyboardInteractiveShortDesc": "Kézi jelszó megadásának kényszerítése akkor is, ha a kulcsok jelen vannak", - "terminalAppearance": "Terminál megjelenése", - "colorTheme": "Színtéma", - "fontFamilyLabel": "Betűcsalád", - "fontSizeLabel": "Betűméret", - "cursorStyleLabel": "Kurzor stílusa", - "letterSpacingPx": "Betűköz (px)", - "lineHeightLabel": "Vonalmagasság", - "bellStyleLabel": "Harang stílus", - "backspaceModeLabel": "Visszatörlés mód", - "cursorBlinking": "Kurzor villog", - "cursorBlinkingDesc": "Villogó animáció engedélyezése a terminálkurzorhoz", - "rightClickSelectsWordLabel": "Jobb klikk Kijelöli a szót", - "rightClickSelectsWordShortDesc": "Jobb klikk esetén jelölje ki a kurzor alatti szót", - "behaviorAndAdvanced": "Viselkedés és haladó", - "scrollbackBufferLabel": "Görgetési puffer", - "scrollbackMaxLines": "A történetben tárolt sorok maximális száma", - "sshAgentForwardingLabel": "SSH ügynöktovábbítás", - "sshAgentForwardingShortDesc": "Adja át a helyi SSH-kulcsait ennek a gazdagépnek", - "enableAutoMosh": "Auto-Mosh engedélyezése", - "enableAutoMoshDesc": "Ha elérhető, akkor a Mosh-t részesítsd előnyben az SSH-val szemben.", - "enableAutoTmux": "Auto-Tmux engedélyezése", - "enableAutoTmuxDesc": "Automatikus indítás vagy csatlakozás tmux munkamenethez", - "sudoPasswordAutoFillLabel": "Sudo jelszó automatikus kitöltése", - "sudoPasswordAutoFillShortDesc": "Automatikusan adja meg a sudo jelszót, amikor a rendszer kéri", - "sudoPasswordLabel": "Sudo jelszó", - "environmentVariablesLabel": "Környezeti változók", - "addVariableBtn": "Változó hozzáadása", - "noEnvVars": "Nincsenek konfigurált környezeti változók.", - "fastScrollModifierLabel": "Gyors görgetési módosító", - "fastScrollSensitivityLabel": "Gyors görgetési érzékenység", - "moshCommandLabel": "Mosh parancsnokság", - "startupSnippetLabel": "Indítási kódrészlet", - "keepaliveIntervalLabel": "Életben tartási időköz (másodperc)", - "maxKeepaliveMisses": "Max Keepalive kihagyások", - "tunnelSettings": "Alagútbeállítások", - "enableTunneling": "Alagútkezelés engedélyezése", - "enableTunnelingDesc": "SSH alagút funkció engedélyezése ehhez a gazdagéphez", - "serverTunnelsSection": "Szerver alagutak", - "addTunnelBtn": "Alagút hozzáadása", - "noTunnelsConfigured": "Nincsenek konfigurált alagutak.", - "tunnelLabel": "Alagút {{number}}", - "tunnelType": "Alagút típusa", - "tunnelModeLocalDesc": "Továbbítson egy helyi portot a távoli szerveren (vagy egy onnan elérhető gépen) lévő portra.", - "tunnelModeRemoteDesc": "Továbbítsd a távoli szerver egyik portját a saját gépeden lévő helyi portra.", - "tunnelModeDynamicDesc": "Hozz létre egy SOCKS5 proxyt egy helyi porton a dinamikus porttovábbításhoz.", - "sameHost": "Ez a gazdagép (közvetlen alagút)", - "endpointHost": "Végponti gazdagép", - "endpointPort": "Végponti port", - "bindHost": "Kötőállomás", - "sourcePort": "Forrásport", - "maxRetries": "Max. újrapróbálkozások", - "retryIntervalS": "Újrapróbálkozási intervallum (s)", - "autoStartLabel": "Automatikus indítás", - "autoStartDesc": "Automatikus csatlakozás ehhez az alagúthoz, amikor a gazdagép betöltődik", - "tunnelConnecting": "Alagút összeköt...", - "tunnelDisconnected": "Alagút leválasztva", - "failedToConnectTunnel": "Sikertelen csatlakozás", - "failedToDisconnectTunnel": "Nem sikerült lecsatlakozni", - "dockerIntegration": "Docker-integráció", - "enableDockerMonitor": "Docker engedélyezése", - "enableDockerMonitorDesc": "Dockeren keresztül figyelheti és kezelheti a konténereket ezen a gazdagépen", - "enableFileManagerMonitor": "Fájlkezelő engedélyezése", - "enableFileManagerMonitorDesc": "Fájlok böngészése és kezelése ezen a gazdagépen SFTP-n keresztül", - "defaultPathLabel": "Alapértelmezett elérési út", - "fileManagerPathHint": "A könyvtár, amely megnyílik, amikor a fájlkezelő elindul ehhez a gazdagéphez.", - "statusChecksLabel": "Állapotellenőrzések", - "enableStatusChecks": "Állapotellenőrzések engedélyezése", - "enableStatusChecksDesc": "Rendszeresen pingelje ezt a gazdagépet az elérhetőség ellenőrzéséhez", - "useGlobalInterval": "Globális intervallum használata", - "useGlobalIntervalDesc": "Felülbírálás a szerver szintű állapotellenőrzési intervallummal", - "checkIntervalS": "Ellenőrzési intervallum (s)", - "checkIntervalDesc": "Másodpercek az egyes csatlakozási pingek között", - "metricsCollectionLabel": "Metrikagyűjtemény", - "enableMetricsLabel": "Metrikák engedélyezése", - "enableMetricsDesc": "Gyűjtse össze a CPU, RAM, lemez és hálózati használat adatait erről a gazdagépről", - "useGlobalMetrics": "Globális intervallum használata", - "useGlobalMetricsDesc": "Felülbírálás a szerverszintű metrika intervallummal", - "metricsIntervalS": "Mérési intervallum (s)", - "metricsIntervalDesc2": "Metrika pillanatképek között eltelt másodpercek", - "visibleWidgets": "Látható widgetek", - "cpuUsageLabel": "CPU-használat", - "cpuUsageDesc": "CPU százalék, terhelési átlagok, sparkline grafikon", - "memoryLabel": "Memóriahasználat", - "memoryDesc": "RAM-használat, swap, gyorsítótárban tárolt", - "storageLabel": "Lemezhasználat", - "storageDesc": "Lemezhasználat csatolási pontonként", - "networkLabel": "Hálózati interfészek", - "networkDesc": "Interfészek listája és sávszélesség", - "uptimeLabel": "Üzemidő", - "uptimeDesc": "Rendszer üzemideje és rendszerindítási ideje", - "systemInfoLabel": "Rendszerinformáció", - "systemInfoDesc": "Operációs rendszer, kernel, hostname, architektúra", - "recentLoginsLabel": "Legutóbbi bejelentkezések", - "recentLoginsDesc": "Sikeres és sikertelen bejelentkezési események", - "topProcessesLabel": "Legfontosabb folyamatok", - "topProcessesDesc": "PID, CPU%, MEM%, parancs", - "listeningPortsLabel": "Figyelő portok", - "listeningPortsDesc": "Portok megnyitása folyamattal és állapottal", - "firewallLabel": "Tűzfal", - "firewallDesc": "Tűzfal, AppArmor, SELinux állapota", - "quickActionsLabel": "Gyors műveletek", - "quickActionsToolbar": "A gyors műveletek gombokként jelennek meg a Kiszolgáló statisztikái eszköztáron, így egyetlen kattintással végrehajthatók a parancsok.", - "noQuickActions": "Még nincsenek gyors intézkedések.", - "buttonLabel": "Gombcímke", - "selectSnippetPlaceholder": "Válasszon részletet...", - "addActionBtn": "Művelet hozzáadása", - "hostSharedSuccessfully": "A gazdagép megosztása sikeresen megtörtént", - "failedToShareHost": "Nem sikerült megosztani a gazdagépet", - "accessRevoked": "Hozzáférés visszavonva", - "failedToRevokeAccess": "Nem sikerült visszavonni a hozzáférést", - "cancelBtn": "Mégsem", - "savingBtn": "Megtakarítás...", - "addHostBtn": "Gazdagép hozzáadása", - "hostUpdated": "Gazdagép frissítve", - "hostCreated": "Gazdagép létrehozva", - "failedToSave": "Nem sikerült menteni a gazdagépet", - "credentialUpdated": "Hitelesítő adat frissítve", - "credentialCreated": "Hitelesítő adat létrehozva", - "failedToSaveCredential": "Nem sikerült menteni a hitelesítő adatokat", - "backToHosts": "Vissza a házigazdákhoz", - "backToCredentials": "Vissza a hitelesítő adatokhoz", - "pinned": "Rögzítve", - "noHostsFound": "Nem találhatók hostok", - "tryDifferentTerm": "Próbáljon ki egy másik kifejezést", - "addFirstHost": "Add hozzá az első gazdagépedet a kezdéshez", - "noCredentialsFound": "Nem találhatók hitelesítő adatok", - "addCredentialBtn": "Hitelesítő adat hozzáadása", - "updateCredentialBtn": "Hitelesítő adatok frissítése", - "features": "Jellemzők", - "noFolder": "(Nincs mappa)", - "deleteSelected": "Töröl", - "exitSelection": "Kilépés a kijelölésből", - "importSkip": "Importálás (meglévő kihagyása)", - "importOverwrite": "Importálás (felülírás)", - "collapseBtn": "Összeomlás", - "importExportBtn": "Importálás / Exportálás", - "hostStatusesRefreshed": "Gazdagép állapotok frissítve", - "failedToRefreshHosts": "Nem sikerült frissíteni a gazdagépeket", - "movedHostTo": "Áthelyezve a {{host}} elemet ide: \"{{folder}}\"", - "failedToMoveHost": "Nem sikerült áthelyezni a gazdagépet", - "folderRenamedTo": "Mappa átnevezve erre: \"{{name}}\"", - "deletedFolder": "Törölt mappa: \"{{name}}\"", - "failedToDeleteFolder": "Nem sikerült törölni a mappát", - "deleteAllInFolder": "Törli az összes gazdagépet a(z) \"{{name}}\" helyen? Ez nem vonható vissza.", - "deletedHost": "Törölt {{name}}", - "copiedToClipboard": "Vágólapra másolva", - "terminalUrlCopied": "Terminál URL-címe másolva", - "fileManagerUrlCopied": "Fájlkezelő URL-címe másolva", - "tunnelUrlCopied": "Alagút URL-címe másolva", - "dockerUrlCopied": "Docker URL másolva", - "serverStatsUrlCopied": "Szerverstatisztikák URL-címe másolva", - "rdpUrlCopied": "RDP URL másolva", - "vncUrlCopied": "VNC URL másolva", - "telnetUrlCopied": "Telnet URL másolva", - "remoteDesktopUrlCopied": "Távoli asztal URL-címe másolva", - "expandActions": "Műveletek kibontása", - "collapseActions": "Műveletek összecsukása", - "wakeOnLanAction": "Ébresztés LAN-on keresztül", - "wakeOnLanSuccess": "Mágikus csomag elküldve a {{name}} címre", - "wakeOnLanError": "Nem sikerült elküldeni a mágikus csomagot", - "cloneHostAction": "Klónozó gazdagép", - "copyAddress": "Cím másolása", - "copyLink": "Link másolása", - "copyTerminalUrlAction": "Terminál URL másolása", - "copyFileManagerUrlAction": "Fájlkezelő URL-címének másolása", - "copyTunnelUrlAction": "Alagút URL-címének másolása", - "copyDockerUrlAction": "Docker URL másolása", - "copyServerStatsUrlAction": "Szerverstatisztikák URL-címének másolása", - "copyRdpUrlAction": "RDP URL másolása", - "copyVncUrlAction": "VNC URL másolása", - "copyTelnetUrlAction": "Telnet URL másolása", - "copyRemoteDesktopUrlAction": "Távoli asztal URL-címének másolása", - "deleteCredentialConfirm": "Törli a hitelesítő adatokat: \"{{name}}\"?", - "deletedCredential": "Törölt {{name}}", - "deploySSHKeyTitle": "SSH-kulcs telepítése", - "deployingBtn": "Telepítés...", - "deployBtn": "Telepítés", - "failedToDeployKey": "Nem sikerült telepíteni a kulcsot", - "deleteHostsConfirm": "Törli a {{count}} gazdagépet{{plural}}? Ez nem vonható vissza.", - "movedToRoot": "Áthelyezve a rootba", - "failedToMoveHosts": "Nem sikerült áthelyezni a gazdagépeket", - "enableTerminalFeature": "Terminál engedélyezése", - "disableTerminalFeature": "Terminál letiltása", - "enableFilesFeature": "Fájlok engedélyezése", - "disableFilesFeature": "Fájlok letiltása", - "enableTunnelsFeature": "Alagutak engedélyezése", - "disableTunnelsFeature": "Alagutak letiltása", - "enableDockerFeature": "Docker engedélyezése", - "disableDockerFeature": "Docker letiltása", - "addTagsPlaceholder": "Címkék hozzáadása...", - "authDetails": "Hitelesítési adatok", - "credType": "Típus", - "generateKeyPairDesc": "Generáljon új kulcspárt, mind a privát, mind a nyilvános kulcsok automatikusan kitöltődnek.", - "generatingKey": "Generálás...", - "generateLabel": "{{label}} generálása", - "uploadFileBtn": "Fájl feltöltése", - "keyPassphraseOptional": "Kulcs jelszó (opcionális)", - "sshPublicKeyOptional": "SSH nyilvános kulcs (opcionális)", - "publicKeyGenerated": "Nyilvános kulcs generálva", - "failedToGeneratePublicKey": "Nem sikerült a nyilvános kulcsot kinyerni", - "publicKeyCopied": "Nyilvános kulcs lemásolva", - "keyPairGenerated": "{{label}} kulcspár generálva", - "failedToGenerateKeyPair": "Nem sikerült kulcspárt generálni", - "searchHostsPlaceholder": "Keresés hosztok, címek, címkék…", - "searchCredentialsPlaceholder": "Hitelesítő adatok keresése…", - "refreshBtn": "Frissítés", - "addTag": "Címkék hozzáadása...", - "deleteConfirmBtn": "Töröl", - "tunnelRequirementsText": "Az SSH szervernek a /etc/ssh/sshd_config fájlban be kell állítania a GatewayPorts yes, az AllowTcpForwarding yes és a PermitRootLogin yes értékeket.", - "deleteHostConfirm": "Törölni a \"{{name}}\"?", - "enableAtLeastOneProtocol": "Engedélyezzen legalább egy protokollt a fentiekből a hitelesítés és a kapcsolat beállításainak konfigurálásához.", - "keyPassphrase": "Kulcs jelszó", - "connectBtn": "Csatlakozás", - "disconnectBtn": "Leválasztás", - "basicInformation": "Alapvető információk", - "authDetailsSection": "Hitelesítési adatok", - "credTypeLabel": "Típus", - "hostsTab": "Házigazdák", - "credentialsTab": "Hitelesítő adatok", - "selectMultiple": "Több kiválasztása", - "selectHosts": "Válasszon hosztokat", - "connectionLabel": "Kapcsolat", - "authenticationLabel": "Hitelesítés", - "generateKeyPairTitle": "Kulcspár generálása", - "generateKeyPairDescription": "Generáljon új kulcspárt, mind a privát, mind a nyilvános kulcsok automatikusan kitöltődnek.", - "generateFromPrivateKey": "Privát kulcsból generálás", - "refreshBtn2": "Frissítés", - "exitSelectionTitle": "Kilépés a kijelölésből", - "exportAll": "Összes exportálása", - "addHostBtn2": "Gazdagép hozzáadása", - "addCredentialBtn2": "Hitelesítő adat hozzáadása", - "checkingHostStatuses": "Gazdagépek állapotának ellenőrzése...", - "pinnedSection": "Rögzítve", - "hostsExported": "A hosztok exportálása sikeresen megtörtént.", - "exportFailed": "Nem sikerült exportálni a gazdagépeket", - "sampleDownloaded": "Mintafájl letöltve", - "failedToDeleteCredential2": "Nem sikerült törölni a hitelesítő adatokat", - "noFolderOption": "(Nincs mappa)", - "nSelected": "{{count}} kiválasztva", - "featuresMenu": "Jellemzők", - "moveMenu": "Mozog", - "cancelSelection": "Mégsem", - "deployDialogDesc": "Telepítse a {{name}} kulcsot egy gazdagép authorized_keys kulcsaira.", - "targetHostLabel": "Célállomás", - "selectHostOption": "Válasszon egy hosztot...", - "keyDeployedSuccess": "Kulcs sikeresen telepítve", - "failedToDeployKey2": "Nem sikerült telepíteni a kulcsot", - "deletedCount": "Törölt {{count}} gazdagépek", - "failedToDeleteCount": "Nem sikerült törölni a(z) {{count}} gazdagépet", - "duplicatedHost": "Duplikálva: \"{{name}}\"", - "failedToDuplicateHost": "Nem sikerült a gazdagépet duplikálni", - "updatedCount": "Frissített {{count}} gazdagépek", - "friendlyNameLabel": "Barátságos név", - "descriptionLabel": "Leírás", - "loadingHost": "Gazdagép betöltése...", - "loadingHosts": "Gazdagépek betöltése...", - "loadingCredentials": "Hitelesítő adatok betöltése...", - "noHostsYet": "Még nincsenek házigazdák", - "noHostsMatchSearch": "Nincsenek a keresésnek megfelelő házigazdák", - "hostNotFound": "Nem található gazdagép", - "searchHosts": "Gazdagépek keresése...", - "sortHosts": "Gazdagépek rendezése", - "sortDefault": "Alapértelmezett sorrend", - "sortNameAsc": "Név (A → Z)", - "sortNameDesc": "Név (Z → A)", - "sortIpAsc": "IP-cím (növekvő)", - "sortIpDesc": "IP-cím (leírás)", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Elsőként rögzítve", - "filterHosts": "Szűrőállomások", - "filterClearAll": "Szűrők törlése", - "filterStatusGroup": "Állapot", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Rögzítve", - "filterAuthGroup": "Aut. típus", - "filterAuthPassword": "Jelszó", - "filterAuthKey": "SSH-kulcs", - "filterAuthCredential": "Hitelesítő adat", - "filterAuthNone": "Egyik sem", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Jegyzőkönyv", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "Vidékfejlesztési Program", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Jellemzők", - "filterFeatureTerminal": "Terminál", - "filterFeatureFileManager": "Fájlkezelő", - "filterFeatureTunnel": "Alagút", - "filterFeatureDocker": "Dokkmunkás", - "filterTagsGroup": "Címkék", - "shareHost": "Megosztási házigazda", - "shareHostTitle": "Megosztás: {{name}}", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Ennek a gazdagépnek hitelesítő adatot kell használnia a megosztás engedélyezéséhez. Először szerkessze a gazdagépet, és rendeljen hozzá egy hitelesítő adatot." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Kapcsolat", - "authentication": "Hitelesítés", - "connectionSettings": "Kapcsolati beállítások", - "displaySettings": "Kijelzőbeállítások", - "audioSettings": "Hangbeállítások", - "rdpPerformance": "RDP-teljesítmény", - "deviceRedirection": "Eszközátirányítás", - "session": "Ülés", - "gateway": "Átjáró", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Vágólap", - "sessionRecording": "Munkamenet-felvétel", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC-beállítások", - "terminalSettings": "Terminálbeállítások", - "rdpPort": "RDP-port", - "username": "Felhasználónév", - "password": "Jelszó", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", "domain": "Domain", - "securityMode": "Biztonsági mód", - "colorDepth": "Színmélység", - "width": "Szélesség", - "height": "Magasság", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Átméretezési módszer", - "clientName": "Ügyfél neve", - "initialProgram": "Kezdeti program", - "serverLayout": "Szerver elrendezése", - "timezone": "Időzóna", - "gatewayHostname": "Átjáró állomásneve", - "gatewayPort": "Átjáró kikötő", - "gatewayUsername": "Átjáró felhasználóneve", - "gatewayPassword": "Átjáró jelszava", - "gatewayDomain": "Átjáró domain", - "remoteAppProgram": "RemoteApp program", - "workingDirectory": "Munkakönyvtár", - "arguments": "érvek", - "normalizeLineEndings": "Sorvégek normalizálása", - "recordingPath": "Felvételi útvonal", - "recordingName": "Felvétel neve", - "macAddress": "MAC-cím", - "broadcastAddress": "Sugárzási cím", - "udpPort": "UDP port", - "waitTimeS": "Várakozási idő (s)", - "driveName": "Meghajtó neve", - "drivePath": "Útvonal", - "ignoreCertificate": "Tanúsítvány figyelmen kívül hagyása", - "ignoreCertificateDesc": "Kapcsolódás engedélyezése önaláírt tanúsítványokkal rendelkező gazdagépekhez", - "forceLossless": "Veszteségmentes erő", - "forceLosslessDesc": "Veszteségmentes képkódolás kényszerítése (jobb minőség, nagyobb sávszélesség)", - "disableAudio": "Hang letiltása", - "disableAudioDesc": "Némítsa el az összes hangot a távoli munkamenetből", - "enableAudioInput": "Hangbemenet engedélyezése (mikrofon)", - "enableAudioInputDesc": "Helyi mikrofon továbbítása a távoli munkamenetbe", - "wallpaper": "Tapéta", - "wallpaperDesc": "Asztali háttérkép megjelenítése (a letiltás javítja a teljesítményt)", - "theming": "Témázás", - "themingDesc": "Vizuális témák és stílusok engedélyezése", - "fontSmoothing": "Betűsimítás", - "fontSmoothingDesc": "ClearType betűtípus-megjelenítés engedélyezése", - "fullWindowDrag": "Teljes ablak húzása", - "fullWindowDragDesc": "Ablak tartalmának megjelenítése húzás közben", - "desktopComposition": "Asztali kompozíció", - "desktopCompositionDesc": "Aero Glass effektek engedélyezése", - "menuAnimations": "Menüanimációk", - "menuAnimationsDesc": "Menü elhalványulásának és diaanimációk engedélyezése", - "disableBitmapCaching": "Bitképes gyorsítótár letiltása", - "disableBitmapCachingDesc": "Kapcsold ki a bitképes gyorsítótárat (segíthet a hibákon)", - "disableOffscreenCaching": "Képernyőn kívüli gyorsítótár letiltása", - "disableOffscreenCachingDesc": "Kapcsolja ki a képernyőn kívüli gyorsítótárat", - "disableGlyphCaching": "Karakterjel-gyorsítótár letiltása", - "disableGlyphCachingDesc": "Kapcsolja ki a karakterjel-gyorsítótárat", - "enableGfx": "GFX engedélyezése", - "enableGfxDesc": "RemoteFX grafikus folyamatsor használata", - "enablePrinting": "Nyomtatás engedélyezése", - "enablePrintingDesc": "Helyi nyomtatók átirányítása a távoli munkamenetre", - "enableDriveRedirection": "Meghajtó átirányításának engedélyezése", - "enableDriveRedirectionDesc": "Helyi mappa meghajtóként való leképezése a távoli munkamenetben", - "createDrivePath": "Útvonal létrehozása", - "createDrivePathDesc": "Automatikusan létrehozza a mappát, ha az nem létezik", - "disableDownload": "Letöltés letiltása", - "disableDownloadDesc": "Fájlok letöltésének megakadályozása a távoli munkamenetből", - "disableUpload": "Feltöltés letiltása", - "disableUploadDesc": "Fájlok távoli munkamenetbe való feltöltésének megakadályozása", - "enableTouch": "Érintés engedélyezése", - "enableTouchDesc": "Érintéses bevitel továbbításának engedélyezése", - "consoleSession": "Konzol munkamenet", - "consoleSessionDesc": "Csatlakozás a konzolhoz (0. munkamenet) egy új munkamenet helyett", - "sendWolPacket": "WOL csomag küldése", - "sendWolPacketDesc": "Küldjön egy varázslatos csomagot a gazdagép felébresztéséhez a csatlakozás előtt", - "disableCopy": "Másolás letiltása", - "disableCopyDesc": "Szöveg másolásának megakadályozása a távoli munkamenetből", - "disablePaste": "Beillesztés letiltása", - "disablePasteDesc": "Szöveg beillesztésének megakadályozása a távoli munkamenetbe", - "createPathIfMissing": "Útvonal létrehozása, ha hiányzik", - "createPathIfMissingDesc": "Felvételi könyvtár automatikus létrehozása", - "excludeOutput": "Kimenet kizárása", - "excludeOutputDesc": "Ne rögzítse a képernyőkimenetet (csak metaadatok)", - "excludeMouse": "Egér kizárása", - "excludeMouseDesc": "Ne rögzítse az egér mozgását", - "includeKeystrokes": "Billentyűleütések belefoglalása", - "includeKeystrokesDesc": "Nyers billentyűleütések rögzítése a képernyőn megjelenő kimenet mellett", - "vncPort": "VNC port", - "vncPassword": "VNC jelszó", - "vncUsernameOptional": "Felhasználónév (opcionális)", - "vncLeaveBlank": "Hagyja üresen, ha nem szükséges", - "cursorMode": "Kurzor mód", - "swapRedBlue": "Piros/Kék csere", - "swapRedBlueDesc": "Cserélje fel a piros és kék színcsatornákat (javít néhány színproblémát)", - "readOnly": "Csak olvasható", - "readOnlyDesc": "Távoli képernyő megtekintése bemenet küldése nélkül", - "telnetPort": "Telnet port", - "terminalType": "Terminál típusa", - "fontName": "Betűtípus neve", - "fontSize": "Betűméret", - "colorScheme": "Színséma", - "backspaceKey": "Visszatörlés billentyű", - "saveHostFirst": "Először mentsd el a gazdagépet.", - "sharingOptionsAfterSave": "A megosztási beállítások a gazdagép mentése után érhetők el.", - "sharingLoadError": "Nem sikerült betölteni a megosztási adatokat. Ellenőrizd a kapcsolatot, és próbáld újra.", - "shareHostSection": "Megosztási házigazda", - "shareWithUser": "Megosztás a felhasználóval", - "shareWithRole": "Megosztás szerepkörrel", - "selectUser": "Felhasználó kiválasztása", - "selectRole": "Szerepkör kiválasztása", - "selectUserOption": "Válasszon ki egy felhasználót...", - "selectRoleOption": "Válasszon egy szerepet...", - "permissionLevel": "Engedélyszint", - "expiresInHours": "Lejár (órán belül)", - "noExpiryPlaceholder": "Hagyja üresen, ha nincs lejárati idő", - "shareBtn": "Részesedés", - "currentAccess": "Jelenlegi hozzáférés", - "typeHeader": "Típus", - "targetHeader": "Cél", - "permissionHeader": "Engedély", - "grantedByHeader": "Megadta", - "expiresHeader": "Lejár", - "noAccessEntries": "Még nincsenek hozzáférési bejegyzések.", - "expiredLabel": "Lejárt", - "neverLabel": "Soha", - "revokeBtn": "Visszavonás", - "cancelBtn": "Mégsem", - "savingBtn": "Megtakarítás...", - "updateHostBtn": "Frissítse a gazdagépet", - "addHostBtn": "Gazdagép hozzáadása" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Keresés állomások, parancsok vagy beállítások között...", - "quickActions": "Gyors műveletek", - "hostManager": "Házigazda-kezelő", - "hostManagerDesc": "Gazdagépek kezelése, hozzáadása vagy szerkesztése", - "addNewHost": "Új állomás hozzáadása", - "addNewHostDesc": "Új házigazda regisztrálása", - "adminSettings": "Adminisztrátori beállítások", - "adminSettingsDesc": "Rendszerbeállítások és felhasználók konfigurálása", - "userProfile": "Felhasználói profil", - "userProfileDesc": "Fiókod és beállításaid kezelése", - "addCredential": "Hitelesítő adat hozzáadása", - "addCredentialDesc": "SSH-kulcsok vagy jelszavak tárolása", - "recentActivity": "Legutóbbi tevékenységek", - "serversAndHosts": "Szerverek és hosztok", - "noHostsFound": "Nem található a következőnek megfelelő gazdagép: \"{{search}}\"", - "links": "Linkek", - "navigate": "Navigálás", - "select": "Válasszon", - "toggleWith": "Váltás erre:" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "{{index}} ablaktábla - üres", - "noTabAssigned": "Nincs hozzárendelve tabulátor", - "focusedPane": "Aktív ablaktábla" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Nincsenek kapcsolatok", - "noConnectionsDesc": "Nyisson meg egy terminált, fájlkezelőt vagy távoli asztalt a kapcsolatok megtekintéséhez.", - "connectedFor": "Csatlakoztatva ehhez: {{duration}}", - "connected": "Csatlakoztatva", - "disconnected": "Szétkapcsolt", - "closeTab": "Lap bezárása", - "closeConnection": "Szoros kapcsolat", - "forgetTab": "Felejtsd el", - "removeBackground": "Eltávolítás", - "reconnect": "Újracsatlakozás", - "reopenTab": "Újranyitás", - "sectionOpen": "Nyitott", - "sectionBackground": "Háttér", - "backgroundDesc": "A munkamenetek a leválasztás után 30 percig megmaradnak, majd újracsatlakoztathatók.", - "persisted": "A háttérben maradt", - "expiresIn": "Lejár {{duration}} múlva", - "search": "Kapcsolatok keresése...", - "noSearchResults": "Nincsenek a keresésnek megfelelő kapcsolatok" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Kapcsolódás ehhez: {{type}} munkamenet...", - "connectionError": "Csatlakozási hiba", - "connectionFailed": "Kapcsolódás sikertelen", - "failedToConnect": "Nem sikerült lekérni a kapcsolati tokent", - "hostNotFound": "Nem található gazdagép", - "noHostSelected": "Nincs kiválasztva gazdagép", - "reconnect": "Újracsatlakozás", - "retry": "Újrapróbálkozás", - "guacdUnavailable": "A távoli asztali szolgáltatás (guacd) nem érhető el. Kérjük, győződjön meg róla, hogy a guacd fut, elérhető és megfelelően van konfigurálva az adminisztrátori beállításokban.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Képernyőzár)", - "winKey": "Windows billentyű", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Váltás", - "win": "Győzelem", - "stickyActive": "{{key}} (reteszelve - kattintson a kioldáshoz)", - "stickyInactive": "{{key}} (kattintson a rögzítéshez)", - "esc": "Menekülés", - "tab": "Fül", - "home": "Otthon", - "end": "Vége", - "pageUp": "Oldal fel", - "pageDown": "Oldal le", - "arrowUp": "Felfelé nyíl", - "arrowDown": "Lefelé mutató nyíl", - "arrowLeft": "Balra nyíl", - "arrowRight": "Jobbra nyíl", - "fnToggle": "Funkcióbillentyűk", - "reconnect": "Munkamenet újracsatlakoztatása", - "collapse": "Eszköztár összecsukása", - "expand": "Eszköztár kibontása", - "dragHandle": "Húzással áthelyezhető" + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Csatlakozás a gazdagéphez", - "clear": "Világos", - "paste": "Paszta", - "reconnect": "Újracsatlakozás", - "connectionLost": "Kapcsolat megszakadt", - "connected": "Csatlakoztatva", - "clipboardWriteFailed": "Nem sikerült a vágólapra másolni. Győződjön meg arról, hogy az oldal HTTPS vagy localhost protokollon keresztül jelenik meg.", - "clipboardReadFailed": "Nem sikerült beolvasni a vágólapról. Győződjön meg arról, hogy a vágólapra vonatkozó engedélyek meg vannak adva.", - "clipboardHttpWarning": "A beillesztéshez HTTPS szükséges. Használd a Ctrl+Shift+V billentyűkombinációt, vagy HTTPS-en keresztül szolgáltasd ki a Termixet.", - "unknownError": "Ismeretlen hiba történt", - "websocketError": "WebSocket csatlakozási hiba", - "connecting": "Kapcsolódás...", - "noHostSelected": "Nincs kiválasztva gazdagép", - "reconnecting": "Újracsatlakozás... ({{attempt}}/{{max}})", - "reconnected": "Sikeresen újrakapcsolódott", - "tmuxSessionCreated": "tmux munkamenet létrehozva: {{name}}", - "tmuxSessionAttached": "tmux munkamenet csatolva: {{name}}", - "tmuxUnavailable": "A tmux nincs telepítve a távoli gépre, a standard shell-t használja.", - "tmuxSessionPickerTitle": "tmux munkamenetek", - "tmuxSessionPickerDesc": "Meglévő tmux munkamenetek találhatók ezen a gazdagépen. Válasszon ki egyet az újracsatlakozáshoz, vagy hozzon létre egy új munkamenetet.", - "tmuxWindows": "Ablakok", - "tmuxWindowCount": "{{count}} ablak", - "tmuxAttached": "Csatlakoztatott ügyfelek", - "tmuxAttachedCount": "{{count}} csatolva", - "tmuxLastActivity": "Utolsó tevékenység", - "tmuxTimeJustNow": "éppen most", - "tmuxTimeMinutes": "{{count}}perccel ezelőtt", - "tmuxTimeHours": "{{count}}órával ezelőtt", - "tmuxTimeDays": "{{count}}nappal ezelőtt", - "tmuxCreateNew": "Új munkamenet indítása", - "tmuxCopyHint": "Módosítsa a kijelölést, és nyomja meg az Enter billentyűt a vágólapra másoláshoz", - "tmuxDetach": "Leválasztás a tmux munkamenetről", - "tmuxDetached": "Leválasztva a tmux munkamenetről", - "maxReconnectAttemptsReached": "Elérte az újracsatlakozási kísérletek maximális számát", - "closeTab": "Közeli", - "connectionTimeout": "Kapcsolati időtúllépés", - "terminalTitle": "Terminál - {{host}}", - "terminalWithPath": "Terminál - {{host}}:{{path}}", - "runTitle": "Futás {{command}} - {{host}}", - "totpRequired": "Kétfaktoros hitelesítés szükséges", - "totpCodeLabel": "Ellenőrző kód", - "totpVerify": "Ellenőrzés", - "warpgateAuthRequired": "Warpgate hitelesítés szükséges", - "warpgateSecurityKey": "Biztonsági kulcs", - "warpgateAuthUrl": "Hitelesítési URL", - "warpgateOpenBrowser": "Megnyitás böngészőben", - "warpgateContinue": "Befejeztem a hitelesítést", - "opksshAuthRequired": "OPKSSH hitelesítés szükséges", - "opksshAuthDescription": "A folytatáshoz végezze el a hitelesítést a böngészőjében. Ez a munkamenet 24 órán át érvényes marad.", - "opksshOpenBrowser": "Böngésző megnyitása a hitelesítéshez", - "opksshWaitingForAuth": "Várakozás a böngésző hitelesítésére...", - "opksshAuthenticating": "Hitelesítés feldolgozása...", - "opksshTimeout": "A hitelesítés időtúllépést okozott. Kérjük, próbálja újra.", - "opksshAuthFailed": "Sikertelen hitelesítés. Kérjük, ellenőrizze a hitelesítő adatait, és próbálja újra.", - "opksshSignInWith": "Bejelentkezés {{provider}} felhasználónévvel", - "sudoPasswordPopupTitle": "Jelszó beszúrása?", - "websocketAbnormalClose": "A kapcsolat váratlanul megszakadt. Ennek oka lehet fordított proxy vagy SSL konfigurációs probléma. Kérjük, ellenőrizze a szervernaplókat.", - "connectionLogTitle": "Kapcsolati napló", - "connectionLogCopy": "Naplók másolása a vágólapra", - "connectionLogEmpty": "Még nincsenek kapcsolati naplók", - "connectionLogWaiting": "Várakozás a kapcsolati naplókra...", - "connectionLogCopied": "Kapcsolati naplók másolva a vágólapra", - "connectionLogCopyFailed": "Nem sikerült a naplókat a vágólapra másolni", - "connectionRejected": "A szerver elutasította a kapcsolatot. Kérjük, ellenőrizze a hitelesítést és a hálózati konfigurációt.", - "hostKeyRejected": "SSH host kulcs ellenőrzése elutasítva. Kapcsolat megszakítva.", - "sessionTakenOver": "A munkamenet egy másik lapon nyílt meg. Újrakapcsolódás..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nincs kiválasztva gazdagép", - "initializingEditor": "Szerkesztő inicializálása...", - "file": "Fájl", - "folder": "Mappa", - "uploadFile": "Fájl feltöltése", - "downloadFile": "Letöltés", - "extractArchive": "Archívum kibontása", - "extractingArchive": "{{name}} kinyerése ...", - "archiveExtractedSuccessfully": "{{name}} sikeresen kibontva", - "extractFailed": "Kibontás sikertelen", - "compressFile": "Fájl tömörítése", - "compressFiles": "Fájlok tömörítése", - "compressFilesDesc": "Tömörítsen {{count}} elemet archívumba", - "archiveName": "Archívum neve", - "enterArchiveName": "Adja meg az archívum nevét...", - "compressionFormat": "Tömörítési formátum", - "selectedFiles": "Kiválasztott fájlok", - "andMoreFiles": "és {{count}} több...", - "compress": "Borogatás", - "compressingFiles": "{{count}} elem tömörítése {{name}}...", - "filesCompressedSuccessfully": "{{name}} sikeresen létrehozva", - "compressFailed": "Sikertelen tömörítés", - "edit": "Szerkesztés", - "preview": "Előnézet", - "previous": "Előző", - "next": "Következő", - "pageXOfY": "{{total}} oldalból a {{current}} oldal", - "zoomOut": "Kicsinyítés", - "zoomIn": "Nagyítás", - "newFile": "Új fájl", - "newFolder": "Új mappa", - "rename": "Átnevezés", - "uploading": "Feltöltés...", - "uploadingFile": "Feltöltés {{name}}...", - "fileName": "Fájlnév", - "folderName": "Mappa neve", - "fileUploadedSuccessfully": "A(z) \"{{name}}\" fájl feltöltése sikeresen megtörtént.", - "failedToUploadFile": "Nem sikerült feltölteni a fájlt", - "fileDownloadedSuccessfully": "A(z) \"{{name}}\" fájl sikeresen letöltve", - "failedToDownloadFile": "Nem sikerült letölteni a fájlt", - "fileCreatedSuccessfully": "A(z) \"{{name}}\" fájl sikeresen létrehozva", - "folderCreatedSuccessfully": "A(z) \"{{name}}\" mappa sikeresen létrehozva", - "failedToCreateItem": "Nem sikerült létrehozni az elemet", - "operationFailed": "{{operation}} művelet sikertelen a következőhöz: {{name}}: {{error}}", - "failedToResolveSymlink": "Nem sikerült feloldani a szimbolikus linket", - "itemsDeletedSuccessfully": "{{count}} elem sikeresen törölve", - "failedToDeleteItems": "Nem sikerült törölni az elemeket", - "sudoPasswordRequired": "Rendszergazdai jelszó szükséges", - "enterSudoPassword": "Írja be a sudo jelszót a művelet folytatásához", - "sudoPassword": "Sudo jelszó", - "sudoOperationFailed": "Sudo művelet sikertelen", - "sudoAuthFailed": "Sudo hitelesítés sikertelen", - "dragFilesToUpload": "Húzd ide a fájlokat a feltöltéshez", - "emptyFolder": "Ez a mappa üres", - "searchFiles": "Fájlok keresése...", - "upload": "Feltöltés", - "selectHostToStart": "Válasszon ki egy gazdagépet a fájlkezelés megkezdéséhez", - "sshRequiredForFileManager": "A fájlkezelő SSH-t igényel. Ezen a gazdagépen nincs engedélyezve az SSH.", - "failedToConnect": "Nem sikerült csatlakozni az SSH-hoz", - "failedToLoadDirectory": "Nem sikerült betölteni a könyvtárat", - "noSSHConnection": "Nincs elérhető SSH-kapcsolat", - "copy": "Másolat", - "cut": "Vágott", - "paste": "Paszta", - "copyPath": "Útvonal másolása", - "copyPaths": "Útvonalak másolása", - "delete": "Töröl", - "properties": "Tulajdonságok", - "refresh": "Frissítés", - "downloadFiles": "{{count}} fájlok letöltése böngészőbe", - "copyFiles": "{{count}} elem másolása", - "cutFiles": "{{count}} elem kivágása", - "deleteFiles": "{{count}} elem törlése", - "filesCopiedToClipboard": "{{count}} elem másolva a vágólapra", - "filesCutToClipboard": "{{count}} elem a vágólapra vágva", - "pathCopiedToClipboard": "Útvonal a vágólapra másolva", - "pathsCopiedToClipboard": "{{count}} elérési utak másolva a vágólapra", - "failedToCopyPath": "Nem sikerült a vágólapra másolni az elérési utat", - "movedItems": "Áthelyezett {{count}} elem", - "failedToDeleteItem": "Nem sikerült törölni az elemet", - "itemRenamedSuccessfully": "{{type}} átnevezése sikeresen megtörtént", - "failedToRenameItem": "Nem sikerült átnevezni az elemet", - "download": "Letöltés", - "permissions": "Engedélyek", - "size": "Méret", - "modified": "Módosított", - "path": "Útvonal", - "confirmDelete": "Biztosan törölni szeretnéd ezt: {{name}}?", - "permissionDenied": "Engedély megtagadva", - "serverError": "Szerverhiba", - "fileSavedSuccessfully": "Fájl mentése sikeresen megtörtént", - "failedToSaveFile": "Nem sikerült menteni a fájlt", - "confirmDeleteSingleItem": "Biztosan véglegesen törölni szeretnéd a(z) \"{{name}} \" fájlt?", - "confirmDeleteMultipleItems": "Biztosan véglegesen törölni szeretnéd a(z) {{count}} elemet?", - "confirmDeleteMultipleItemsWithFolders": "Biztosan véglegesen törölni szeretnéd a(z) {{count}} elemet? Ez a mappákat és azok tartalmát is tartalmazza.", - "confirmDeleteFolder": "Biztosan véglegesen törölni szeretné a(z) \"{{name}}\" mappát és annak teljes tartalmát?", - "permanentDeleteWarning": "Ez a művelet nem vonható vissza. Az elem(ek) véglegesen törlődnek a szerverről.", - "recent": "Legutóbbi", - "pinned": "Rögzítve", - "folderShortcuts": "Mappa parancsikonok", - "failedToReconnectSSH": "Nem sikerült újracsatlakozni az SSH-munkamenethez", - "openTerminalHere": "Nyissa meg a terminált itt", - "run": "Fut", - "openTerminalInFolder": "Nyissa meg a terminált ebben a mappában", - "openTerminalInFileLocation": "Nyissa meg a terminált a fájl helyén", - "runningFile": "Futás - {{file}}", - "onlyRunExecutableFiles": "Csak futtatható fájlokat futtathat", - "directories": "Könyvtárak", - "removedFromRecentFiles": "Eltávolította a(z) „{{name}}” elemet a legutóbbi fájlok közül", - "removeFailed": "Eltávolítás sikertelen", - "unpinnedSuccessfully": "A(z) \"{{name}}\" rögzítésének feloldása sikeresen megtörtént.", - "unpinFailed": "Feloldás sikertelen", - "removedShortcut": "Eltávolította a(z) \"{{name}} \" parancsikont", - "removeShortcutFailed": "A parancsikon eltávolítása sikertelen", - "clearedAllRecentFiles": "Az összes legutóbbi fájl törölve", - "clearFailed": "Törlés sikertelen", - "removeFromRecentFiles": "Eltávolítás a legutóbbi fájlok közül", - "clearAllRecentFiles": "Az összes legutóbbi fájl törlése", - "unpinFile": "Fájl rögzítésének feloldása", - "removeShortcut": "Parancsikon eltávolítása", - "pinFile": "Fájl rögzítése", - "addToShortcuts": "Hozzáadás a parancsikonokhoz", - "pasteFailed": "Beillesztés sikertelen", - "noUndoableActions": "Nincsenek visszavonható műveletek", - "undoCopySuccess": "Visszavont másolási művelet: Törölt {{count}} másolt fájlok", - "undoCopyFailedDelete": "Visszavonás sikertelen: Nem sikerült törölni a másolt fájlokat", - "undoCopyFailedNoInfo": "Visszavonás sikertelen: Nem találhatók másolt fájlinformációk", - "undoMoveSuccess": "Visszavont áthelyezési művelet: {{count}} fájl visszahelyezése az eredeti helyére", - "undoMoveFailedMove": "Visszavonás sikertelen: Nem sikerült visszahelyezni a fájlokat", - "undoMoveFailedNoInfo": "Visszavonás sikertelen: Nem találhatók áthelyezett fájlinformációk", - "undoDeleteNotSupported": "A törlési művelet nem vonható vissza: A fájlok véglegesen törölve lettek a szerverről.", - "undoTypeNotSupported": "Nem támogatott visszavonási művelettípus", - "undoOperationFailed": "A visszavonási művelet sikertelen", - "unknownError": "Ismeretlen hiba", - "confirm": "Megerősítés", - "find": "Lelet...", - "replace": "Csere", - "downloadInstead": "Letöltés helyett", - "keyboardShortcuts": "Billentyűparancsok", - "searchAndReplace": "Keresés és csere", - "editing": "Szerkesztés", - "search": "Keresés", - "findNext": "Következő keresése", - "findPrevious": "Előző keresése", - "save": "Megtakarítás", - "selectAll": "Összes kijelölése", - "undo": "Visszavonás", - "redo": "Újra", - "moveLineUp": "Sor felfelé mozgatása", - "moveLineDown": "Sor mozgatása lejjebb", - "toggleComment": "Hozzászólás be-/kikapcsolása", - "autoComplete": "Automatikus kiegészítés", - "imageLoadError": "Nem sikerült betölteni a képet", - "startTyping": "Kezdj el gépelni...", - "unknownSize": "Ismeretlen méret", - "fileIsEmpty": "A fájl üres", - "largeFileWarning": "Nagy fájlra vonatkozó figyelmeztetés", - "largeFileWarningDesc": "A fájl mérete {{size}} , ami teljesítményproblémákat okozhat szövegként megnyitva.", - "fileNotFoundAndRemoved": "A(z) „{{name}}” fájl nem található, és eltávolításra került a legutóbbi/rögzített fájlok közül.", - "failedToLoadFile": "Nem sikerült betölteni a fájlt: {{error}}", - "serverErrorOccurred": "Szerverhiba történt. Kérjük, próbálja meg később.", - "autoSaveFailed": "Automatikus mentés sikertelen", - "fileAutoSaved": "Fájl automatikusan mentve", - "moveFileFailed": "Nem sikerült áthelyezni {{name}}", - "moveOperationFailed": "Áthelyezési művelet sikertelen", - "canOnlyCompareFiles": "Csak két fájlt lehet összehasonlítani", - "comparingFiles": "Fájlok összehasonlítása: {{file1}} és {{file2}}", - "dragFailed": "Húzási művelet sikertelen", - "filePinnedSuccessfully": "A(z) „{{name}}” fájl rögzítése sikeresen megtörtént.", - "pinFileFailed": "Nem sikerült rögzíteni a fájlt", - "fileUnpinnedSuccessfully": "A(z) „{{name}}” fájl rögzítése sikeresen feloldva", - "unpinFileFailed": "Nem sikerült feloldani a fájl rögzítését", - "shortcutAddedSuccessfully": "A(z) „{{name}}” mappa parancsikonja sikeresen hozzáadva", - "addShortcutFailed": "Nem sikerült hozzáadni a parancsikont", - "operationCompletedSuccessfully": "{{operation}} {{count}} elem sikeresen", - "operationCompleted": "{{operation}} {{count}} elem", - "downloadFileSuccess": "A {{name}} fájl sikeresen letöltve", - "downloadFileFailed": "Letöltés sikertelen", - "moveTo": "Áthelyezés ide: {{name}}", - "diffCompareWith": "Különbség összehasonlítása {{name}}-val", - "dragOutsideToDownload": "Húzd az ablakon kívülre a letöltéshez ({{count}} fájl)", - "newFolderDefault": "Új mappa", - "newFileDefault": "ÚjFájl.txt", - "successfullyMovedItems": "{{count}} elem sikeresen áthelyezve ide: {{target}}", - "move": "Mozog", - "searchInFile": "Keresés a fájlban (Ctrl+F)", - "showKeyboardShortcuts": "Billentyűparancsok megjelenítése", - "startWritingMarkdown": "Kezdj el írni a Markdown tartalmaidat...", - "loadingFileComparison": "Fájl-összehasonlítás betöltése...", - "reload": "Újratöltés", - "compare": "Összehasonlítás", - "sideBySide": "Egymás mellett", - "inline": "Beágyazott", - "fileComparison": "Fájl-összehasonlítás: {{file1}} vs {{file2}}", - "fileTooLarge": "A fájl túl nagy: {{error}}", - "sshConnectionFailed": "SSH kapcsolat sikertelen. Kérjük, ellenőrizze a kapcsolatot a következővel: {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Nem sikerült betölteni a fájlt: {{error}}", - "connecting": "Kapcsolódás...", - "connectedSuccessfully": "Sikeres csatlakozás", - "totpVerificationFailed": "TOTP-ellenőrzés sikertelen", - "warpgateVerificationFailed": "Warpgate hitelesítés sikertelen", - "authenticationFailed": "Hitelesítés sikertelen", - "verificationCodePrompt": "Ellenőrző kód:", - "changePermissions": "Engedélyek módosítása", - "currentPermissions": "Jelenlegi engedélyek", - "owner": "Tulajdonos", - "group": "Csoport", - "others": "Mások", - "read": "Olvas", - "write": "Írás", - "execute": "Végrehajtás", - "permissionsChangedSuccessfully": "Engedélyek sikeresen módosítva", - "failedToChangePermissions": "Nem sikerült módosítani az engedélyeket", - "name": "Név", - "sortByName": "Név", - "sortByDate": "Módosítás dátuma", - "sortBySize": "Méret", - "ascending": "Növekvő", - "descending": "Csökkenő", - "root": "Gyökér", - "new": "Új", - "sortBy": "Rendezés alapja", - "items": "Tételek", - "selected": "Kiválasztott", - "editor": "Szerkesztő", - "octal": "Oktális", - "storage": "Tárolás", - "disk": "Korong", - "used": "Használt", - "of": "a", - "toggleSidebar": "Oldalsáv be- és kikapcsolása", - "cannotLoadPdf": "Nem tudom betölteni a PDF-et", - "pdfLoadError": "Hiba történt a PDF fájl betöltésekor.", - "loadingPdf": "PDF betöltése...", - "loadingPage": "Oldal betöltése..." + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Másolás a… gazdagépre", - "moveToHost": "Áthelyezés a… gazdagépre", - "copyItemsToHost": "Másolja a {{count}} elemet a… hosztba", - "moveItemsToHost": "Mozgass {{count}} elemet a… tárhelyre", - "noHostsConnected": "Nincsenek más elérhető fájlkezelő gazdagépek.", - "noHostsConnectedHint": "Adjon hozzá egy másik SSH-hosztot, amelynél a Fájlkezelő engedélyezve van a Host Managerben.", - "selectDestinationHost": "Célállomás kiválasztása", - "destinationPath": "Célútvonal", - "recentDestinations": "Legutóbbi úti célok", - "collapseRecentDestinations": "Legutóbbi úti célok összecsukása", - "expandRecentDestinations": "Legutóbbi úti célok kibontása", - "browseFolders": "Célmappák böngészése", - "browseDestination": "Böngészés vagy elérési út megadása", - "confirmCopy": "Másolat", - "confirmMove": "Mozog", - "transferring": "… átvitele", - "compressing": "Tömörítés…", - "extracting": "… kinyerése", - "transferringItems": "{{current}} {{total}} elem… átvitele", - "transferSuccess": "Átvitel befejezve", - "transferError": "Átvitel sikertelen", - "transferPartial": "Az átvitel {{count}} hibával fejeződött be.", - "transferPartialHint": "Nem sikerült átvinni: {{paths}}", - "itemsSummary": "{{count}} elem", - "destMustBeDirectory": "Több elem átviteléhez a célnak egy könyvtárnak kell lennie", - "selectThisFolder": "Jelölje ki ezt a mappát", - "browsePathWillBeCreated": "Ez a mappa még nem létezik. Létrejön, amikor az átvitel megkezdődik.", - "browsePathError": "Nem sikerült megnyitni ezt az elérési utat a célgépen.", - "goUp": "Felmegy", - "copyFolderToHost": "Mappa másolása a… gazdagépre", - "moveFolderToHost": "Mappa áthelyezése a… gazdagépre", - "hostReady": "Kész", - "hostConnecting": "… csatlakoztatása", - "hostDisconnected": "Nincs csatlakoztatva", - "hostAuthRequired": "Hitelesítés szükséges — először nyissa meg a Fájlkezelőt ezen a gépen", - "hostConnectionFailed": "Kapcsolódás sikertelen", - "metricsTitle": "Átszállási idők", - "metricsPrepare": "Célhely előkészítése: {{duration}}", - "metricsCompress": "Tömörítés forráskód szerint: {{duration}}", - "metricsHopSourceRead": "Forrás → szerver: {{throughput}}", - "metricsHopDestSftpWrite": "Szerver → cél (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Szerver → cél (helyi): {{throughput}}", - "metricsTransfer": "Végtől végig: {{throughput}} ({{duration}})", - "metricsExtract": "Kivonat a rendeltetési helyen: {{duration}}", - "metricsSourceDelete": "Eltávolítás a forrásból: {{duration}}", - "metricsTotal": "Összesen: {{duration}}", - "progressCompressing": "Tömörítés a forrásgépen…", - "progressExtracting": "Kibontás a célállomáson…", - "progressTransferring": "Adatátvitel…", - "progressReconnecting": "Újracsatlakozás…", - "parallelSegmentsLabel": "Párhuzamos átszállósávok", - "parallelSegmentsOption": "{{count}} sávok", - "parallelSegmentsHint": "A nagy fájlokat 256 MB-os darabokra osztja a rendszer. Több sáv külön kapcsolatokat használ (például több átvitel indítása) a nagyobb összátviteli sebesség érdekében.", - "progressTotalSpeed": "{{speed}} összesen ({{lanes}} sávok)", - "progressTransferringItems": "Fájlok átvitele ({{current}} / {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} fájlok", - "sourceNotDeletedPartial": "Forrásfájlok megtartása (részleges átvitel)", - "jumpHostLimitation": "Mindkét gépnek elérhetőnek kell lennie a Termix szerverről. A közvetlen gépközi útvonalválasztás nem támogatott.", - "cancel": "Mégsem", - "methodLabel": "Átviteli módszer", - "methodAuto": "Autó", - "methodTar": "Tar archívum", - "methodItemSftp": "Fájlonkénti SFTP", - "methodAutoHint": "A fájlok száma, mérete és tömöríthetősége alapján választja ki a tar vagy a fájlonkénti SFTP protokollt. Az egyes fájlok mindig folyamatos SFTP-t használnak.", - "methodTarHint": "Tömörítés a forráskódban, egy archívum átvitele, kibontás a célkódban. Tar fájlt igényel mindkét Unix gépen.", - "methodItemSftpHint": "Minden fájlt külön-külön átvihet SFTP-n keresztül. Minden gazdagépen működik, beleértve a Windowst is.", - "methodPreviewLoading": "Átviteli módszer kiszámítása…", - "methodPreviewError": "Nem sikerült megtekinteni az átviteli módot. A szerver továbbra is kiválaszt egy módszert az indításkor.", - "methodPreviewWillUseTar": "Felhasználás: Tar archívum", - "methodPreviewWillUseItemSftp": "Használat: Fájlonkénti SFTP", - "methodPreviewScanSummary": "{{fileCount}} fájl, {{totalSize}} összesen (a forrásgépen beolvasva).", - "methodItemSftpLimitation": "Minden fájl ugyanazt az SFTP-folyamot használja, mint egyetlen fájl másolata, egymás után. A folyamat az összes fájlban összevonódik, így a sáv lassan mozog nagy fájlok esetén.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Fájlonkénti SFTP-t választottál.", - "user_tar": "A tar archívumot választottad.", - "tar_unavailable": "A Tar nem érhető el az egyik vagy mindkét hoszton – helyette fájlonkénti SFTP-t fog használni.", - "windows_host": "Egy Windows gazdagép érintett – tar-t nem használunk.", - "auto_multi_large": "Automatikus: több fájl, beleértve egy nagy fájlt ({{largestSize}}) tömöríthető adatokkal — a tar egyetlen átvitelbe csomagolja.", - "auto_single_large_in_archive": "Automatikus: egy nagy fájl ({{largestSize}}) ebben a készletben — fájlonként SFTP.", - "auto_many_incompressible": "Automatikus: többnyire tömöríthetetlen adatok — fájlonkénti SFTP.", - "auto_many_files": "Auto: sok fájl ({{fileCount}}) — a tar csökkenti a fájlonkénti terhelést.", - "auto_default": "Automatikus: fájlonkénti SFTP ehhez a készlethez." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Mégsem", - "progressCancelling": "Törlés…", - "progressStalled": "Elakadt", - "resumedHint": "Újracsatlakozva egy másik ablakban indított aktív átvitelhez.", - "transferCancelled": "Átutalás törölve", - "transferCancelledCopyHint": "Részleges fájlok maradhatnak a célhelyen.", - "transferCancelledMoveHint": "Részleges fájlok maradhatnak a célhelyen. A forrásfájlok nem lettek eltávolítva.", - "cleanupDestFiles": "Takarítási célpont", - "cleanupDestFilesSuccess": "Részleges fájlok eltávolítva a célhelyről", - "cleanupDestFilesPartial": "Néhány részleges fájlt nem sikerült eltávolítani", - "cleanupDestFilesNothing": "Nincs mit takarítani a célállomáson", - "cleanupDestFilesError": "Sikertelen takarítás", - "retryTransfer": "Újrapróbálkozás", - "retryTransferError": "Újrapróbálkozás sikertelen", - "transferFailedRetryHint": "Részleges adatok maradtak a célállomáson. Az újrapróbálkozás a kapcsolat helyreállásakor folytatódik." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Nincsenek SSH alagutak", - "createFirstTunnelMessage": "Még nem hozott létre SSH alagutakat. A kezdéshez konfigurálja az alagútkapcsolatokat a Host Managerben.", - "connected": "Csatlakoztatva", - "disconnected": "Szétkapcsolt", - "connecting": "Kapcsolódás...", - "error": "Hiba", - "canceling": "Lemondás...", - "connect": "Csatlakozás", - "disconnect": "Leválasztás", - "cancel": "Mégsem", - "port": "Kikötő", - "localPort": "Helyi kikötő", - "remotePort": "Távoli port", - "currentHostPort": "Jelenlegi host port", - "endpointPort": "Végponti port", - "bindIp": "Helyi IP-cím", - "endpointSshConfig": "Végpont SSH konfiguráció", - "endpointSshHost": "Végpont SSH-gazdagép", - "endpointSshHostPlaceholder": "Válasszon ki egy konfigurált gazdagépet", - "endpointSshHostRequired": "Válasszon ki egy végponti SSH-hosztot minden kliens alagúthoz.", - "attempt": "{{current}} kísérlet a {{max}}-ből", - "nextRetryIn": "Következő újrapróbálkozás {{seconds}} másodperc múlva", - "clientTunnels": "Ügyfél alagutak", - "clientTunnel": "Ügyfél alagút", - "addClientTunnel": "Ügyfélalagút hozzáadása", - "noClientTunnels": "Nincsenek kliens alagutak konfigurálva ezen az asztalon.", - "tunnelName": "Alagút neve", - "remoteHost": "Távoli gép", - "autoStart": "Automatikus indítás", - "clientAutoStartDesc": "Akkor indul el, amikor ez az asztali kliens megnyílik és csatlakoztatva marad.", - "clientManualStartDesc": "Használd a Start és Stop gombokat ebből a sorból. A Termix nem nyitja meg automatikusan.", - "clientRemoteServerNote": "A távoli továbbításhoz szükség lehet az AllowTcpForwarding és a GatewayPorts paraméterekre a végponti SSH-kiszolgálón. A távoli port bezárul, amikor ez az asztal lecsatlakozik.", - "clientTunnelStarted": "Ügyfélalagút elindult", - "clientTunnelStopped": "Kliens alagút leállt", - "tunnelTestSucceeded": "Alagútteszt sikeres", - "tunnelTestFailed": "Alagútteszt sikertelen", - "localSaved": "Kliens alagutak mentve", - "localSaveError": "Nem sikerült menteni a helyi kliens alagutak.", - "invalidBindIp": "A helyi IP-címnek érvényes IPv4-címnek kell lennie.", - "invalidLocalTargetIp": "A helyi cél IP-címnek érvényes IPv4-címnek kell lennie.", - "invalidLocalPort": "A helyi portnak 1 és 65535 között kell lennie.", - "invalidRemotePort": "A távoli portnak 1 és 65535 között kell lennie.", - "invalidLocalTargetPort": "A helyi célportnak 1 és 65535 között kell lennie.", - "invalidEndpointPort": "A végpont portjának 1 és 65535 között kell lennie.", - "duplicateAutoStartBind": "Csak egy automatikusan induló kliens alagút használhatja a {{bind}} kapcsolót.", - "manualControlError": "Nem sikerült frissíteni az alagút állapotát.", - "active": "Aktív", - "start": "Indul", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", "stop": "Stop", - "test": "Teszt", - "type": "Alagút típusa", - "typeLocal": "Helyi (-L)", - "typeRemote": "Távoli (-R)", - "typeDynamic": "Dinamikus (-D)", - "typeServerLocalDesc": "Aktuális gazdagéptől a végpontig.", - "typeServerRemoteDesc": "Végpont vissza az aktuális gazdagépre.", - "typeClientLocalDesc": "Helyi számítógépről a végpontra.", - "typeClientRemoteDesc": "Végpont vissza a helyi számítógépre.", - "typeClientDynamicDesc": "SOCKS a helyi számítógépen.", - "typeDynamicDesc": "SOCKS5 CONNECT forgalom továbbítása SSH-n keresztül", - "forwardDescriptionServerLocal": "Aktuális hoszt {{sourcePort}} → végpont {{endpointPort}}.", - "forwardDescriptionServerRemote": "Végpont {{endpointPort}} → aktuális gazdagép {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS a jelenlegi gépen {{sourcePort}}.", - "forwardDescriptionClientLocal": "Helyi {{sourcePort}} → távoli {{endpointPort}}.", - "forwardDescriptionClientRemote": "Távoli {{sourcePort}} → helyi {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS a helyi {{sourcePort}} porton.", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → ZOKNIK {{endpoint}}-on keresztül", - "autoNameClientLocal": "Lokális {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → helyi {{localPort}}", - "autoNameClientDynamic": "ZOKNIK {{localPort}} via {{endpoint}}", - "route": "Útvonal:", - "lastStarted": "Utolsó indítás", - "lastTested": "Utolsó tesztelés", - "lastError": "Utolsó hiba", - "maxRetries": "Max. újrapróbálkozások", - "maxRetriesDescription": "Az újrapróbálkozások maximális száma.", - "retryInterval": "Újrapróbálkozási intervallum (másodperc)", - "retryIntervalDescription": "Várakozási idő az újrapróbálkozások között.", - "local": "Helyi", - "remote": "Távoli", - "destination": "Rendeltetési hely", - "host": "Házigazda", - "mode": "Mód", - "noHostSelected": "Nincs kiválasztva gazdagép", - "working": "Dolgozó..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Memória", - "disk": "Korong", - "network": "Hálózat", - "uptime": "Üzemidő", - "processes": "Folyamatok", - "available": "Elérhető", - "free": "Ingyenes", - "connecting": "Kapcsolódás...", - "connectionFailed": "Nem sikerült csatlakozni a szerverhez", - "naCpus": "Nincs CPU", - "cpuCores_one": "{{count}} Mag", - "cpuCores_other": "{{count}} Magok", - "cpuUsage": "CPU-használat", - "memoryUsage": "Memóriahasználat", - "diskUsage": "Lemezhasználat", - "failedToFetchHostConfig": "Nem sikerült lekérni a gazdagép konfigurációját", - "serverOffline": "Szerver offline állapotban", - "cannotFetchMetrics": "Nem lehet metrikákat lekérni az offline szerverről", - "totpFailed": "TOTP-ellenőrzés sikertelen", - "noneAuthNotSupported": "A kiszolgáló statisztikái nem támogatják a „nincs” hitelesítési típust.", - "load": "Terhelés", - "systemInfo": "Rendszerinformációk", - "hostname": "Gazdagépnév", - "operatingSystem": "Operációs rendszer", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "másodperc", - "networkInterfaces": "Hálózati interfészek", - "noInterfacesFound": "Nem találhatók hálózati interfészek", - "noProcessesFound": "Nem találhatók folyamatok", - "loginStats": "SSH bejelentkezési statisztikák", - "noRecentLoginData": "Nincsenek friss bejelentkezési adatok", - "executingQuickAction": "{{name}} végrehajtása ...", - "quickActionSuccess": "{{name}} sikeresen befejeződött", - "quickActionFailed": "{{name}} sikertelen", - "quickActionError": "Nem sikerült végrehajtani a {{name}} parancsot.", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Figyelő portok", - "protocol": "Jegyzőkönyv", - "port": "Kikötő", - "address": "Cím", - "process": "Folyamat", - "noData": "Nincsenek figyelőport-adatok" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Tűzfal", - "inactive": "Inaktív", - "policy": "Politika", - "rules": "szabályok", - "noData": "Nincsenek elérhető tűzfaladatok", - "action": "Akció", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Kikötő", - "source": "Forrás", - "anywhere": "Bárhol" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Átlagos betöltés", - "swap": "Csere", - "architecture": "Építészet", - "refresh": "Frissítés", - "retry": "Újrapróbálkozás" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Saját üzemeltetésű SSH és távoli asztalkezelés", - "loginTitle": "Bejelentkezés a Termixbe", - "registerTitle": "Fiók létrehozása", - "forgotPassword": "Elfelejtett jelszó?", - "rememberMe": "Eszköz megjegyzése 30 napig (TOTP-t is tartalmaz)", - "noAccount": "Nincs fiókod?", - "hasAccount": "Már van fiókod?", - "twoFactorAuth": "Kétfaktoros hitelesítés", - "enterCode": "Írja be az ellenőrző kódot", - "backupCode": "Vagy használjon biztonsági kódot", - "verifyCode": "Kód ellenőrzése", - "redirectingToApp": "Átirányítás az alkalmazáshoz...", - "sshAuthenticationRequired": "SSH-hitelesítés szükséges", - "sshNoKeyboardInteractive": "Billentyűzet-interaktív hitelesítés nem érhető el", - "sshAuthenticationFailed": "Hitelesítés sikertelen", - "sshAuthenticationTimeout": "Hitelesítési időtúllépés", - "sshNoKeyboardInteractiveDescription": "A szerver nem támogatja a billentyűzet-interaktív hitelesítést. Kérjük, adja meg jelszavát vagy SSH-kulcsát.", - "sshAuthFailedDescription": "A megadott hitelesítő adatok helytelenek voltak. Kérjük, próbálkozzon újra érvényes hitelesítő adatokkal.", - "sshTimeoutDescription": "A hitelesítési kísérlet időtúllépést okozott. Kérjük, próbálja újra.", - "sshProvideCredentialsDescription": "Kérjük, adja meg SSH hitelesítő adatait a szerverhez való csatlakozáshoz.", - "sshPasswordDescription": "Add meg az SSH-kapcsolat jelszavát.", - "sshKeyPasswordDescription": "Ha az SSH-kulcsa titkosított, adja meg itt a jelszót.", - "passphraseRequired": "Jelszó szükséges", - "passphraseRequiredDescription": "Az SSH-kulcs titkosított. Kérjük, adja meg a jelszót a feloldáshoz.", - "back": "Vissza", - "firstUser": "Első felhasználó", - "firstUserMessage": "Te vagy az első felhasználó, és rendszergazda leszel. Az adminisztrátori beállításokat az oldalsáv felhasználói legördülő menüjében tekintheted meg. Ha úgy gondolod, hogy ez egy hiba, ellenőrizd a Docker naplókat, vagy hozz létre egy GitHub-problémát.", - "external": "Külső", - "loginWithExternal": "Bejelentkezés külső szolgáltatóval", - "loginWithExternalDesc": "Bejelentkezés a konfigurált külső identitásszolgáltató használatával", - "externalNotSupportedInElectron": "Az Electron alkalmazás egyelőre nem támogatja a külső hitelesítést. Kérjük, használja a webes verziót az OIDC bejelentkezéshez.", - "resetPasswordButton": "Jelszó visszaállítása", - "sendResetCode": "Visszaállítási kód küldése", - "resetCodeDesc": "Add meg a felhasználóneved, hogy jelszó-visszaállító kódot kapj. A kód a Docker konténer naplóiban lesz rögzítve.", - "resetCode": "Kód visszaállítása", - "verifyCodeButton": "Kód ellenőrzése", - "enterResetCode": "Írja be a felhasználó 6 számjegyű kódját a Docker konténer naplóiból:", - "newPassword": "Új jelszó", - "confirmNewPassword": "Jelszó megerősítése", - "enterNewPassword": "Írja be az új jelszavát a következő felhasználóhoz:", - "signUp": "Regisztráció", - "desktopApp": "Asztali alkalmazás", - "loggingInToDesktopApp": "Bejelentkezés az asztali alkalmazásba", - "loadingServer": "Szerver betöltése...", - "dataLossWarning": "A jelszó ilyen módon történő visszaállítása törli az összes mentett SSH-hosztot, hitelesítő adatot és egyéb titkosított adatot. Ez a művelet nem vonható vissza. Csak akkor használja ezt a funkciót, ha elfelejtette jelszavát, és nincs bejelentkezve.", - "authenticationDisabled": "Hitelesítés letiltva", - "authenticationDisabledDesc": "Jelenleg minden hitelesítési módszer le van tiltva. Kérjük, vegye fel a kapcsolatot a rendszergazdával.", - "attemptsRemaining": "{{count}} próbálkozás van hátra" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH host kulcs ellenőrzése", - "keyChangedWarning": "SSH állomáskulcs megváltozott", - "firstConnectionTitle": "Első csatlakozás ehhez a gazdagéphez", - "firstConnectionDescription": "A gazdagép hitelessége nem állapítható meg. Ellenőrizze, hogy az ujjlenyomat megfelel-e a vártnak.", - "keyChangedDescription": "A szerver host key-je megváltozott az utolsó csatlakozás óta. Ez biztonsági problémára utalhat.", - "previousKey": "Előző kulcs", - "newFingerprint": "Új ujjlenyomat", - "fingerprint": "Ujjlenyomat", - "verifyInstructions": "Ha megbízik ebben a gazdagépben, kattintson az Elfogadás gombra a folytatáshoz és az ujjlenyomat mentéséhez a jövőbeli kapcsolatokhoz.", - "securityWarning": "Biztonsági figyelmeztetés", - "acceptAndContinue": "Elfogadom és folytatom", - "acceptNewKey": "Új kulcs elfogadása és folytatás" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Nem sikerült csatlakozni az adatbázishoz", - "unknownError": "Ismeretlen hiba", - "loginFailed": "Bejelentkezés sikertelen", - "failedPasswordReset": "Nem sikerült elindítani a jelszó-visszaállítást", - "failedVerifyCode": "Nem sikerült ellenőrizni a visszaállítási kódot", - "failedCompleteReset": "Nem sikerült befejezni a jelszó visszaállítását", - "invalidTotpCode": "Érvénytelen TOTP-kód", - "failedOidcLogin": "Nem sikerült elindítani az OIDC bejelentkezést", - "silentSigninOidcUnavailable": "Csendes bejelentkezést kértek, de az OIDC bejelentkezés nem érhető el.", - "failedUserInfo": "Bejelentkezés után nem sikerült lekérni a felhasználói adatokat", - "oidcAuthFailed": "OIDC hitelesítés sikertelen", - "invalidAuthUrl": "Érvénytelen engedélyezési URL érkezett a háttérrendszertől", - "requiredField": "Ez a mező kötelező", - "minLength": "A minimális hossz {{min}}", - "passwordMismatch": "A jelszavak nem egyeznek", - "passwordLoginDisabled": "A felhasználónév/jelszó bejelentkezés jelenleg le van tiltva.", - "sessionExpired": "Lejárt a munkamenet - kérjük, jelentkezzen be újra", - "totpRateLimited": "Korlátozott sebesség: Túl sok TOTP ellenőrzési kísérlet. Kérjük, próbálja újra később.", - "totpRateLimitedWithTime": "Korlátozott sebesség: Túl sok TOTP ellenőrzési kísérlet. Kérjük, várjon {{time}} másodpercet, mielőtt újra próbálkozna.", - "resetCodeRateLimited": "Korlátozott sebesség: Túl sok ellenőrzési kísérlet. Kérjük, próbálja újra később.", - "resetCodeRateLimitedWithTime": "Korlátozott sebesség: Túl sok ellenőrzési kísérlet. Kérjük, várjon {{time}} másodpercet, mielőtt újra próbálkozik.", - "authTokenSaveFailed": "Nem sikerült menteni a hitelesítési tokent", - "failedToLoadServer": "Nem sikerült betölteni a szervert" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Az új fiók regisztrációját jelenleg egy adminisztrátor letiltotta. Kérjük, jelentkezzen be, vagy vegye fel a kapcsolatot egy adminisztrátorral.", - "userNotAllowed": "A fiókod nem jogosult a regisztrációra. Kérjük, vedd fel a kapcsolatot egy rendszergazdával.", - "databaseConnectionFailed": "Nem sikerült csatlakozni az adatbázis-kiszolgálóhoz", - "resetCodeSent": "Docker naplókba küldött kód visszaállítása", - "codeVerified": "Kód sikeresen ellenőrizve", - "passwordResetSuccess": "Jelszó visszaállítása sikeresen megtörtént", - "loginSuccess": "Bejelentkezés sikeres", - "registrationSuccess": "Regisztráció sikeres" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Konfigurált SSH-hosztokat célzó helyi asztali alagutak.", - "c2sTunnelPresets": "Kliens alagút előbeállításai", - "c2sTunnelPresetsDesc": "Mentse el az asztali kliens helyi alagútlistáját elnevezett kiszolgálói előbeállításként, vagy töltsön be egy előbeállítást vissza ebbe a kliensbe.", - "c2sTunnelPresetsUnavailable": "Az ügyfélalagút-előbeállítások csak az asztali kliensben érhetők el.", - "c2sPresetName": "Előbeállítás neve", - "c2sPresetNamePlaceholder": "Ügyfél előre beállított neve", - "c2sPresetToLoad": "Betöltendő előbeállítás", - "c2sNoPresetSelected": "Nincs kiválasztva előre beállított érték", - "c2sNoPresets": "Nincsenek mentett előbeállítások", - "c2sLoadPreset": "Terhelés", - "c2sCurrentLocalConfig": "{{count}} helyi kliens alagút(ok) konfigurálva ezen az asztalon.", - "c2sPresetSyncNote": "Az előbeállítások explicit pillanatképek; egyik betöltése felülírja az asztali kliens helyi kliens alagútlistáját.", - "c2sPresetSaved": "Kliens alagút előre beállított értéke mentve", - "c2sPresetLoaded": "A kliens alagút presetje helyben betöltött", - "c2sPresetRenamed": "Ügyfél alagút preset átnevezve", - "c2sPresetDeleted": "Kliens alagút preset törölve", - "c2sPresetLoadError": "Nem sikerült betölteni a kliens alagút előbeállításait" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Nyelv", - "keyPassword": "kulcs jelszó", - "pastePrivateKey": "Illeszd be ide a privát kulcsodat...", - "localListenerHost": "127.0.0.1 (helyben hallgatható)", - "localTargetHost": "127.0.0.1 (cél ezen a számítógépen)", - "socksListenerHost": "127.0.0.1 (SOCKS figyelő)", - "enterPassword": "Írja be a jelszavát", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Műszerfal", - "loading": "Irányítópult betöltése...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Támogatás", - "discord": "Viszály", - "serverOverview": "Szerver áttekintése", - "version": "Változat", - "upToDate": "Naprakész", - "updateAvailable": "Frissítés elérhető", - "beta": "Béta", - "uptime": "Üzemidő", - "database": "Adatbázis", - "healthy": "Egészséges", - "error": "Hiba", - "totalHosts": "Összes házigazda", - "totalTunnels": "Összes alagutak", - "totalCredentials": "Összes hitelesítő adat", - "recentActivity": "Legutóbbi tevékenységek", - "reset": "Visszaállítás", - "loadingRecentActivity": "Legutóbbi tevékenységek betöltése...", - "noRecentActivity": "Nincs friss tevékenység", - "quickActions": "Gyors műveletek", - "addHost": "Gazdagép hozzáadása", - "addCredential": "Hitelesítő adat hozzáadása", - "adminSettings": "Adminisztrátori beállítások", - "userProfile": "Felhasználói profil", - "serverStats": "Szerver statisztikák", - "loadingServerStats": "Szerverstatisztikák betöltése...", - "noServerData": "Nincsenek elérhető szerveradatok", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Irányítópult testreszabása", - "dashboardSettings": "Irányítópult beállításai", - "enableDisableCards": "Kártyák engedélyezése/letiltása", - "resetLayout": "Alapértelmezett visszaállítás", - "serverOverviewCard": "Szerver áttekintése", - "recentActivityCard": "Legutóbbi tevékenységek", - "networkGraphCard": "Hálózati gráf", - "networkGraph": "Hálózati gráf", - "quickActionsCard": "Gyors műveletek", - "serverStatsCard": "Szerver statisztikák", - "panelMain": "Fő", - "panelSide": "Oldal", - "justNow": "éppen most" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABIL", - "hostsOnline": "Online házigazdák", - "activeTunnels": "Aktív alagutak", - "registerNewServer": "Új szerver regisztrálása", - "storeSshKeysOrPasswords": "SSH-kulcsok vagy jelszavak tárolása", - "manageUsersAndRoles": "Felhasználók és szerepkörök kezelése", - "manageYourAccount": "Fiók kezelése", - "hostStatus": "Gazdagép állapota", - "noHostsConfigured": "Nincsenek konfigurált gazdagépek", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", - "nodes": "{{count}} csomópontok", - "add": "Hozzáadás:", - "commandPalette": "Parancspaletta", - "done": "Kész", - "editModeInstructions": "Kártyák húzásával átrendezheted a sorrendet · Az oszlopelválasztó húzásával átméretezheted az oszlopokat · A kártya alsó szélének húzásával átméretezheted a magasságát · A kukába helyezéssel eltávolíthatod", - "empty": "Üres", - "clear": "Világos" + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Gazdagép hozzáadása", - "addGroup": "Csoport hozzáadása", - "addLink": "Link hozzáadása", - "zoomIn": "Nagyítás", - "zoomOut": "Kicsinyítés", - "resetView": "Nézet visszaállítása", - "selectHost": "Válasszon gazdagépet", - "chooseHost": "Válassz egy tárhelyszolgáltatót...", - "parentGroup": "Szülőcsoport", - "noGroup": "Nincs csoport", - "groupName": "Csoport neve", - "color": "Szín", - "source": "Forrás", - "target": "Cél", - "moveToGroup": "Áthelyezés csoportba", - "selectGroup": "Csoport kiválasztása...", - "addConnection": "Kapcsolat hozzáadása", - "hostDetails": "Gazdagép adatai", - "removeFromGroup": "Eltávolítás a csoportból", - "addHostHere": "Gazdagép hozzáadása ide", - "editGroup": "Csoport szerkesztése", - "delete": "Töröl", - "add": "Hozzáadás", - "create": "Teremt", - "move": "Mozog", - "connect": "Csatlakozás", - "createGroup": "Csoport létrehozása", - "selectSourcePlaceholder": "Forrás kiválasztása...", - "selectTargetPlaceholder": "Cél kiválasztása...", - "invalidFile": "Érvénytelen fájl", - "hostAlreadyExists": "A gazdagép már szerepel a topológiában", - "connectionExists": "A kapcsolat már létezik", - "unknown": "Ismeretlen", - "name": "Név", - "ip": "IP-cím", - "status": "Állapot", - "failedToAddNode": "Nem sikerült hozzáadni a csomópontot", - "sourceDifferentFromTarget": "A forrásnak és a célnak eltérőnek kell lennie", - "exportJSON": "JSON exportálása", - "importJSON": "JSON importálása", - "terminal": "Terminál", - "fileManager": "Fájlkezelő", - "tunnel": "Alagút", - "docker": "Dokkmunkás", - "serverStats": "Szerver statisztikák", - "noNodes": "Még nincsenek csomópontok" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "A Docker nincs engedélyezve ehhez a gazdagéphez", - "validating": "Docker érvényesítése...", - "connecting": "Kapcsolódás...", - "error": "Hiba", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Nem sikerült csatlakozni a Dockerhez", - "containerStarted": "{{name}} konténer elindítva", - "failedToStartContainer": "Nem sikerült elindítani a(z) {{name}} konténert", - "containerStopped": "Konténer {{name}} leállítva", - "failedToStopContainer": "Nem sikerült leállítani a(z) {{name}} konténert", - "containerRestarted": "{{name}} konténer újraindítva", - "failedToRestartContainer": "Nem sikerült újraindítani a konténert {{name}}", - "containerPaused": "{{name}} konténer szünetel", - "containerUnpaused": "{{name}} konténer szüneteltetése feloldva", - "failedToTogglePauseContainer": "Nem sikerült a szüneteltetési állapotot be- és kikapcsolni a következő konténernél: {{name}}", - "containerRemoved": "{{name}} konténer eltávolítva", - "failedToRemoveContainer": "Nem sikerült eltávolítani a(z) {{name}} konténert", - "image": "Kép", - "ports": "kikötők", - "noPorts": "Nincsenek portok", - "start": "Indul", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", - "runningContainerWarning": "Figyelmeztetés: Ez a konténer jelenleg fut. Eltávolítása először a konténer leállítását jelenti.", - "loadingContainers": "Konténerek betöltése...", - "manager": "Docker-kezelő", - "autoRefresh": "Automatikus frissítés", - "timestamps": "Időbélyegek", - "lines": "Vonalak", - "filterLogs": "Naplók szűrése...", - "refresh": "Frissítés", - "download": "Letöltés", - "clear": "Világos", - "logsDownloaded": "Naplók sikeresen letöltve", - "last50": "Utolsó 50", - "last100": "Utolsó 100", - "last500": "Utolsó 500", - "last1000": "Utolsó 1000", - "allLogs": "Minden napló", - "noLogsMatching": "Nincsenek a következőnek megfelelő naplók: \"{{query}}\"", - "noLogsAvailable": "Nincsenek elérhető naplók", - "noContainersFound": "Nem találhatók konténerek", - "noContainersFoundHint": "Nincsenek elérhető Docker konténerek ezen a gazdagépen", - "searchPlaceholder": "Konténerek keresése...", - "allStatuses": "Minden állapot", - "stateRunning": "Futás", - "statePaused": "Szüneteltetett", - "stateExited": "Kilépett", - "stateRestarting": "Újraindítás", - "noContainersMatchFilters": "Egyetlen tároló sem felel meg a szűrőknek", - "noContainersMatchFiltersHint": "Próbálja meg módosítani a keresési vagy szűrési feltételeket", - "failedToFetchStats": "Nem sikerült lekérni a konténer statisztikáit", - "containerNotRunning": "A konténer nem működik", - "startContainerToViewStats": "Indítsa el a konténert a statisztikák megtekintéséhez", - "loadingStats": "Statisztikák betöltése...", - "errorLoadingStats": "Hiba a statisztikák betöltése során", - "noStatsAvailable": "Nincsenek elérhető statisztikák", - "cpuUsage": "CPU-használat", - "current": "Jelenlegi", - "memoryUsage": "Memóriahasználat", - "networkIo": "Hálózati I/O", - "input": "Bemenet", - "output": "Kimenet", - "blockIo": "Blokk I/O", - "read": "Olvas", - "write": "Írás", - "pids": "PID-ek", - "containerInformation": "Konténerinformációk", - "name": "Név", - "id": "azonosító", - "state": "Állami", - "containerMustBeRunning": "A konténernek futnia kell a konzol eléréséhez", - "verificationCodePrompt": "Írja be az ellenőrző kódot", - "totpVerificationFailed": "A TOTP ellenőrzése sikertelen. Próbáld újra.", - "warpgateVerificationFailed": "A Warpgate hitelesítése sikertelen. Próbáld újra.", - "connectedTo": "Csatlakoztatva ehhez: {{containerName}}", - "disconnected": "Szétkapcsolt", - "consoleError": "Konzolhiba", - "errorMessage": "Hiba: {{message}}", - "failedToConnect": "Nem sikerült csatlakozni a konténerhez", - "console": "Konzol", - "selectShell": "Válassza ki a héjat", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "hamu", - "connect": "Csatlakozás", - "disconnect": "Leválasztás", - "notConnected": "Nincs csatlakoztatva", - "clickToConnect": "Kattintson a Csatlakozás gombra a shell munkamenet elindításához", - "connectingTo": "Kapcsolódás ehhez: {{containerName}}...", - "containerNotFound": "A konténer nem található", - "backToList": "Vissza a listához", - "logs": "Naplók", - "stats": "Statisztikák", - "consoleTab": "Konzol", - "startContainerToAccess": "Indítsa el a konténert a konzol eléréséhez" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Általános", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Felhasználók", - "sectionSessions": "Munkamenetek", - "sectionRoles": "Szerepkörök", - "sectionDatabase": "Adatbázis", - "sectionApiKeys": "API-kulcsok", - "allowRegistration": "Felhasználói regisztráció engedélyezése", - "allowRegistrationDesc": "Új felhasználók önregisztrációjának engedélyezése", - "allowPasswordLogin": "Jelszó bejelentkezés engedélyezése", - "allowPasswordLoginDesc": "Felhasználónév/jelszó bejelentkezés", - "oidcAutoProvision": "OIDC automatikus kiépítés", - "oidcAutoProvisionDesc": "Automatikus fiókok létrehozása OIDC-felhasználók számára, még akkor is, ha a regisztráció le van tiltva", - "allowPasswordReset": "Jelszó-visszaállítás engedélyezése", - "allowPasswordResetDesc": "Kód visszaállítása Docker naplókon keresztül", - "sessionTimeout": "Munkamenet időkorlátja", - "hours": "órák", - "sessionTimeoutRange": "Minimum 1 óra · Max. 720 óra", - "monitoringDefaults": "Monitorozási alapértelmezések", - "statusCheck": "Állapotellenőrzés", - "metrics": "Metrikák", - "sec": "másodperc", - "logLevel": "Naplózási szint", - "enableGuacamole": "Guacamole engedélyezése", - "enableGuacamoleDesc": "RDP/VNC távoli asztal", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "OpenID Connect konfigurálása SSO-hoz. A csillaggal (*) jelölt mezők kitöltése kötelező.", - "oidcClientId": "Ügyfél-azonosító", - "oidcClientSecret": "Ügyféltitkos kód", - "oidcAuthUrl": "Engedélyezési URL", - "oidcIssuerUrl": "Kibocsátó URL-címe", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "Token URL", - "oidcUserIdentifier": "Felhasználói azonosító útvonala", - "oidcDisplayName": "Megjelenített név útvonala", - "oidcScopes": "Hatókör", - "oidcUserinfoUrl": "Felhasználói adatok URL-címének felülbírálása", - "oidcAllowedUsers": "Engedélyezett felhasználók", - "oidcAllowedUsersDesc": "Egy e-mail cím soronként. Hagyja üresen, ha az összeset engedélyezi.", - "removeOidc": "Eltávolítás", - "usersCount": "{{count}} felhasználó", - "createUser": "Teremt", - "newRole": "Új szerepkör", - "roleName": "Név", - "roleDisplayName": "Megjelenített név", - "roleDescription": "Leírás", - "rolesCount": "{{count}} szerepkörök", - "createRole": "Teremt", - "creating": "Létrehozás...", - "exportDatabase": "Adatbázis exportálása", - "exportDatabaseDesc": "Töltsön le egy biztonsági másolatot az összes gazdagépről, hitelesítő adatokról és beállításokról", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", "export": "Export", - "exporting": "Exportálás...", - "importDatabase": "Adatbázis importálása", - "importDatabaseDesc": "Visszaállítás .sqlite biztonsági mentésfájlból", - "importDatabaseSelected": "Kiválasztva: {{name}}", - "selectFile": "Fájl kiválasztása", - "changeFile": "Változás", - "import": "Importálás", - "importing": "Importálás...", - "apiKeysCount": "{{count}} billentyűk", - "newApiKey": "Új API-kulcs", - "apiKeyCreatedWarning": "Kulcs létrehozva - másolja le most, nem fog újra megjelenni.", - "apiKeyName": "Név", - "apiKeyUser": "Felhasználó", - "apiKeySelectUser": "Válasszon ki egy felhasználót...", - "apiKeyExpiresAt": "Lejár ekkor:", - "createKey": "Kulcs létrehozása", - "apiKeyNoExpiry": "Nincs lejárati idő", - "revokedBadge": "VISSZAVONVA", - "authTypeDual": "Kettős hitelesítés", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Helyi", - "adminStatusAdministrator": "Adminisztrátor", - "adminStatusRegularUser": "Rendszeres felhasználó", - "adminBadge": "ADMINISZTRÁCIÓ", - "systemBadge": "RENDSZER", - "customBadge": "SZOKÁS", - "youBadge": "TE", - "sessionsActive": "{{count}} aktív", - "sessionActive": "Aktív: {{time}}", - "sessionExpires": "Lejárati idő: {{time}}", - "revokeAll": "Minden", - "revokeAllSessionsSuccess": "A felhasználó összes munkamenete visszavonva", - "revokeAllSessionsFailed": "Nem sikerült visszavonni a munkameneteket", - "revokeSessionFailed": "Nem sikerült visszavonni a munkamenetet", - "addRole": "Szerepkör hozzáadása", - "noCustomRoles": "Nincsenek definiált egyéni szerepkörök", - "removeRoleFailed": "Nem sikerült eltávolítani a szerepkört", - "assignRoleFailed": "Nem sikerült hozzárendelni a szerepkört", - "deleteRoleFailed": "Nem sikerült törölni a szerepkört", - "userAdminAccess": "Adminisztrátor", - "userAdminAccessDesc": "Teljes hozzáférés az összes adminisztrátori beállításhoz", - "userRoles": "Szerepkörök", - "revokeAllUserSessions": "Összes munkamenet visszavonása", - "revokeAllUserSessionsDesc": "Újrabejelentkezés kényszerítése minden eszközön", - "revoke": "Visszavonás", - "deleteUserWarning": "A felhasználó törlése végleges.", - "deleteUser": "Törlés {{username}}", - "deleting": "Törlés...", - "deleteUserFailed": "Nem sikerült törölni a felhasználót", - "deleteUserSuccess": "„{{username}}” felhasználó törölve", - "deleteRoleSuccess": "„{{name}}” szerepkör törölve", - "revokeKeySuccess": "A \"{{name}}\" kulcs visszavonva", - "revokeKeyFailed": "Nem sikerült visszavonni a kulcsot", - "copiedToClipboard": "Vágólapra másolva", - "done": "Kész", - "createUserTitle": "Felhasználó létrehozása", - "createUserDesc": "Hozz létre egy új helyi fiókot.", - "createUserUsername": "Felhasználónév", - "createUserPassword": "Jelszó", - "createUserPasswordHint": "Minimum 6 karakter.", - "createUserEnterUsername": "Felhasználónév megadása", - "createUserEnterPassword": "Jelszó megadása", - "createUserSubmit": "Felhasználó létrehozása", - "editUserTitle": "Felhasználó kezelése: {{username}}", - "editUserDesc": "Szerkesztheti a szerepköröket, az adminisztrátori állapotot, a munkameneteket és a fiókbeállításokat.", - "editUserUsername": "Felhasználónév", - "editUserAuthType": "Aut. típus", - "editUserAdminStatus": "Adminisztrátori állapot", - "editUserUserId": "Felhasználói azonosító", - "linkAccountTitle": "OIDC összekapcsolása Jelszófiókkal", - "linkAccountDesc": "Egyesítsd az OIDC fiókot {{username}} egy meglévő helyi fiókkal.", - "linkAccountWarningTitle": "Ez a következőket fogja eredményezni:", - "linkAccountEffect1": "Csak OIDC-t tartalmazó fiók törlése", - "linkAccountEffect2": "OIDC bejelentkezés hozzáadása a célfiókhoz", - "linkAccountEffect3": "OIDC és jelszó bejelentkezésének engedélyezése", - "linkAccountTargetUsername": "Cél felhasználónév", - "linkAccountTargetPlaceholder": "Add meg a helyi fiók felhasználónevét a csatoláshoz", - "linkAccounts": "Fiókok összekapcsolása", - "linkAccountSuccess": "OIDC-fiók összekapcsolva a következővel: \"{{username}}\"", - "linkAccountFailed": "Nem sikerült összekapcsolni az OIDC-fiókot", - "linkAccountInProgress": "Összekapcsolás...", - "saving": "Megtakarítás...", - "updateRegistrationFailed": "Nem sikerült frissíteni a regisztrációs beállításokat", - "updatePasswordLoginFailed": "Nem sikerült frissíteni a jelszó bejelentkezési beállítását", - "updateOidcAutoProvisionFailed": "Nem sikerült frissíteni az OIDC automatikus kiépítési beállítását", - "updatePasswordResetFailed": "Nem sikerült frissíteni a jelszó-visszaállítási beállítást", - "sessionTimeoutRange2": "A munkamenet időkorlátjának 1 és 720 óra között kell lennie.", - "sessionTimeoutSaved": "Munkamenet időkorlátja mentve", - "sessionTimeoutSaveFailed": "Nem sikerült menteni a munkamenet időkorlátját", - "monitoringIntervalInvalid": "Érvénytelen intervallumértékek", - "monitoringSaved": "Monitorozási beállítások mentve", - "monitoringSaveFailed": "Nem sikerült menteni a megfigyelési beállításokat", - "guacamoleSaved": "Guacamole beállítások mentve", - "guacamoleSaveFailed": "Nem sikerült menteni a guacamole beállításait", - "guacamoleUpdateFailed": "Nem sikerült frissíteni a guacamole beállítást", - "logLevelUpdateFailed": "Nem sikerült frissíteni a naplózási szintet", - "oidcSaved": "OIDC konfiguráció mentése", - "oidcSaveFailed": "Nem sikerült menteni az OIDC konfigurációt", - "oidcRemoved": "OIDC konfiguráció eltávolítva", - "oidcRemoveFailed": "Nem sikerült eltávolítani az OIDC konfigurációt", - "createUserRequired": "Felhasználónév és jelszó szükséges", - "createUserPasswordTooShort": "A jelszónak legalább 6 karakterből kell állnia", - "createUserSuccess": "„{{username}}” felhasználó létrehozva", - "createUserFailed": "Nem sikerült létrehozni a felhasználót", - "updateAdminStatusFailed": "Nem sikerült frissíteni az adminisztrátori állapotot", - "allSessionsRevoked": "Minden munkamenet visszavonva", - "revokeSessionsFailed": "Nem sikerült visszavonni a munkameneteket", - "createRoleRequired": "Név és megjelenített név megadása kötelező", - "createRoleSuccess": "\"{{name}}\" szerepkör létrehozva", - "createRoleFailed": "Nem sikerült létrehozni a szerepkört", - "apiKeyNameRequired": "Kulcsnév megadása kötelező", - "apiKeyUserRequired": "Felhasználói azonosító megadása kötelező", - "apiKeyCreatedSuccess": "„{{name}}” API-kulcs létrehozva", - "apiKeyCreateFailed": "Nem sikerült létrehozni az API-kulcsot", - "exportSuccess": "Az adatbázis exportálása sikeresen megtörtént", - "exportFailed": "Adatbázis exportálása sikertelen", - "importSelectFile": "Kérjük, először válasszon ki egy fájlt", - "importCompleted": "Importálás befejezve: {{total}} elem importálva, {{skipped}} kihagyva", - "importFailed": "Importálás sikertelen: {{error}}", - "importError": "Adatbázis importálása sikertelen" + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Házigazda", - "hostPlaceholder": "192.168.1.1 vagy example.com", - "portLabel": "Kikötő", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Felhasználónév", - "usernamePlaceholder": "felhasználónév", - "authLabel": "Aut.", - "passwordLabel": "Jelszó", - "passwordPlaceholder": "jelszó", - "privateKeyLabel": "Privát kulcs", - "privateKeyPlaceholder": "Privát kulcs beillesztése...", - "credentialLabel": "Hitelesítő adat", - "credentialPlaceholder": "Válasszon ki egy mentett hitelesítő adatot", - "connectToTerminal": "Csatlakozás a terminálhoz", - "connectToFiles": "Kapcsolódás fájlokhoz" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nincs kiválasztva terminál", - "noTerminalSelectedHint": "Nyisson meg egy SSH terminálfület a parancselőzmények megtekintéséhez", - "searchPlaceholder": "Keresési előzmények...", - "clearAll": "Összes törlése", - "noHistoryEntries": "Nincsenek előzménybejegyzések", - "trackingDisabled": "Az előzmények követése le van tiltva", - "trackingDisabledHint": "Engedélyezd a profilbeállításaidban a parancsok rögzítését." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Kulcsfelvétel", - "recordToTerminals": "Felvétel terminálokra", - "selectAll": "Minden", - "selectNone": "Egyik sem", - "noTerminalTabsOpen": "Nincsenek megnyitott terminálfülek", - "selectTerminalsAbove": "Válassza ki a fenti terminálokat", - "broadcastInputPlaceholder": "Írjon ide a billentyűleütések küldéséhez...", - "stopRecording": "Felvétel leállítása", - "startRecording": "Felvétel indítása", - "settingsTitle": "Beállítások", - "enableRightClickCopyPaste": "Jobb klikkes másolás/beillesztés engedélyezése" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Elrendezés", - "selectLayoutAbove": "Válasszon egy elrendezést fent", - "selectLayoutHint": "Válassza ki, hogy hány panelt szeretne megjeleníteni", - "panesTitle": "Ablaktáblák", - "openTabsTitle": "Nyitott lapok", - "dragTabsHint": "Húzza a lapokat a fenti panelekbe, vagy használja a Gyors hozzárendelés funkciót", - "dropHere": "Dobd ide", - "emptyPane": "Üres", - "dashboard": "Műszerfal", - "clearSplitScreen": "Tiszta osztott képernyő", - "quickAssign": "Gyors hozzárendelés", - "alreadyAssigned": "{{index}} panel", - "splitTab": "Osztott lap", - "addToSplit": "Hozzáadás Splithez", - "removeFromSplit": "Eltávolítás a felosztásból", - "assignToPane": "Hozzárendelés panelhez" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Kódrészlet létrehozása", - "createSnippetDescription": "Hozzon létre egy új parancsrészletet a gyors végrehajtáshoz", - "nameLabel": "Név", - "namePlaceholder": "pl. Indítsd újra az Nginx-et", - "descriptionLabel": "Leírás", - "descriptionPlaceholder": "Opcionális leírás", - "optional": "Választható", - "folderLabel": "Mappa", - "noFolder": "Nincs mappa (Nincs kategorizálva)", - "commandLabel": "Parancs", - "commandPlaceholder": "Pl.: sudo systemctl indítsa újra az nginx-et", - "cancel": "Mégsem", - "createSnippetButton": "Kódrészlet létrehozása", - "createFolderTitle": "Mappa létrehozása", - "createFolderDescription": "Rendezd a kódrészleteket mappákba", - "folderNameLabel": "Mappa neve", - "folderNamePlaceholder": "pl. rendszerparancsok, Docker szkriptek", - "folderColorLabel": "Mappa színe", - "folderIconLabel": "Mappa ikon", - "previewLabel": "Előnézet", - "folderNameFallback": "Mappa neve", - "createFolderButton": "Mappa létrehozása", - "targetTerminals": "Célterminálok", - "selectAll": "Minden", - "selectNone": "Egyik sem", - "noTerminalTabsOpen": "Nincsenek megnyitott terminálfülek", - "searchPlaceholder": "Keresési kódrészletek...", - "newSnippet": "Új részlet", - "newFolder": "Új mappa", - "run": "Fut", - "noSnippetsInFolder": "Nincsenek kódrészletek ebben a mappában", - "uncategorized": "Kategorizálatlan", - "editSnippetTitle": "Kódrészlet szerkesztése", - "editSnippetDescription": "Frissítse ezt a parancsrészletet", - "saveSnippetButton": "Változtatások mentése", - "createSuccess": "A kódrészlet sikeresen létrehozva", - "createFailed": "Nem sikerült létrehozni a kódrészletet", - "updateSuccess": "A részlet sikeresen frissítve", - "updateFailed": "Nem sikerült frissíteni a kódrészletet", - "deleteFailed": "Nem sikerült törölni a részletet", - "folderCreateSuccess": "Mappa sikeresen létrehozva", - "folderCreateFailed": "Nem sikerült létrehozni a mappát", - "editFolderTitle": "Mappa szerkesztése", - "editFolderDescription": "Nevezze át vagy módosítsa a mappa megjelenését", - "saveFolderButton": "Változtatások mentése", - "editFolder": "Mappa szerkesztése", - "deleteFolder": "Mappa törlése", - "folderDeleteSuccess": "A(z) „{{name}}” mappa törölve", - "folderDeleteFailed": "Nem sikerült törölni a mappát", - "folderEditSuccess": "Mappa sikeresen frissítve", - "folderEditFailed": "Nem sikerült frissíteni a mappát", - "confirmRunMessage": "Futtassa a \"{{name}} \" programot?", - "confirmRunButton": "Fut", - "runSuccess": "Futtatta a következőt: \"{{name}}\" {{count}} terminál(ok)ban", - "copySuccess": "A(z) „{{name}}” a vágólapra másolva", - "shareTitle": "Részlet megosztása", - "shareUser": "Felhasználó", - "shareRole": "Szerep", - "selectUser": "Válasszon ki egy felhasználót...", - "selectRole": "Válasszon egy szerepet...", - "shareSuccess": "A részlet sikeresen megosztva", - "shareFailed": "Nem sikerült megosztani a részletet", - "revokeSuccess": "Hozzáférés visszavonva", - "revokeFailed": "Nem sikerült visszavonni a hozzáférést", - "currentAccess": "Jelenlegi hozzáférés", - "shareLoadError": "Nem sikerült betölteni a megosztott adatokat", - "loading": "Terhelés...", - "close": "Közeli", - "reorderFailed": "Nem sikerült menteni a kódrészlet sorrendjét" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Fiók", - "sectionAppearance": "Megjelenés", - "sectionSecurity": "Biztonság", - "sectionApiKeys": "API-kulcsok", - "sectionC2sTunnels": "C2S alagutak", - "usernameLabel": "Felhasználónév", - "roleLabel": "Szerep", - "roleAdministrator": "Adminisztrátor", - "authMethodLabel": "Aut. módszer", - "authMethodLocal": "Helyi", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", "twoFaOn": "On", - "twoFaOff": "Le", - "versionLabel": "Változat", - "deleteAccount": "Fiók törlése", - "deleteAccountDescription": "Fiók végleges törlése", - "deleteButton": "Töröl", - "deleteAccountPermanent": "Ez a művelet végleges és nem vonható vissza.", - "deleteAccountWarning": "Minden munkamenet, gazdagép, hitelesítő adat és beállítás véglegesen törlődik.", - "confirmPasswordDeletePlaceholder": "Írja be jelszavát a megerősítéshez", - "languageLabel": "Nyelv", - "themeLabel": "Téma", - "fontSizeLabel": "Betűméret", - "accentColorLabel": "hangsúlyos szín", - "settingsTerminal": "Terminál", - "commandAutocomplete": "Parancs automatikus kiegészítése", - "commandAutocompleteDesc": "Automatikus kiegészítés megjelenítése gépelés közben", - "historyTracking": "Előzménykövetés", - "historyTrackingDesc": "Terminálparancsok követés", - "syntaxHighlighting": "Szintaxis kiemelése", - "syntaxHighlightingDesc": "Jelölje ki a terminál kimenetét", - "commandPalette": "Parancspaletta", - "commandPaletteDesc": "Billentyűparancs engedélyezése", - "reopenTabsOnLogin": "Lapok újranyitása bejelentkezéskor", - "reopenTabsOnLoginDesc": "Állítsa vissza a megnyitott lapokat bejelentkezéskor vagy az oldal frissítésekor, akár egy másik eszközről is", - "confirmTabClose": "Lap bezárásának megerősítése", - "confirmTabCloseDesc": "Kérdezzen rá a terminálfülek bezárása előtt", - "settingsSidebar": "Oldalsáv", - "showHostTags": "Gazdagép címkék megjelenítése", - "showHostTagsDesc": "Címkék megjelenítése a gazdagépek listájában", - "hostTrayOnClick": "Kattintson a Gazdagép-műveletek kibontásához", - "hostTrayOnClickDesc": "Mindig jelenjenek meg a kapcsolat gombok; kattints a kezelési lehetőségek kibontásához az egérrel való mozgatás helyett", - "pinAppRail": "Alkalmazássín rögzítése", - "pinAppRailDesc": "A bal oldalsáv alkalmazássávjának mindig legyen kibontva, ne pedig az egérmutatóra történő kibontás esetén", - "settingsSnippets": "Kódrészletek", - "foldersCollapsed": "Mappák összecsukva", - "foldersCollapsedDesc": "Mappák összecsukása alapértelmezés szerint", - "confirmExecution": "Végrehajtás megerősítése", - "confirmExecutionDesc": "Kódrészletek futtatása előtti megerősítés", - "settingsUpdates": "Frissítések", - "disableUpdateChecks": "Frissítés-ellenőrzések letiltása", - "disableUpdateChecksDesc": "Frissítések keresésének leállítása", - "totpAuthenticator": "TOTP hitelesítő", - "totpEnabled": "2FA engedélyezve", - "totpDisabled": "Fokozott bejelentkezési biztonság", - "disable": "Letiltás", - "enable": "Engedélyezés", - "setupTotp": "TOTP beállítása", - "qrCode": "QR-kód", - "totpInstructions": "Olvasd be a QR-kódot, vagy írd be a titkos kódot a hitelesítő alkalmazásodba, majd írd be a 6 jegyű kódot", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Ellenőrzés", - "changePassword": "Jelszó módosítása", - "currentPasswordLabel": "Jelenlegi jelszó", - "currentPasswordPlaceholder": "Jelenlegi jelszó", - "newPasswordLabel": "Új jelszó", - "newPasswordPlaceholder": "Új jelszó", - "confirmPasswordLabel": "Új jelszó megerősítése", - "confirmPasswordPlaceholder": "Új jelszó megerősítése", - "updatePassword": "Jelszó frissítése", - "createApiKeyTitle": "API-kulcs létrehozása", - "createApiKeyDescription": "Új API-kulcs létrehozása programozott hozzáféréshez.", - "apiKeyNameLabel": "Név", - "apiKeyNamePlaceholder": "pl. CI Pipeline", - "expiryDateLabel": "Lejárati dátum", - "optional": "választható", - "cancel": "Mégsem", - "createKey": "Kulcs létrehozása", - "apiKeyCount": "{{count}} billentyűk", - "newKey": "Új kulcs", - "noApiKeys": "Még nincsenek API-kulcsok.", - "apiKeyActive": "Aktív", - "apiKeyUsageHint": "Tedd bele a kulcsodat", - "apiKeyUsageHintHeader": "fejléc.", - "apiKeyPermissionsHint": "A kulcsok öröklik a létrehozó felhasználó jogosultságait.", - "roleUser": "Felhasználó", - "authMethodDual": "Kettős hitelesítés", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Nem sikerült elindítani a TOTP beállítását", - "totpEnter6Digits": "Adjon meg egy 6 jegyű kódot", - "totpEnabledSuccess": "Kétfaktoros hitelesítés engedélyezve", - "totpInvalidCode": "Érvénytelen kód, kérjük, próbálja újra", - "totpDisableInputRequired": "Add meg a TOTP kódodat vagy jelszavadat", - "totpDisabledSuccess": "Kétfaktoros hitelesítés letiltva", - "totpDisableFailed": "Nem sikerült letiltani a 2FA-t", - "totpDisableTitle": "2FA letiltása", - "totpDisablePlaceholder": "Írja be a TOTP kódot vagy jelszót", - "totpDisableConfirm": "2FA letiltása", - "totpContinueVerify": "Tovább az ellenőrzéshez", - "totpVerifyTitle": "Kód ellenőrzése", - "totpBackupTitle": "Biztonsági kódok", - "totpDownloadBackup": "Tartalékkódok letöltése", - "done": "Kész", - "secretCopied": "Titkos kód a vágólapra másolva", - "apiKeyNameRequired": "Kulcsnév megadása kötelező", - "apiKeyCreated": "„{{name}}” API-kulcs létrehozva", - "apiKeyCreateFailed": "Nem sikerült létrehozni az API-kulcsot", - "apiKeyUser": "Felhasználó", - "apiKeyExpires": "Lejár", - "apiKeyRevoked": "„{{name}}” API-kulcs visszavonva", - "apiKeyRevokeFailed": "Nem sikerült visszavonni az API-kulcsot", - "passwordFieldsRequired": "A jelenlegi és az új jelszó megadása kötelező", - "passwordMismatch": "A jelszavak nem egyeznek", - "passwordTooShort": "A jelszónak legalább 6 karakterből kell állnia", - "passwordUpdated": "Jelszó sikeresen frissítve", - "passwordUpdateFailed": "Nem sikerült frissíteni a jelszót", - "deletePasswordRequired": "Jelszó szükséges a fiók törléséhez", - "deleteFailed": "Nem sikerült törölni a fiókot", - "deleting": "Törlés...", - "colorPickerTooltip": "Színválasztó megnyitása", - "themeSystem": "Rendszer", - "themeLight": "Fény", - "themeDark": "Sötét", - "themeDracula": "Drakula", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", - "themeNord": "Észak", - "themeSolarized": "Solarizált", - "themeTokyoNight": "Tokiói éjszaka", - "themeOneDark": "Egy sötét", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/id_ID.json b/src/ui/locales/translated/id_ID.json index 25de029b..9bc5af32 100644 --- a/src/ui/locales/translated/id_ID.json +++ b/src/ui/locales/translated/id_ID.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Folder", - "folder": "Map", - "password": "Kata sandi", - "key": "Kunci", - "sshPrivateKey": "Kunci Pribadi SSH", - "upload": "Mengunggah", - "keyPassword": "Kata Sandi Kunci", - "sshKey": "Kunci SSH", - "uploadPrivateKeyFile": "Unggah File Kunci Pribadi", - "searchCredentials": "Cari kredensial...", - "addCredential": "Tambahkan Kredensial", - "caCertificate": "Sertifikat CA (-cert.pub)", - "caCertificateDescription": "Opsional: Unggah atau tempel file sertifikat yang ditandatangani CA (misalnya id_ed25519-cert.pub). Diperlukan jika server SSH Anda menggunakan otorisasi berbasis sertifikat.", - "uploadCertFile": "Unggah File -cert.pub", - "clearCert": "Jernih", - "certLoaded": "Sertifikat telah dimuat.", - "certPublicKeyLabel": "Sertifikat CA", - "certTypeLabel": "Jenis sertifikat", - "pasteOrUploadCert": "Tempel atau unggah sertifikat -cert.pub...", - "hasCaCert": "Memiliki Sertifikat CA", - "noCaCert": "Tidak memiliki Sertifikat CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Peringatan gagal dimuat.", - "failedToDismissAlert": "Gagal menutup peringatan" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Konfigurasi Server", - "description": "Konfigurasikan URL server Termix untuk terhubung ke layanan backend Anda.", - "serverUrl": "URL Server", - "enterServerUrl": "Silakan masukkan URL server", - "saveFailed": "Gagal menyimpan konfigurasi", - "saveError": "Terjadi kesalahan saat menyimpan konfigurasi.", - "saving": "Penghematan...", - "saveConfig": "Simpan Konfigurasi", - "helpText": "Masukkan URL tempat server Termix Anda berjalan (misalnya, http://localhost:30001 atau https://your-server.com)", - "changeServer": "Ubah Server", - "mustIncludeProtocol": "URL server harus diawali dengan http:// atau https://", - "allowInvalidCertificate": "Izinkan sertifikat tidak valid", - "allowInvalidCertificateDesc": "Gunakan hanya untuk server mandiri tepercaya dengan sertifikat yang ditandatangani sendiri atau sertifikat alamat IP.", - "useEmbedded": "Gunakan Server Lokal", - "embeddedDesc": "Jalankan Termix dengan server lokal bawaan (tidak memerlukan server jarak jauh)", - "embeddedConnecting": "Menghubungkan ke server lokal...", - "embeddedNotReady": "Server lokal belum siap. Mohon tunggu sebentar dan coba lagi.", - "localServer": "Server Lokal" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Kesalahan Pemeriksaan Versi", - "checkFailed": "Gagal memeriksa pembaruan.", - "upToDate": "Aplikasi sudah diperbarui.", - "currentVersion": "Anda menjalankan versi {{version}}", - "updateAvailable": "Pembaruan Tersedia", - "newVersionAvailable": "Versi baru tersedia! Anda menjalankan {{current}}, tetapi {{latest}} tersedia.", - "betaVersion": "Versi Beta", - "betaVersionDesc": "Anda menjalankan {{current}}, yang lebih baru daripada rilis stabil terbaru {{latest}}.", - "releasedOn": "Dirilis pada {{date}}", - "downloadUpdate": "Unduh Pembaruan", - "checking": "Memeriksa pembaruan...", - "checkUpdates": "Periksa Pembaruan", - "checkingUpdates": "Memeriksa pembaruan...", - "updateRequired": "Pembaruan Diperlukan" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Menutup", - "minimize": "Memperkecil", - "online": "On line", + "close": "Close", + "minimize": "Minimize", + "online": "Online", "offline": "Offline", - "continue": "Melanjutkan", - "maintenance": "Pemeliharaan", - "degraded": "Terdegradasi", - "error": "Kesalahan", - "warning": "Peringatan", - "unsavedChanges": "Perubahan yang belum disimpan", - "dismiss": "Membubarkan", - "loading": "Memuat...", - "optional": "Opsional", - "connect": "Menghubungkan", - "copied": "Disalin", - "connecting": "Menghubungkan...", - "updateAvailable": "Pembaruan Tersedia", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Buka di Tab Baru", - "noReleases": "Tidak ada rilis", - "updatesAndReleases": "Pembaruan & Rilis", - "newVersionAvailable": "Versi baru ({{version}}) tersedia.", - "failedToFetchUpdateInfo": "Gagal mengambil informasi pembaruan.", - "preRelease": "Pra-rilis", - "noReleasesFound": "Tidak ada rilis yang ditemukan.", - "cancel": "Membatalkan", - "username": "Nama pengguna", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", "login": "Login", - "register": "Daftar", - "password": "Kata sandi", - "confirmPassword": "Konfirmasi Kata Sandi", - "back": "Kembali", - "save": "Menyimpan", - "saving": "Penghematan...", - "delete": "Menghapus", - "rename": "Ganti nama", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", "edit": "Edit", - "add": "Menambahkan", - "confirm": "Mengonfirmasi", - "no": "TIDAK", - "or": "ATAU", - "next": "Berikutnya", - "previous": "Sebelumnya", - "refresh": "Menyegarkan", - "language": "Bahasa", - "checking": "Sedang memeriksa...", - "checkingDatabase": "Memeriksa koneksi basis data...", - "checkingAuthentication": "Memeriksa autentikasi...", - "backendReconnected": "Koneksi server dipulihkan.", - "connectionDegraded": "Koneksi server terputus, sedang memulihkan…", - "reload": "Muat Ulang", - "remove": "Menghapus", - "create": "Membuat", - "update": "Memperbarui", - "copy": "Menyalin", - "copyFailed": "Gagal menyalin ke papan klip", - "maximize": "Maksimalkan", - "restore": "Memulihkan", - "of": "dari" + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Rumah", + "home": "Home", "terminal": "Terminal", - "docker": "Buruh pelabuhan", + "docker": "Docker", "tunnels": "Tunnels", - "fileManager": "Pengelola File", - "serverStats": "Statistik Server", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", - "userProfile": "Profil Pengguna", - "splitScreen": "Layar Terpisah", - "confirmClose": "Tutup sesi aktif ini?", - "close": "Menutup", - "cancel": "Membatalkan", - "sshManager": "Manajer SSH", - "cannotSplitTab": "Tidak dapat memisahkan tab ini", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Salin Kata Sandi", - "copySudoPassword": "Salin Kata Sandi Sudo", - "passwordCopied": "Kata sandi disalin ke papan klip", - "noPasswordAvailable": "Tidak ada kata sandi yang tersedia", - "failedToCopyPassword": "Gagal menyalin kata sandi", - "refreshTab": "Segarkan koneksi", - "openFileManager": "Buka Pengelola File", - "dashboard": "Dasbor", - "networkGraph": "Grafik Jaringan", - "quickConnect": "Koneksi Cepat", - "sshTools": "Alat SSH", - "history": "Sejarah", - "hosts": "Tuan rumah", - "snippets": "Cuplikan", - "hostManager": "Manajer Host", - "credentials": "Kredensial", - "connections": "Koneksi", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", "roleAdministrator": "Administrator", - "roleUser": "Pengguna" + "roleUser": "User" }, "hosts": { - "hosts": "Tuan rumah", - "noHosts": "Tidak ada Host SSH", - "retry": "Mencoba kembali", - "refresh": "Menyegarkan", - "optional": "Opsional", - "downloadSample": "Unduh Sampel", - "failedToDeleteHost": "Gagal menghapus {{name}}", - "importSkipExisting": "Impor (lewati yang sudah ada)", - "connectionDetails": "Detail Koneksi", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Desktop Jarak Jauh", - "port": "Pelabuhan", - "username": "Nama pengguna", - "folder": "Map", - "tags": "Tag", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", "pin": "Pin", - "addHost": "Tambahkan Host", + "addHost": "Add Host", "editHost": "Edit Host", - "cloneHost": "Kloning Host", - "enableTerminal": "Aktifkan Terminal", - "enableTunnel": "Aktifkan Tunnel", - "enableFileManager": "Aktifkan Pengelola File", - "enableDocker": "Aktifkan Docker", - "defaultPath": "Jalur Default", - "connection": "Koneksi", - "upload": "Mengunggah", - "authentication": "Autentikasi", - "password": "Kata sandi", - "key": "Kunci", - "credential": "Mandat", - "none": "Tidak ada", - "sshPrivateKey": "Kunci Pribadi SSH", - "keyType": "Jenis Kunci", - "uploadFile": "Unggah File", - "tabGeneral": "Umum", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Terowongan", - "tabDocker": "Buruh pelabuhan", - "tabFiles": "Berkas", - "tabStats": "Statistik", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Membagikan", - "tabAuthentication": "Autentikasi", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Pengelola File", - "serverStats": "Statistik Server", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Folder \"{{oldName}}\" berhasil diganti namanya menjadi \"{{newName}}\"", - "failedToRenameFolder": "Gagal mengganti nama folder", - "movedToFolder": "Dipindahkan ke \"{{folder}}\"", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", "editHostTooltip": "Edit host", - "statusChecks": "Pemeriksaan Status", - "metricsCollection": "Pengumpulan Metrik", - "metricsInterval": "Interval Pengumpulan Metrik", - "metricsIntervalDesc": "Seberapa sering mengumpulkan statistik server (5 detik - 1 jam)", - "behavior": "Perilaku", - "themePreview": "Pratinjau Tema", - "theme": "Tema", - "fontFamily": "Keluarga Font", - "fontSize": "Ukuran Huruf", - "letterSpacing": "Jarak Antar Huruf", - "lineHeight": "Tinggi Baris", - "cursorStyle": "Gaya Kursor", - "cursorBlink": "Kedipan Kursor", - "scrollbackBuffer": "Buffer Gulir Balik", - "bellStyle": "Gaya Lonceng", - "rightClickSelectsWord": "Klik kanan memilih Word", - "fastScrollModifier": "Pengubah Gulir Cepat", - "fastScrollSensitivity": "Sensitivitas Gulir Cepat", - "sshAgentForwarding": "Penerusan Agen SSH", - "backspaceMode": "Mode Hapus", - "startupSnippet": "Cuplikan Startup", - "selectSnippet": "Pilih cuplikan", - "forceKeyboardInteractive": "Paksa Interaktif Keyboard", - "overrideCredentialUsername": "Ganti Nama Pengguna Kredensial", - "overrideCredentialUsernameDesc": "Gunakan nama pengguna yang telah ditentukan di atas, bukan nama pengguna kredensial.", - "jumpHostChain": "Rantai Host Lompatan", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Tambah Port", - "addProxyNode": "Tambahkan Node", - "proxyNode": "Node Proksi", - "proxyType": "Jenis Proksi", - "quickActions": "Tindakan Cepat", - "sudoPasswordAutoFill": "Isi Otomatis Kata Sandi Sudo", - "sudoPassword": "Kata Sandi Sudo", - "keepaliveInterval": "Interval Keepalive (ms)", - "moshCommand": "Komando MOSH", - "environmentVariables": "Variabel Lingkungan", - "addVariable": "Tambahkan Variabel", - "docker": "Buruh pelabuhan", - "copyTerminalUrl": "Salin URL Terminal", - "copyFileManagerUrl": "Salin URL Pengelola File", - "copyRemoteDesktopUrl": "Salin URL Remote Desktop", - "failedToConnect": "Gagal terhubung ke konsol", - "connect": "Menghubungkan", - "disconnect": "Memutuskan", - "start": "Awal", - "enableStatusCheck": "Aktifkan Pemeriksaan Status", - "enableMetrics": "Aktifkan Metrik", - "bulkUpdateFailed": "Pembaruan massal gagal", - "selectAll": "Pilih Semua", - "deselectAll": "Batalkan Pilihan Semua", - "protocols": "Protokol", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", "secureShell": "Secure Shell", - "virtualNetwork": "Jaringan Virtual", - "unencryptedShell": "Shell yang tidak terenkripsi", - "addressIp": "Alamat / IP", - "friendlyName": "Nama yang Ramah", - "folderAndAdvanced": "Folder & Lanjutan", - "privateNotes": "Catatan Pribadi", - "privateNotesPlaceholder": "Detail tentang server ini...", - "pinToTop": "Sematkan ke Atas", - "pinToTopDesc": "Selalu tampilkan penyedia layanan ini di bagian atas daftar.", - "portKnockingSequence": "Urutan Ketukan Port", - "addKnockBtn": "Tambahkan Ketukan", - "noPortKnocking": "Tidak ada port knocking yang dikonfigurasi.", - "knockPort": "Lubang Ketuk", - "protocol": "Protokol", - "delayAfterMs": "Penundaan Setelah (ms)", - "useSocks5Proxy": "Gunakan Proxy SOCKS5", - "useSocks5ProxyDesc": "Rute koneksi melalui server proxy.", - "proxyHost": "Host Proksi", - "proxyPort": "Port Proksi", - "proxyUsername": "Nama Pengguna Proksi", - "proxyPassword": "Kata Sandi Proksi", - "proxySingleMode": "Proksi Tunggal", - "proxyChainMode": "Rantai Proksi", - "you": "Anda", - "jumpHostChainLabel": "Rantai Host Lompatan", - "addJumpBtn": "Tambahkan Lompatan", - "noJumpHosts": "Tidak ada jump host yang dikonfigurasi.", - "selectAServer": "Pilih server...", - "sshPort": "Port SSH", - "authMethod": "Metode Otentikasi", - "storedCredential": "Kredensial Tersimpan", - "selectACredential": "Pilih kredensial...", - "keyTypeLabel": "Jenis Kunci", - "keyTypeAuto": "Deteksi Otomatis", - "keyPasteTab": "Pasta", - "keyUploadTab": "Mengunggah", - "keyFileLoaded": "Berkas kunci telah dimuat.", - "keyUploadClick": "Klik untuk mengunggah file .pem / .key / .ppk", - "clearKey": "Hapus kunci", - "keySaved": "Kunci SSH tersimpan", - "keyReplaceNotice": "Tempelkan kunci baru di bawah ini untuk menggantinya.", - "keyPassphraseSaved": "Kata sandi tersimpan, ketik untuk mengubahnya", - "replaceKey": "Ganti kunci", - "forceKeyboardInteractiveLabel": "Paksa Keyboard Interaktif", - "forceKeyboardInteractiveShortDesc": "Paksa memasukkan kata sandi secara manual meskipun kunci tersedia.", - "terminalAppearance": "Tampilan Terminal", - "colorTheme": "Tema Warna", - "fontFamilyLabel": "Keluarga Font", - "fontSizeLabel": "Ukuran Huruf", - "cursorStyleLabel": "Gaya Kursor", - "letterSpacingPx": "Jarak Antar Huruf (px)", - "lineHeightLabel": "Tinggi Baris", - "bellStyleLabel": "Gaya Lonceng", - "backspaceModeLabel": "Mode Hapus", - "cursorBlinking": "Kursor Berkedip", - "cursorBlinkingDesc": "Aktifkan animasi berkedip untuk kursor terminal.", - "rightClickSelectsWordLabel": "Klik kanan Pilih Word", - "rightClickSelectsWordShortDesc": "Pilih kata di bawah kursor dengan klik kanan.", - "behaviorAndAdvanced": "Perilaku & Tingkat Lanjut", - "scrollbackBufferLabel": "Buffer Gulir Balik", - "scrollbackMaxLines": "Jumlah maksimum baris yang disimpan dalam riwayat", - "sshAgentForwardingLabel": "Penerusan Agen SSH", - "sshAgentForwardingShortDesc": "Berikan kunci SSH lokal Anda ke host ini.", - "enableAutoMosh": "Aktifkan Auto-Mosh", - "enableAutoMoshDesc": "Lebih baik menggunakan Mosh daripada SSH jika tersedia.", - "enableAutoTmux": "Aktifkan Auto-Tmux", - "enableAutoTmuxDesc": "Secara otomatis meluncurkan atau terhubung ke sesi tmux.", - "sudoPasswordAutoFillLabel": "Isi Otomatis Kata Sandi Sudo", - "sudoPasswordAutoFillShortDesc": "Secara otomatis memberikan kata sandi sudo saat diminta.", - "sudoPasswordLabel": "Kata Sandi Sudo", - "environmentVariablesLabel": "Variabel Lingkungan", - "addVariableBtn": "Tambahkan Variabel", - "noEnvVars": "Tidak ada variabel lingkungan yang dikonfigurasi.", - "fastScrollModifierLabel": "Pengubah Gulir Cepat", - "fastScrollSensitivityLabel": "Sensitivitas Gulir Cepat", - "moshCommandLabel": "Perintah Mosh", - "startupSnippetLabel": "Cuplikan Startup", - "keepaliveIntervalLabel": "Interval Keepalive (detik)", - "maxKeepaliveMisses": "Max Keepalive Gagal", - "tunnelSettings": "Pengaturan Terowongan", - "enableTunneling": "Aktifkan Tunneling", - "enableTunnelingDesc": "Aktifkan fungsi terowongan SSH untuk host ini.", - "serverTunnelsSection": "Terowongan Server", - "addTunnelBtn": "Tambahkan Terowongan", - "noTunnelsConfigured": "Tidak ada terowongan yang dikonfigurasi.", - "tunnelLabel": "Terowongan {{number}}", - "tunnelType": "Jenis Tunnel", - "tunnelModeLocalDesc": "Teruskan port lokal ke port pada server jarak jauh (atau host yang dapat dijangkau dari server tersebut).", - "tunnelModeRemoteDesc": "Teruskan port pada server jarak jauh kembali ke port lokal pada mesin Anda.", - "tunnelModeDynamicDesc": "Buat proxy SOCKS5 pada port lokal untuk penerusan port dinamis.", - "sameHost": "Host ini (terowongan langsung)", - "endpointHost": "Host Titik Akhir", - "endpointPort": "Port Titik Akhir", - "bindHost": "Host Terikat", - "sourcePort": "Port Sumber", - "maxRetries": "Jumlah Percobaan Maksimum", - "retryIntervalS": "Interval Percobaan Ulang (detik)", - "autoStartLabel": "Mulai otomatis", - "autoStartDesc": "Terowongan ini akan terhubung secara otomatis saat host dimuat.", - "tunnelConnecting": "Terowongan penghubung...", - "tunnelDisconnected": "Terowongan terputus", - "failedToConnectTunnel": "Gagal terhubung", - "failedToDisconnectTunnel": "Gagal memutuskan koneksi", - "dockerIntegration": "Integrasi Docker", - "enableDockerMonitor": "Aktifkan Docker", - "enableDockerMonitorDesc": "Pantau dan kelola kontainer di host ini melalui Docker.", - "enableFileManagerMonitor": "Aktifkan Pengelola File", - "enableFileManagerMonitorDesc": "Jelajahi dan kelola file di host ini melalui SFTP.", - "defaultPathLabel": "Jalur Default", - "fileManagerPathHint": "Direktori yang akan dibuka saat pengelola file diluncurkan untuk host ini.", - "statusChecksLabel": "Pemeriksaan Status", - "enableStatusChecks": "Aktifkan Pemeriksaan Status", - "enableStatusChecksDesc": "Lakukan ping secara berkala ke host ini untuk memverifikasi ketersediaan.", - "useGlobalInterval": "Gunakan Interval Global", - "useGlobalIntervalDesc": "Ganti dengan interval pemeriksaan status di seluruh server.", - "checkIntervalS": "Interval Pemeriksaan (detik)", - "checkIntervalDesc": "Detik antara setiap ping konektivitas", - "metricsCollectionLabel": "Pengumpulan Metrik", - "enableMetricsLabel": "Aktifkan Metrik", - "enableMetricsDesc": "Kumpulkan data penggunaan CPU, RAM, disk, dan jaringan dari host ini.", - "useGlobalMetrics": "Gunakan Interval Global", - "useGlobalMetricsDesc": "Ganti dengan interval metrik di seluruh server.", - "metricsIntervalS": "Interval Metrik (detik)", - "metricsIntervalDesc2": "Detik antara pengambilan snapshot metrik", - "visibleWidgets": "Widget yang Terlihat", - "cpuUsageLabel": "Penggunaan CPU", - "cpuUsageDesc": "Persentase CPU, rata-rata beban, grafik sparkline", - "memoryLabel": "Penggunaan Memori", - "memoryDesc": "Penggunaan RAM, swap, cache", - "storageLabel": "Penggunaan Disk", - "storageDesc": "Penggunaan disk per titik pemasangan", - "networkLabel": "Antarmuka Jaringan", - "networkDesc": "Daftar antarmuka dan bandwidth", - "uptimeLabel": "Waktu aktif", - "uptimeDesc": "Waktu aktif sistem dan waktu booting", - "systemInfoLabel": "Informasi Sistem", - "systemInfoDesc": "Sistem operasi, kernel, nama host, arsitektur", - "recentLoginsLabel": "Login Terbaru", - "recentLoginsDesc": "Peristiwa login berhasil dan gagal", - "topProcessesLabel": "Proses Teratas", - "topProcessesDesc": "PID, CPU%, MEM%, perintah", - "listeningPortsLabel": "Port Pendengaran", - "listeningPortsDesc": "Buka port dengan proses dan status.", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Status Firewall, AppArmor, SELinux", - "quickActionsLabel": "Tindakan Cepat", - "quickActionsToolbar": "Tindakan cepat muncul sebagai tombol di bilah alat Statistik Server untuk eksekusi perintah sekali klik.", - "noQuickActions": "Belum ada tindakan cepat.", - "buttonLabel": "Label tombol", - "selectSnippetPlaceholder": "Pilih cuplikan...", - "addActionBtn": "Tambahkan Tindakan", - "hostSharedSuccessfully": "Host berhasil berbagi.", - "failedToShareHost": "Gagal berbagi host", - "accessRevoked": "Akses dicabut", - "failedToRevokeAccess": "Gagal mencabut akses", - "cancelBtn": "Membatalkan", - "savingBtn": "Penghematan...", - "addHostBtn": "Tambahkan Host", - "hostUpdated": "Host diperbarui", - "hostCreated": "Host membuat", - "failedToSave": "Gagal menyimpan host", - "credentialUpdated": "Kredensial telah diperbarui.", - "credentialCreated": "Kredensial telah dibuat.", - "failedToSaveCredential": "Gagal menyimpan kredensial", - "backToHosts": "Kembali ke Host", - "backToCredentials": "Kembali ke Kredensial", - "pinned": "Disematkan", - "noHostsFound": "Tidak ada host yang ditemukan.", - "tryDifferentTerm": "Coba istilah lain", - "addFirstHost": "Tambahkan host pertama Anda untuk memulai.", - "noCredentialsFound": "Tidak ditemukan kredensial.", - "addCredentialBtn": "Tambahkan Kredensial", - "updateCredentialBtn": "Perbarui Kredensial", - "features": "Fitur", - "noFolder": "(Tidak ada folder)", - "deleteSelected": "Menghapus", - "exitSelection": "Keluar dari pilihan", - "importSkip": "Impor (lewati yang sudah ada)", - "importOverwrite": "Impor (timpa)", - "collapseBtn": "Runtuh", - "importExportBtn": "Impor / Ekspor", - "hostStatusesRefreshed": "Status host diperbarui", - "failedToRefreshHosts": "Gagal memperbarui host", - "movedHostTo": "Memindahkan {{host}} ke \"{{folder}}\"", - "failedToMoveHost": "Gagal memindahkan host", - "folderRenamedTo": "Folder diubah namanya menjadi \"{{name}}\"", - "deletedFolder": "Folder \"{{name}} \" telah dihapus", - "failedToDeleteFolder": "Gagal menghapus folder", - "deleteAllInFolder": "Hapus semua host di \"{{name}}\"? Ini tidak dapat dibatalkan.", - "deletedHost": "Dihapus {{name}}", - "copiedToClipboard": "Disalin ke papan klip", - "terminalUrlCopied": "URL terminal disalin", - "fileManagerUrlCopied": "URL Pengelola File disalin", - "tunnelUrlCopied": "URL terowongan disalin", - "dockerUrlCopied": "URL Docker disalin", - "serverStatsUrlCopied": "URL Statistik Server disalin", - "rdpUrlCopied": "URL RDP disalin", - "vncUrlCopied": "URL VNC disalin", - "telnetUrlCopied": "URL Telnet disalin", - "remoteDesktopUrlCopied": "URL Desktop Jarak Jauh disalin", - "expandActions": "Perluas tindakan", - "collapseActions": "Ciutkan tindakan", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Paket ajaib dikirim ke {{name}}", - "wakeOnLanError": "Gagal mengirim paket ajaib", - "cloneHostAction": "Kloning Host", - "copyAddress": "Salin Alamat", - "copyLink": "Salin Tautan", - "copyTerminalUrlAction": "Salin URL Terminal", - "copyFileManagerUrlAction": "Salin URL Pengelola File", - "copyTunnelUrlAction": "Salin URL Terowongan", - "copyDockerUrlAction": "Salin URL Docker", - "copyServerStatsUrlAction": "Salin URL Statistik Server", - "copyRdpUrlAction": "Salin URL RDP", - "copyVncUrlAction": "Salin URL VNC", - "copyTelnetUrlAction": "Salin URL Telnet", - "copyRemoteDesktopUrlAction": "Salin URL Remote Desktop", - "deleteCredentialConfirm": "Hapus kredensial \"{{name}}\"?", - "deletedCredential": "Dihapus {{name}}", - "deploySSHKeyTitle": "Sebarkan Kunci SSH", - "deployingBtn": "Sedang melakukan penyebaran...", - "deployBtn": "Menyebarkan", - "failedToDeployKey": "Gagal menerapkan kunci", - "deleteHostsConfirm": "Hapus {{count}} host{{plural}}? Ini tidak dapat dibatalkan.", - "movedToRoot": "Dipindahkan ke root", - "failedToMoveHosts": "Gagal memindahkan host", - "enableTerminalFeature": "Aktifkan Terminal", - "disableTerminalFeature": "Nonaktifkan Terminal", - "enableFilesFeature": "Aktifkan File", - "disableFilesFeature": "Nonaktifkan File", - "enableTunnelsFeature": "Aktifkan Terowongan", - "disableTunnelsFeature": "Nonaktifkan Terowongan", - "enableDockerFeature": "Aktifkan Docker", - "disableDockerFeature": "Nonaktifkan Docker", - "addTagsPlaceholder": "Tambahkan tag...", - "authDetails": "Detail Otentikasi", - "credType": "Jenis", - "generateKeyPairDesc": "Buat pasangan kunci baru, baik kunci privat maupun kunci publik akan terisi secara otomatis.", - "generatingKey": "Sedang menghasilkan...", - "generateLabel": "Hasilkan {{label}}", - "uploadFileBtn": "Unggah file", - "keyPassphraseOptional": "Kata Sandi Utama (Opsional)", - "sshPublicKeyOptional": "Kunci Publik SSH (Opsional)", - "publicKeyGenerated": "Kunci publik dihasilkan", - "failedToGeneratePublicKey": "Gagal mendapatkan kunci publik.", - "publicKeyCopied": "Kunci publik disalin", - "keyPairGenerated": "{{label}} pasangan kunci dihasilkan", - "failedToGenerateKeyPair": "Gagal menghasilkan pasangan kunci", - "searchHostsPlaceholder": "Cari host, alamat, tag…", - "searchCredentialsPlaceholder": "Cari kredensial…", - "refreshBtn": "Menyegarkan", - "addTag": "Tambahkan tag...", - "deleteConfirmBtn": "Menghapus", - "tunnelRequirementsText": "Server SSH harus memiliki pengaturan GatewayPorts yes, AllowTcpForwarding yes, dan PermitRootLogin yes di /etc/ssh/sshd_config.", - "deleteHostConfirm": "Hapus \"{{name}}\"?", - "enableAtLeastOneProtocol": "Aktifkan setidaknya satu protokol di atas untuk mengkonfigurasi pengaturan otentikasi dan koneksi.", - "keyPassphrase": "Kata Sandi Kunci", - "connectBtn": "Menghubungkan", - "disconnectBtn": "Memutuskan", - "basicInformation": "Informasi Dasar", - "authDetailsSection": "Detail Otentikasi", - "credTypeLabel": "Jenis", - "hostsTab": "Tuan rumah", - "credentialsTab": "Kredensial", - "selectMultiple": "Pilih beberapa", - "selectHosts": "Pilih tuan rumah", - "connectionLabel": "Koneksi", - "authenticationLabel": "Autentikasi", - "generateKeyPairTitle": "Hasilkan Pasangan Kunci", - "generateKeyPairDescription": "Buat pasangan kunci baru, baik kunci privat maupun kunci publik akan terisi secara otomatis.", - "generateFromPrivateKey": "Hasilkan dari Kunci Pribadi", - "refreshBtn2": "Menyegarkan", - "exitSelectionTitle": "Keluar dari pilihan", - "exportAll": "Ekspor Semua", - "addHostBtn2": "Tambahkan Host", - "addCredentialBtn2": "Tambahkan Kredensial", - "checkingHostStatuses": "Memeriksa status host...", - "pinnedSection": "Disematkan", - "hostsExported": "Host berhasil diekspor.", - "exportFailed": "Gagal mengekspor host", - "sampleDownloaded": "File contoh yang diunduh", - "failedToDeleteCredential2": "Gagal menghapus kredensial", - "noFolderOption": "(Tidak ada folder)", - "nSelected": "{{count}} terpilih", - "featuresMenu": "Fitur", - "moveMenu": "Bergerak", - "cancelSelection": "Membatalkan", - "deployDialogDesc": "Sebarkan {{name}} ke authorized_keys host.", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", "targetHostLabel": "Target Host", - "selectHostOption": "Pilih tuan rumah...", - "keyDeployedSuccess": "Kunci berhasil diterapkan.", - "failedToDeployKey2": "Gagal menerapkan kunci", - "deletedCount": "Host {{count}} yang dihapus", - "failedToDeleteCount": "Gagal menghapus {{count}} host", - "duplicatedHost": "Duplikat \"{{name}}\"", - "failedToDuplicateHost": "Gagal menduplikasi host", - "updatedCount": "Host {{count}} yang diperbarui", - "friendlyNameLabel": "Nama yang Ramah", - "descriptionLabel": "Keterangan", - "loadingHost": "Memuat host...", - "loadingHosts": "Memuat host...", - "loadingCredentials": "Sedang memuat kredensial...", - "noHostsYet": "Belum ada host", - "noHostsMatchSearch": "Tidak ada host yang sesuai dengan pencarian Anda.", - "hostNotFound": "Host tidak ditemukan", - "searchHosts": "Cari host...", - "sortHosts": "Urutkan Host", - "sortDefault": "Urutan Default", - "sortNameAsc": "Nama (A → Z)", - "sortNameDesc": "Nama (Z → A)", - "sortIpAsc": "Alamat IP (Naik)", - "sortIpDesc": "Alamat IP (Deskripsi)", - "sortOnlineFirst": "Online Pertama", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Dipin Pertama", - "filterHosts": "Filter Host", - "filterClearAll": "Hapus Filter", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "On line", + "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Disematkan", - "filterAuthGroup": "Jenis Otorisasi", - "filterAuthPassword": "Kata sandi", - "filterAuthKey": "Kunci SSH", - "filterAuthCredential": "Mandat", - "filterAuthNone": "Tidak ada", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokol", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Fitur", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Pengelola File", - "filterFeatureTunnel": "Terowongan", - "filterFeatureDocker": "Buruh pelabuhan", - "filterTagsGroup": "Tag", - "shareHost": "Bagikan Host", - "shareHostTitle": "Bagikan: {{name}}", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Host ini harus menggunakan kredensial agar dapat berbagi. Edit host dan tetapkan kredensial terlebih dahulu." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Koneksi", - "authentication": "Autentikasi", - "connectionSettings": "Pengaturan Koneksi", - "displaySettings": "Pengaturan Tampilan", - "audioSettings": "Pengaturan Audio", - "rdpPerformance": "Kinerja RDP", - "deviceRedirection": "Pengalihan Perangkat", - "session": "Sidang", - "gateway": "Gerbang", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Papan klip", - "sessionRecording": "Rekaman Sesi", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Pengaturan VNC", - "terminalSettings": "Pengaturan Terminal", - "rdpPort": "Port RDP", - "username": "Nama belakang", - "password": "Kata sandi", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", "domain": "Domain", - "securityMode": "Mode Keamanan", - "colorDepth": "Kedalaman Warna", - "width": "Lebar", - "height": "Tinggi", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Metode Pengubahan Ukuran", - "clientName": "Nama Klien", - "initialProgram": "Program Awal", - "serverLayout": "Tata Letak Server", - "timezone": "Zona waktu", - "gatewayHostname": "Nama Host Gateway", - "gatewayPort": "Port Gerbang", - "gatewayUsername": "Nama Pengguna Gateway", - "gatewayPassword": "Kata Sandi Gerbang", - "gatewayDomain": "Domain Gerbang", - "remoteAppProgram": "Program RemoteApp", - "workingDirectory": "Direktori Kerja", - "arguments": "Argumen", - "normalizeLineEndings": "Normalisasi Akhiran Baris", - "recordingPath": "Jalur Perekaman", - "recordingName": "Nama Rekaman", - "macAddress": "Alamat MAC", - "broadcastAddress": "Pidato Siaran", - "udpPort": "Port UDP", - "waitTimeS": "Waktu Tunggu (detik)", - "driveName": "Nama Drive", - "drivePath": "Jalur Berkendara", - "ignoreCertificate": "Abaikan Sertifikat", - "ignoreCertificateDesc": "Izinkan koneksi ke host dengan sertifikat yang ditandatangani sendiri.", - "forceLossless": "Paksa Tanpa Kehilangan", - "forceLosslessDesc": "Paksa pengkodean gambar tanpa kehilangan data (kualitas lebih tinggi, bandwidth lebih besar)", - "disableAudio": "Nonaktifkan Audio", - "disableAudioDesc": "Nonaktifkan semua audio dari sesi jarak jauh.", - "enableAudioInput": "Aktifkan Input Audio (Mikrofon)", - "enableAudioInputDesc": "Teruskan mikrofon lokal ke sesi jarak jauh.", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", "wallpaper": "Wallpaper", - "wallpaperDesc": "Tampilkan wallpaper desktop (menonaktifkannya akan meningkatkan kinerja)", - "theming": "Tema", - "themingDesc": "Aktifkan tema dan gaya visual.", - "fontSmoothing": "Penghalusan Font", - "fontSmoothingDesc": "Aktifkan rendering font ClearType", - "fullWindowDrag": "Geser Jendela Penuh", - "fullWindowDragDesc": "Tampilkan isi jendela saat menyeret", - "desktopComposition": "Komposisi Desktop", - "desktopCompositionDesc": "Aktifkan efek kaca Aero", - "menuAnimations": "Animasi Menu", - "menuAnimationsDesc": "Aktifkan animasi pudar dan geser menu", - "disableBitmapCaching": "Nonaktifkan Cache Bitmap", - "disableBitmapCachingDesc": "Matikan cache bitmap (dapat membantu mengatasi gangguan)", - "disableOffscreenCaching": "Nonaktifkan Cache di Luar Layar", - "disableOffscreenCachingDesc": "Matikan cache di luar layar.", - "disableGlyphCaching": "Nonaktifkan Cache Glif", - "disableGlyphCachingDesc": "Matikan cache glif", - "enableGfx": "Aktifkan GFX", - "enableGfxDesc": "Gunakan pipeline grafis RemoteFX.", - "enablePrinting": "Aktifkan Pencetakan", - "enablePrintingDesc": "Alihkan printer lokal ke sesi jarak jauh.", - "enableDriveRedirection": "Aktifkan Pengalihan Drive", - "enableDriveRedirectionDesc": "Petakan folder lokal sebagai drive di sesi jarak jauh.", - "createDrivePath": "Buat Jalur Mengemudi", - "createDrivePathDesc": "Buat folder secara otomatis jika belum ada.", - "disableDownload": "Nonaktifkan Unduhan", - "disableDownloadDesc": "Mencegah pengunduhan file dari sesi jarak jauh", - "disableUpload": "Nonaktifkan Unggahan", - "disableUploadDesc": "Mencegah pengunggahan file ke sesi jarak jauh", - "enableTouch": "Aktifkan Sentuhan", - "enableTouchDesc": "Aktifkan penerusan input sentuh", - "consoleSession": "Sesi Konsol", - "consoleSessionDesc": "Hubungkan ke konsol (sesi 0) alih-alih sesi baru.", - "sendWolPacket": "Kirim Paket WOL", - "sendWolPacketDesc": "Kirim paket ajaib untuk membangunkan host ini sebelum terhubung.", - "disableCopy": "Nonaktifkan Salin", - "disableCopyDesc": "Mencegah penyalinan teks dari sesi jarak jauh", - "disablePaste": "Nonaktifkan Tempel", - "disablePasteDesc": "Mencegah penyisipan teks ke dalam sesi jarak jauh", - "createPathIfMissing": "Buat Jalur jika Belum Ada", - "createPathIfMissingDesc": "Membuat direktori rekaman secara otomatis.", - "excludeOutput": "Kecualikan Output", - "excludeOutputDesc": "Jangan merekam output layar (hanya metadata)", - "excludeMouse": "Kecualikan Tikus", - "excludeMouseDesc": "Jangan merekam pergerakan mouse.", - "includeKeystrokes": "Sertakan Penekanan Tombol", - "includeKeystrokesDesc": "Rekam setiap ketukan keyboard mentah selain output layar.", - "vncPort": "Port VNC", - "vncPassword": "Kata Sandi VNC", - "vncUsernameOptional": "Nama pengguna (opsional)", - "vncLeaveBlank": "Kosongkan jika tidak diperlukan", - "cursorMode": "Mode Kursor", - "swapRedBlue": "Tukar Merah/Biru", - "swapRedBlueDesc": "Tukar saluran warna merah dan biru (memperbaiki beberapa masalah warna)", - "readOnly": "Hanya baca", - "readOnlyDesc": "Lihat layar jarak jauh tanpa mengirimkan input apa pun.", - "telnetPort": "Port Telnet", - "terminalType": "Jenis Terminal", - "fontName": "Nama Font", - "fontSize": "Ukuran Huruf", - "colorScheme": "Skema Warna", - "backspaceKey": "Tombol Backspace", - "saveHostFirst": "Selamatkan tuan rumah terlebih dahulu.", - "sharingOptionsAfterSave": "Opsi berbagi tersedia setelah host disimpan.", - "sharingLoadError": "Gagal memuat data berbagi. Periksa koneksi Anda dan coba lagi.", - "shareHostSection": "Bagikan Host", - "shareWithUser": "Bagikan dengan Pengguna", - "shareWithRole": "Bagikan dengan Peran", - "selectUser": "Pilih Pengguna", - "selectRole": "Pilih Peran", - "selectUserOption": "Pilih pengguna...", - "selectRoleOption": "Pilih peran...", - "permissionLevel": "Tingkat Izin", - "expiresInHours": "Berakhir dalam (jam)", - "noExpiryPlaceholder": "Biarkan kosong agar tidak ada tanggal kedaluwarsa.", - "shareBtn": "Membagikan", - "currentAccess": "Akses Saat Ini", - "typeHeader": "Jenis", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Izin", - "grantedByHeader": "Diberikan Oleh", - "expiresHeader": "Berakhir", - "noAccessEntries": "Belum ada entri akses.", - "expiredLabel": "Kedaluwarsa", - "neverLabel": "Tidak pernah", - "revokeBtn": "Menarik kembali", - "cancelBtn": "Membatalkan", - "savingBtn": "Penghematan...", - "updateHostBtn": "Perbarui Host", - "addHostBtn": "Tambahkan Host" + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Cari host, perintah, atau pengaturan...", - "quickActions": "Tindakan Cepat", - "hostManager": "Manajer Host", - "hostManagerDesc": "Kelola, tambahkan, atau edit host", - "addNewHost": "Tambahkan Host Baru", - "addNewHostDesc": "Daftarkan host baru", - "adminSettings": "Pengaturan Admin", - "adminSettingsDesc": "Konfigurasi preferensi sistem dan pengguna.", - "userProfile": "Profil Pengguna", - "userProfileDesc": "Kelola akun dan preferensi Anda", - "addCredential": "Tambahkan Kredensial", - "addCredentialDesc": "Simpan kunci atau kata sandi SSH", - "recentActivity": "Aktivitas Terkini", - "serversAndHosts": "Server & Host", - "noHostsFound": "Tidak ditemukan host yang cocok dengan \"{{search}}\"", - "links": "Tautan", - "navigate": "Navigasi", - "select": "Memilih", - "toggleWith": "Beralih dengan" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Panel {{index}} - kosong", - "noTabAssigned": "Tidak ada tab yang ditetapkan", - "focusedPane": "Panel aktif" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Tidak ada koneksi", - "noConnectionsDesc": "Buka terminal, pengelola file, atau desktop jarak jauh untuk melihat koneksi di sini.", - "connectedFor": "Terhubung untuk {{duration}}", - "connected": "Terhubung", - "disconnected": "Terputus", - "closeTab": "Tutup tab", - "closeConnection": "Hubungan yang erat", - "forgetTab": "Lupa", - "removeBackground": "Menghapus", - "reconnect": "Terhubung kembali", - "reopenTab": "Buka kembali", - "sectionOpen": "Membuka", - "sectionBackground": "Latar belakang", - "backgroundDesc": "Sesi tetap aktif selama 30 menit setelah terputus dan dapat disambungkan kembali.", - "persisted": "Tetap ada di latar belakang", - "expiresIn": "Berakhir dalam {{duration}}", - "search": "Cari koneksi...", - "noSearchResults": "Tidak ada koneksi yang sesuai dengan pencarian Anda." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Menghubungkan ke sesi {{type}}...", - "connectionError": "Kesalahan koneksi", - "connectionFailed": "Koneksi gagal", - "failedToConnect": "Gagal mendapatkan token koneksi.", - "hostNotFound": "Host tidak ditemukan", - "noHostSelected": "Tidak ada pembawa acara yang dipilih.", - "reconnect": "Terhubung kembali", - "retry": "Mencoba kembali", - "guacdUnavailable": "Layanan desktop jarak jauh (guacd) tidak tersedia. Pastikan guacd berjalan, dapat diakses, dan dikonfigurasi dengan benar di pengaturan admin.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Kunci Layar)", - "winKey": "Kunci Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "Alternatif", - "shift": "Menggeser", - "win": "Menang", - "stickyActive": "{{key}} (terkunci - klik untuk membuka)", - "stickyInactive": "{{key}} (klik untuk mengunci)", - "esc": "Melarikan diri", + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Rumah", - "end": "Akhir", - "pageUp": "Halaman Atas", - "pageDown": "Halaman ke Bawah", - "arrowUp": "Panah Atas", - "arrowDown": "Panah ke Bawah", - "arrowLeft": "Panah Kiri", - "arrowRight": "Panah Kanan", - "fnToggle": "Tombol Fungsi", - "reconnect": "Sesi Terhubung Kembali", - "collapse": "Ciutkan bilah alat", - "expand": "Perluas bilah alat", - "dragHandle": "Seret untuk mengubah posisi" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Terhubung ke Host", - "clear": "Jernih", - "paste": "Pasta", - "reconnect": "Terhubung kembali", - "connectionLost": "Koneksi terputus", - "connected": "Terhubung", - "clipboardWriteFailed": "Gagal menyalin ke papan klip. Pastikan halaman disajikan melalui HTTPS atau localhost.", - "clipboardReadFailed": "Gagal membaca dari clipboard. Pastikan izin clipboard telah diberikan.", - "clipboardHttpWarning": "Paste membutuhkan HTTPS. Gunakan Ctrl+Shift+V atau sajikan Termix melalui HTTPS.", - "unknownError": "Terjadi kesalahan yang tidak diketahui.", - "websocketError": "Kesalahan koneksi WebSocket", - "connecting": "Menghubungkan...", - "noHostSelected": "Tidak ada pembawa acara yang dipilih.", - "reconnecting": "Menghubungkan kembali... ({{attempt}}/{{max}})", - "reconnected": "Berhasil terhubung kembali", - "tmuxSessionCreated": "tmux session dibuat: {{name}}", - "tmuxSessionAttached": "sesi tmux terhubung: {{name}}", - "tmuxUnavailable": "tmux tidak terinstal di host, beralih ke shell standar", - "tmuxSessionPickerTitle": "sesi tmux", - "tmuxSessionPickerDesc": "Sesi tmux yang sudah ada ditemukan di host ini. Pilih salah satu untuk disambungkan kembali atau buat sesi baru.", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", "tmuxWindows": "Windows", "tmuxWindowCount": "{{count}} window", - "tmuxAttached": "Client terhubung", - "tmuxAttachedCount": "{{count}} terhubung", - "tmuxLastActivity": "Aktivitas terakhir", - "tmuxTimeJustNow": "baru saja", - "tmuxTimeMinutes": "{{count}} menit lalu", - "tmuxTimeHours": "{{count}} jam lalu", - "tmuxTimeDays": "{{count}} detik lalu", - "tmuxCreateNew": "Buat session baru", - "tmuxCopyHint": "Sesuaikan seleksi lalu tekan Enter untuk salin ke clipboard", - "tmuxDetach": "Lepaskan diri dari sesi tmux", - "tmuxDetached": "Terputus dari sesi tmux", - "maxReconnectAttemptsReached": "Upaya penyambungan kembali maksimum telah tercapai.", - "closeTab": "Tutup", - "connectionTimeout": "Waktu habis koneksi", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Menjalankan {{command}} - {{host}}", - "totpRequired": "Diperlukan Otentikasi Dua Faktor", - "totpCodeLabel": "Kode Verifikasi", - "totpVerify": "Memeriksa", - "warpgateAuthRequired": "Autentikasi Warpgate Diperlukan", - "warpgateSecurityKey": "Kunci Keamanan", - "warpgateAuthUrl": "URL Otentikasi", - "warpgateOpenBrowser": "Buka di Browser", - "warpgateContinue": "Saya telah menyelesaikan otentikasi.", - "opksshAuthRequired": "Otentikasi OPKSSH Diperlukan", - "opksshAuthDescription": "Lakukan otentikasi di browser Anda untuk melanjutkan. Sesi ini akan tetap berlaku selama 24 jam.", - "opksshOpenBrowser": "Buka Browser untuk Melakukan Otentikasi", - "opksshWaitingForAuth": "Menunggu otentikasi di browser...", - "opksshAuthenticating": "Memproses otentikasi...", - "opksshTimeout": "Autentikasi habis waktu. Silakan coba lagi.", - "opksshAuthFailed": "Autentikasi gagal. Silakan periksa kredensial Anda dan coba lagi.", - "opksshSignInWith": "Masuk dengan {{provider}}", - "sudoPasswordPopupTitle": "Masukkan kata sandi?", - "websocketAbnormalClose": "Koneksi terputus secara tidak terduga. Ini mungkin disebabkan oleh masalah konfigurasi reverse proxy atau SSL. Silakan periksa log server.", - "connectionLogTitle": "Log Koneksi", - "connectionLogCopy": "Salin log ke papan klip", - "connectionLogEmpty": "Belum ada log koneksi.", - "connectionLogWaiting": "Menunggu log koneksi...", - "connectionLogCopied": "Log koneksi disalin ke papan klip.", - "connectionLogCopyFailed": "Gagal menyalin log ke papan klip.", - "connectionRejected": "Koneksi ditolak oleh server. Harap periksa otentikasi dan konfigurasi jaringan Anda.", - "hostKeyRejected": "Verifikasi kunci host SSH ditolak. Koneksi dibatalkan.", - "sessionTakenOver": "Sesi dibuka di tab lain. Sedang menyambungkan kembali..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Tidak ada pembawa acara yang dipilih.", - "initializingEditor": "Menginisialisasi editor...", - "file": "Mengajukan", - "folder": "Map", - "uploadFile": "Unggah File", - "downloadFile": "Unduh", - "extractArchive": "Ekstrak Arsip", - "extractingArchive": "Mengekstrak {{name}}...", - "archiveExtractedSuccessfully": "{{name}} berhasil diekstrak", - "extractFailed": "Ekstraksi gagal", - "compressFile": "Kompres File", - "compressFiles": "Kompres File", - "compressFilesDesc": "Kompres {{count}} item ke dalam arsip", - "archiveName": "Nama Arsip", - "enterArchiveName": "Masukkan nama arsip...", - "compressionFormat": "Format Kompresi", - "selectedFiles": "File terpilih", - "andMoreFiles": "dan {{count}} lainnya...", - "compress": "Kompres", - "compressingFiles": "Mengompres {{count}} item menjadi {{name}}...", - "filesCompressedSuccessfully": "{{name}} berhasil dibuat", - "compressFailed": "Kompresi gagal", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", "edit": "Edit", - "preview": "Pratinjau", - "previous": "Sebelumnya", - "next": "Berikutnya", - "pageXOfY": "Halaman {{current}} dari {{total}}", - "zoomOut": "Perkecil tampilan", - "zoomIn": "Perbesar", - "newFile": "Berkas Baru", - "newFolder": "Folder Baru", - "rename": "Ganti nama", - "uploading": "Sedang mengunggah...", - "uploadingFile": "Mengunggah {{name}}...", - "fileName": "Nama File", - "folderName": "Nama Folder", - "fileUploadedSuccessfully": "Berkas \"{{name}}\" berhasil diunggah", - "failedToUploadFile": "Gagal mengunggah file", - "fileDownloadedSuccessfully": "File \"{{name}}\" berhasil diunduh", - "failedToDownloadFile": "Gagal mengunduh file", - "fileCreatedSuccessfully": "File \"{{name}}\" berhasil dibuat", - "folderCreatedSuccessfully": "Folder \"{{name}}\" berhasil dibuat", - "failedToCreateItem": "Gagal membuat item", - "operationFailed": "Operasi {{operation}} gagal untuk {{name}}: {{error}}", - "failedToResolveSymlink": "Gagal menyelesaikan symlink", - "itemsDeletedSuccessfully": "{{count}} item berhasil dihapus", - "failedToDeleteItems": "Gagal menghapus item", - "sudoPasswordRequired": "Kata Sandi Administrator Diperlukan", - "enterSudoPassword": "Masukkan kata sandi sudo untuk melanjutkan operasi ini.", - "sudoPassword": "Kata sandi Sudo", - "sudoOperationFailed": "Operasi Sudo gagal", - "sudoAuthFailed": "Autentikasi Sudo gagal", - "dragFilesToUpload": "Seret file ke sini untuk mengunggah", - "emptyFolder": "Folder ini kosong", - "searchFiles": "Cari file...", - "upload": "Mengunggah", - "selectHostToStart": "Pilih host untuk memulai manajemen file.", - "sshRequiredForFileManager": "Pengelola file memerlukan SSH. Host ini tidak mengaktifkan SSH.", - "failedToConnect": "Gagal terhubung ke SSH", - "failedToLoadDirectory": "Gagal memuat direktori", - "noSSHConnection": "Tidak ada koneksi SSH yang tersedia.", - "copy": "Menyalin", - "cut": "Memotong", - "paste": "Pasta", - "copyPath": "Salin Jalur", - "copyPaths": "Salin Jalur", - "delete": "Menghapus", - "properties": "Properti", - "refresh": "Menyegarkan", - "downloadFiles": "Unduh {{count}} file ke Browser", - "copyFiles": "Salin {{count}} item", - "cutFiles": "Potong {{count}} item", - "deleteFiles": "Hapus {{count}} item", - "filesCopiedToClipboard": "{{count}} item disalin ke papan klip", - "filesCutToClipboard": "{{count}} item dipotong ke papan klip", - "pathCopiedToClipboard": "Jalur disalin ke papan klip", - "pathsCopiedToClipboard": "{{count}} jalur disalin ke papan klip", - "failedToCopyPath": "Gagal menyalin jalur ke papan klip.", - "movedItems": "Memindahkan {{count}} item", - "failedToDeleteItem": "Gagal menghapus item", - "itemRenamedSuccessfully": "{{type}} berhasil diganti namanya", - "failedToRenameItem": "Gagal mengganti nama item", - "download": "Unduh", - "permissions": "Izin", - "size": "Ukuran", - "modified": "Dimodifikasi", - "path": "Jalur", - "confirmDelete": "Apakah Anda yakin ingin menghapus {{name}}?", - "permissionDenied": "Izin ditolak", - "serverError": "Kesalahan Server", - "fileSavedSuccessfully": "Berkas berhasil disimpan.", - "failedToSaveFile": "Gagal menyimpan file", - "confirmDeleteSingleItem": "Apakah Anda yakin ingin menghapus \"{{name}} \" secara permanen?", - "confirmDeleteMultipleItems": "Apakah Anda yakin ingin menghapus {{count}} item secara permanen?", - "confirmDeleteMultipleItemsWithFolders": "Apakah Anda yakin ingin menghapus {{count}} item secara permanen? Ini termasuk folder dan isinya.", - "confirmDeleteFolder": "Apakah Anda yakin ingin menghapus folder \"{{name}}\" beserta seluruh isinya secara permanen?", - "permanentDeleteWarning": "Tindakan ini tidak dapat dibatalkan. Item tersebut akan dihapus secara permanen dari server.", - "recent": "Terkini", - "pinned": "Disematkan", - "folderShortcuts": "Pintasan Folder", - "failedToReconnectSSH": "Gagal menyambungkan kembali sesi SSH", - "openTerminalHere": "Buka Terminal di Sini", - "run": "Berlari", - "openTerminalInFolder": "Buka Terminal di Folder Ini", - "openTerminalInFileLocation": "Buka Terminal di Lokasi File", - "runningFile": "Berlari - {{file}}", - "onlyRunExecutableFiles": "Hanya dapat menjalankan file yang dapat dieksekusi.", - "directories": "Direktori", - "removedFromRecentFiles": "Menghapus \"{{name}}\" dari file terbaru", - "removeFailed": "Hapus yang gagal", - "unpinnedSuccessfully": "Pin \"{{name}}\" berhasil dilepas", - "unpinFailed": "Gagal membuka pin", - "removedShortcut": "Pintasan \"{{name}} \" telah dihapus", - "removeShortcutFailed": "Penghapusan pintasan gagal.", - "clearedAllRecentFiles": "Semua file terbaru telah dihapus.", - "clearFailed": "Hapus gagal", - "removeFromRecentFiles": "Hapus dari berkas terbaru", - "clearAllRecentFiles": "Hapus semua file terbaru", - "unpinFile": "Lepaskan pin file", - "removeShortcut": "Hapus pintasan", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", "pinFile": "Pin file", - "addToShortcuts": "Tambahkan ke pintasan", - "pasteFailed": "Pasta gagal", - "noUndoableActions": "Tidak ada tindakan yang tidak dapat dibatalkan.", - "undoCopySuccess": "Batalkan operasi penyalinan: Menghapus {{count}} file yang disalin", - "undoCopyFailedDelete": "Pembatalan gagal: Tidak dapat menghapus file yang disalin.", - "undoCopyFailedNoInfo": "Pembatalan gagal: Informasi file yang disalin tidak ditemukan.", - "undoMoveSuccess": "Operasi pemindahan dibatalkan: {{count}} berkas dipindahkan kembali ke lokasi semula", - "undoMoveFailedMove": "Pembatalan gagal: Tidak dapat memindahkan file apa pun kembali.", - "undoMoveFailedNoInfo": "Pembatalan gagal: Informasi file yang dipindahkan tidak ditemukan.", - "undoDeleteNotSupported": "Operasi penghapusan tidak dapat dibatalkan: File telah dihapus secara permanen dari server.", - "undoTypeNotSupported": "Jenis operasi undo yang tidak didukung", - "undoOperationFailed": "Operasi pembatalan gagal", - "unknownError": "Kesalahan tidak dikenal", - "confirm": "Mengonfirmasi", - "find": "Menemukan...", - "replace": "Mengganti", - "downloadInstead": "Unduh sebagai gantinya", - "keyboardShortcuts": "Pintasan Keyboard", - "searchAndReplace": "Cari & Ganti", - "editing": "Pengeditan", - "search": "Mencari", - "findNext": "Temukan Selanjutnya", - "findPrevious": "Temukan Sebelumnya", - "save": "Menyimpan", - "selectAll": "Pilih Semua", - "undo": "Membuka", - "redo": "Mengulangi", - "moveLineUp": "Susunan Langkah", - "moveLineDown": "Geser Garis ke Bawah", - "toggleComment": "Alihkan Komentar", - "autoComplete": "Pelengkapan Otomatis", - "imageLoadError": "Gambar gagal dimuat", - "startTyping": "Mulai mengetik...", - "unknownSize": "Ukuran tidak diketahui", - "fileIsEmpty": "Berkas kosong", - "largeFileWarning": "Peringatan Ukuran File Besar", - "largeFileWarningDesc": "File ini berukuran {{size}} , yang dapat menyebabkan masalah kinerja saat dibuka sebagai teks.", - "fileNotFoundAndRemoved": "Berkas \"{{name}}\" tidak ditemukan dan telah dihapus dari berkas terbaru/yang disematkan", - "failedToLoadFile": "Gagal memuat file: {{error}}", - "serverErrorOccurred": "Terjadi kesalahan server. Silakan coba lagi nanti.", - "autoSaveFailed": "Penyimpanan otomatis gagal", - "fileAutoSaved": "Berkas tersimpan otomatis", - "moveFileFailed": "Gagal memindahkan {{name}}", - "moveOperationFailed": "Operasi pemindahan gagal", - "canOnlyCompareFiles": "Hanya dapat membandingkan dua file.", - "comparingFiles": "Membandingkan berkas: {{file1}} dan {{file2}}", - "dragFailed": "Operasi seret gagal", - "filePinnedSuccessfully": "Berkas \"{{name}}\" berhasil disematkan", - "pinFileFailed": "Gagal menyematkan file", - "fileUnpinnedSuccessfully": "Berkas \"{{name}}\" berhasil dilepas dari pin", - "unpinFileFailed": "Gagal melepaskan pin file", - "shortcutAddedSuccessfully": "Pintasan folder \"{{name}}\" berhasil ditambahkan", - "addShortcutFailed": "Gagal menambahkan pintasan", - "operationCompletedSuccessfully": "{{operation}} {{count}} item berhasil", - "operationCompleted": "{{operation}} {{count}} item", - "downloadFileSuccess": "File {{name}} berhasil diunduh", - "downloadFileFailed": "Pengunduhan gagal", - "moveTo": "Pindah ke {{name}}", - "diffCompareWith": "Bandingkan perbedaan dengan {{name}}", - "dragOutsideToDownload": "Seret ke luar jendela untuk mengunduh ({{count}} file)", - "newFolderDefault": "Folder Baru", - "newFileDefault": "File Baru.txt", - "successfullyMovedItems": "Berhasil memindahkan {{count}} item ke {{target}}", - "move": "Bergerak", - "searchInFile": "Cari di dalam file (Ctrl+F)", - "showKeyboardShortcuts": "Tampilkan pintasan keyboard", - "startWritingMarkdown": "Mulailah menulis konten markdown Anda...", - "loadingFileComparison": "Sedang memuat perbandingan file...", - "reload": "Muat ulang", - "compare": "Membandingkan", - "sideBySide": "Berdampingan", - "inline": "Sejajar", - "fileComparison": "Perbandingan File: {{file1}} vs {{file2}}", - "fileTooLarge": "Ukuran file terlalu besar: {{error}}", - "sshConnectionFailed": "Koneksi SSH gagal. Harap periksa koneksi Anda ke {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Gagal memuat file: {{error}}", - "connecting": "Menghubungkan...", - "connectedSuccessfully": "Terhubung berhasil", - "totpVerificationFailed": "Verifikasi TOTP gagal", - "warpgateVerificationFailed": "Autentikasi Warpgate gagal", - "authenticationFailed": "Autentikasi gagal", - "verificationCodePrompt": "Kode verifikasi:", - "changePermissions": "Ubah Izin", - "currentPermissions": "Izin Saat Ini", - "owner": "Pemilik", - "group": "Kelompok", - "others": "Yang lain", - "read": "Membaca", - "write": "Menulis", - "execute": "Menjalankan", - "permissionsChangedSuccessfully": "Izin berhasil diubah.", - "failedToChangePermissions": "Gagal mengubah izin", - "name": "Nama", - "sortByName": "Nama", - "sortByDate": "Tanggal Diubah", - "sortBySize": "Ukuran", - "ascending": "Naik", - "descending": "Menurun", - "root": "Akar", - "new": "Baru", - "sortBy": "Urutkan Berdasarkan", - "items": "Barang-barang", - "selected": "Terpilih", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", - "octal": "Oktal", - "storage": "Penyimpanan", + "octal": "Octal", + "storage": "Storage", "disk": "Disk", - "used": "Digunakan", - "of": "dari", - "toggleSidebar": "Alihkan Sidebar", - "cannotLoadPdf": "Tidak dapat memuat PDF", - "pdfLoadError": "Terjadi kesalahan saat memuat file PDF ini.", - "loadingPdf": "Sedang memuat PDF...", - "loadingPage": "Sedang memuat halaman..." + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Salin ke host…", - "moveToHost": "Pindah ke host…", - "copyItemsToHost": "Salin {{count}} item ke host…", - "moveItemsToHost": "Pindahkan {{count}} item ke host…", - "noHostsConnected": "Tidak ada penyedia pengelola file lain yang tersedia.", - "noHostsConnectedHint": "Tambahkan host SSH lain dengan File Manager diaktifkan di Host Manager.", - "selectDestinationHost": "Pilih host tujuan", - "destinationPath": "Jalur tujuan", - "recentDestinations": "Destinasi terkini", - "collapseRecentDestinations": "Ciutkan destinasi terbaru", - "expandRecentDestinations": "Perluas destinasi terbaru", - "browseFolders": "Telusuri folder tujuan", - "browseDestination": "Telusuri atau masukkan jalur", - "confirmCopy": "Menyalin", - "confirmMove": "Bergerak", - "transferring": "Mentransfer…", - "compressing": "Mengompresi…", - "extracting": "Mengekstrak…", - "transferringItems": "Memindahkan {{current}} dari {{total}} item…", - "transferSuccess": "Transfer selesai", - "transferError": "Transfer gagal", - "transferPartial": "Transfer selesai dengan {{count}} kesalahan", - "transferPartialHint": "Tidak dapat mentransfer: {{paths}}", - "itemsSummary": "{{count}} item", - "destMustBeDirectory": "Tujuan harus berupa direktori untuk transfer multi-item.", - "selectThisFolder": "Pilih folder ini", - "browsePathWillBeCreated": "Folder ini belum ada. Folder ini akan dibuat saat transfer dimulai.", - "browsePathError": "Tidak dapat membuka jalur ini pada host tujuan.", - "goUp": "Naik", - "copyFolderToHost": "Salin folder ke host…", - "moveFolderToHost": "Pindahkan folder ke host…", - "hostReady": "Siap", - "hostConnecting": "Menghubungkan…", - "hostDisconnected": "Tidak terhubung", - "hostAuthRequired": "Diperlukan autentikasi — buka File Manager di host ini terlebih dahulu.", - "hostConnectionFailed": "Koneksi gagal", - "metricsTitle": "Waktu transfer", - "metricsPrepare": "Siapkan tujuan: {{duration}}", - "metricsCompress": "Kompres pada sumber: {{duration}}", - "metricsHopSourceRead": "Sumber → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → tujuan (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → tujuan (lokal): {{throughput}}", - "metricsTransfer": "Ujung ke ujung: {{throughput}} ({{duration}})", - "metricsExtract": "Ekstrak pada tujuan: {{duration}}", - "metricsSourceDelete": "Hapus dari sumber: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Kompresi pada host sumber…", - "progressExtracting": "Mengekstraksi pada tujuan…", - "progressTransferring": "Mentransfer data…", - "progressReconnecting": "Menghubungkan kembali…", - "parallelSegmentsLabel": "Jalur transfer paralel", - "parallelSegmentsOption": "{{count}} jalur", - "parallelSegmentsHint": "File berukuran besar dibagi menjadi bagian-bagian berukuran 256 MB. Beberapa jalur menggunakan koneksi terpisah (seperti memulai beberapa transfer) untuk throughput total yang lebih tinggi.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} lajur)", - "progressTransferringItems": "Mentransfer berkas ({{current}} dari {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "berkas {{current}} / {{total}}", - "sourceNotDeletedPartial": "Berkas sumber tetap disimpan (transfer sebagian)", - "jumpHostLimitation": "Kedua host harus dapat dijangkau dari server Termix. Perutean langsung antar host tidak didukung.", - "cancel": "Membatalkan", - "methodLabel": "Metode transfer", - "methodAuto": "Mobil", - "methodTar": "Arsip Tar", - "methodItemSftp": "SFTP per file", - "methodAutoHint": "Memilih antara tar atau SFTP per-file berdasarkan jumlah file, ukuran, dan kemampuan kompresi. File tunggal selalu menggunakan SFTP streaming.", - "methodTarHint": "Kompres di sumber, transfer satu arsip, ekstrak di tujuan. Membutuhkan tar di kedua host Unix.", - "methodItemSftpHint": "Transfer setiap file secara individual melalui SFTP. Berfungsi di semua host termasuk Windows.", - "methodPreviewLoading": "Metode perhitungan transfer…", - "methodPreviewError": "Metode transfer tidak dapat dipratinjau. Server akan tetap memilih metode saat Anda memulai.", - "methodPreviewWillUseTar": "Akan menggunakan: Arsip Tar", - "methodPreviewWillUseItemSftp": "Akan menggunakan: SFTP per file", - "methodPreviewScanSummary": "{{fileCount}} berkas, {{totalSize}} total (dipindai pada host sumber).", - "methodItemSftpLimitation": "Setiap berkas menggunakan aliran SFTP yang sama sebagai salinan berkas tunggal, satu demi satu. Kemajuan digabungkan di semua berkas, sehingga bilah kemajuan bergerak lambat selama pemrosesan berkas berukuran besar.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Anda memilih SFTP per-file.", - "user_tar": "Anda memilih arsip tar.", - "tar_unavailable": "Tar tidak tersedia di salah satu atau kedua host — SFTP per-file akan digunakan sebagai gantinya.", - "windows_host": "Sistem operasi yang digunakan adalah Windows — tar tidak digunakan.", - "auto_multi_large": "Otomatis: beberapa file termasuk file besar ({{largestSize}}) dengan data yang dapat dikompresi — tar menggabungkan menjadi satu transfer.", - "auto_single_large_in_archive": "Otomatis: satu file besar ({{largestSize}}) dalam set ini — SFTP per file.", - "auto_many_incompressible": "Otomatis: sebagian besar data tidak dapat dikompresi — SFTP per file.", - "auto_many_files": "Otomatis: banyak file ({{fileCount}}) — tar mengurangi overhead per file.", - "auto_default": "Otomatis: SFTP per-file untuk set ini." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Membatalkan", - "progressCancelling": "Membatalkan…", - "progressStalled": "Terhenti", - "resumedHint": "Terhubung kembali ke transfer aktif yang dimulai di jendela lain.", - "transferCancelled": "Transfer dibatalkan", - "transferCancelledCopyHint": "File yang tidak lengkap mungkin masih tersisa di tujuan.", - "transferCancelledMoveHint": "Sebagian berkas mungkin masih tersisa di tujuan. Berkas sumber tidak dihapus.", - "cleanupDestFiles": "Tujuan pembersihan", - "cleanupDestFilesSuccess": "File parsial telah dihapus dari tujuan.", - "cleanupDestFilesPartial": "Beberapa file parsial tidak dapat dihapus.", - "cleanupDestFilesNothing": "Tidak ada yang perlu dibersihkan di tempat tujuan.", - "cleanupDestFilesError": "Pembersihan gagal", - "retryTransfer": "Mencoba kembali", - "retryTransferError": "Percobaan ulang gagal.", - "transferFailedRetryHint": "Sebagian data tersimpan di tujuan. Upaya pengulangan akan dilanjutkan saat koneksi kembali pulih." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Tidak ada tunnel SSH", - "createFirstTunnelMessage": "Anda belum membuat tunnel SSH apa pun. Konfigurasikan koneksi tunnel di Host Manager untuk memulai.", - "connected": "Terhubung", - "disconnected": "Terputus", - "connecting": "Menghubungkan...", - "error": "Kesalahan", - "canceling": "Membatalkan...", - "connect": "Menghubungkan", - "disconnect": "Memutuskan", - "cancel": "Membatalkan", - "port": "Pelabuhan", - "localPort": "Pelabuhan Lokal", - "remotePort": "Port Jarak Jauh", - "currentHostPort": "Port Host Saat Ini", - "endpointPort": "Port Titik Akhir", - "bindIp": "IP Lokal", - "endpointSshConfig": "Konfigurasi Endpoint SSH", - "endpointSshHost": "Host SSH Titik Akhir", - "endpointSshHostPlaceholder": "Pilih host yang telah dikonfigurasi.", - "endpointSshHostRequired": "Pilih host SSH titik akhir untuk setiap terowongan klien.", - "attempt": "Percobaan {{current}} dari {{max}}", - "nextRetryIn": "Percobaan berikutnya dalam {{seconds}} detik", - "clientTunnels": "Terowongan Klien", - "clientTunnel": "Terowongan Klien", - "addClientTunnel": "Tambahkan Terowongan Klien", - "noClientTunnels": "Tidak ada terowongan klien yang dikonfigurasi pada desktop ini.", - "tunnelName": "Nama Tunnel", - "remoteHost": "Host Jarak Jauh", - "autoStart": "Mulai Otomatis", - "clientAutoStartDesc": "Proses dimulai saat klien desktop ini dibuka dan tetap terhubung.", - "clientManualStartDesc": "Gunakan tombol Mulai dan Berhenti dari baris ini. Termix tidak akan membukanya secara otomatis.", - "clientRemoteServerNote": "Penerusan jarak jauh mungkin memerlukan AllowTcpForwarding dan GatewayPorts pada server SSH titik akhir. Port jarak jauh akan tertutup ketika desktop ini terputus.", - "clientTunnelStarted": "Terowongan klien telah dimulai.", - "clientTunnelStopped": "Terowongan klien dihentikan", - "tunnelTestSucceeded": "Uji terowongan berhasil", - "tunnelTestFailed": "Uji terowongan gagal", - "localSaved": "Terowongan klien tersimpan", - "localSaveError": "Gagal menyimpan terowongan klien lokal", - "invalidBindIp": "IP lokal harus berupa alamat IPv4 yang valid.", - "invalidLocalTargetIp": "Alamat IP target lokal harus berupa alamat IPv4 yang valid.", - "invalidLocalPort": "Port lokal harus berada di antara 1 dan 65535.", - "invalidRemotePort": "Port jarak jauh harus berada di antara 1 dan 65535.", - "invalidLocalTargetPort": "Port target lokal harus berada di antara 1 dan 65535.", - "invalidEndpointPort": "Port endpoint harus berada di antara 1 dan 65535.", - "duplicateAutoStartBind": "Hanya satu terowongan klien auto-start yang dapat menggunakan {{bind}}.", - "manualControlError": "Gagal memperbarui status terowongan.", - "active": "Aktif", - "start": "Awal", - "stop": "Berhenti", - "test": "Tes", - "type": "Jenis Terowongan", - "typeLocal": "Lokal (-L)", - "typeRemote": "Jarak Jauh (-R)", - "typeDynamic": "Dinamis (-D)", - "typeServerLocalDesc": "Host saat ini ke titik akhir.", - "typeServerRemoteDesc": "Endpoint kembali ke host saat ini.", - "typeClientLocalDesc": "Komputer lokal ke titik akhir.", - "typeClientRemoteDesc": "Titik akhir kembali ke komputer lokal.", - "typeClientDynamicDesc": "SOCKS di komputer lokal.", - "typeDynamicDesc": "Meneruskan lalu lintas SOCKS5 CONNECT melalui SSH", - "forwardDescriptionServerLocal": "Host saat ini {{sourcePort}} → titik akhir {{endpointPort}}.", - "forwardDescriptionServerRemote": "Titik akhir {{endpointPort}} → host saat ini {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS pada host saat ini {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokal {{sourcePort}} → jarak jauh {{endpointPort}}.", - "forwardDescriptionClientRemote": "Jarak jauh {{sourcePort}} → lokal {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS pada port lokal {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → KAOS KAKI melalui {{endpoint}}", - "autoNameClientLocal": "Lokal {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokal {{localPort}}", - "autoNameClientDynamic": "KAOS KAKI {{localPort}} melalui {{endpoint}}", - "route": "Rute:", - "lastStarted": "Terakhir dimulai", - "lastTested": "Terakhir diuji", - "lastError": "Kesalahan terakhir", - "maxRetries": "Jumlah Percobaan Maksimum", - "maxRetriesDescription": "Jumlah maksimum percobaan ulang.", - "retryInterval": "Interval Percobaan Ulang (detik)", - "retryIntervalDescription": "Waktu tunggu antar upaya percobaan ulang.", - "local": "Lokal", - "remote": "Terpencil", - "destination": "Tujuan", - "host": "Tuan rumah", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", "mode": "Mode", - "noHostSelected": "Tidak ada pembawa acara yang dipilih.", - "working": "Bekerja..." + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Ingatan", + "memory": "Memory", "disk": "Disk", - "network": "Jaringan", - "uptime": "Waktu aktif", - "processes": "Proses", - "available": "Tersedia", - "free": "Bebas", - "connecting": "Menghubungkan...", - "connectionFailed": "Gagal terhubung ke server", - "naCpus": "CPU tidak tersedia", - "cpuCores_one": "{{count}} Inti", - "cpuCores_other": "{{count}} Inti", - "cpuUsage": "Penggunaan CPU", - "memoryUsage": "Penggunaan Memori", - "diskUsage": "Penggunaan Disk", - "failedToFetchHostConfig": "Gagal mengambil konfigurasi host", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", "serverOffline": "Server Offline", - "cannotFetchMetrics": "Tidak dapat mengambil metrik dari server offline.", - "totpFailed": "Verifikasi TOTP gagal", - "noneAuthNotSupported": "Statistik Server tidak mendukung tipe autentikasi 'none'.", - "load": "Memuat", - "systemInfo": "Informasi Sistem", - "hostname": "Nama host", - "operatingSystem": "Sistem Operasi", - "kernel": "Inti", - "seconds": "detik", - "networkInterfaces": "Antarmuka Jaringan", - "noInterfacesFound": "Tidak ditemukan antarmuka jaringan.", - "noProcessesFound": "Tidak ada proses yang ditemukan.", - "loginStats": "Statistik Login SSH", - "noRecentLoginData": "Tidak ada data login terbaru.", - "executingQuickAction": "Menjalankan {{name}}...", - "quickActionSuccess": "{{name}} berhasil diselesaikan", - "quickActionFailed": "{{name}} gagal", - "quickActionError": "Gagal mengeksekusi {{name}}", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Port Pendengaran", - "protocol": "Protokol", - "port": "Pelabuhan", - "address": "Alamat", - "process": "Proses", - "noData": "Tidak ada data port yang mendengarkan." + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Tidak aktif", - "policy": "Kebijakan", - "rules": "aturan", - "noData": "Tidak ada data firewall yang tersedia.", - "action": "Tindakan", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Pelabuhan", - "source": "Sumber", - "anywhere": "Di mana saja" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Beban Rata-rata", - "swap": "Menukar", - "architecture": "Arsitektur", - "refresh": "Menyegarkan", - "retry": "Mencoba kembali" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Manajemen SSH dan desktop jarak jauh yang dihosting sendiri", - "loginTitle": "Masuk ke Termix", - "registerTitle": "Buat Akun", - "forgotPassword": "Lupa kata sandi?", - "rememberMe": "Ingat Perangkat Selama 30 Hari (termasuk TOTP)", - "noAccount": "Belum punya akun?", - "hasAccount": "Sudah punya akun?", - "twoFactorAuth": "Autentikasi Dua Faktor", - "enterCode": "Masukkan kode verifikasi", - "backupCode": "Atau gunakan kode cadangan", - "verifyCode": "Verifikasi Kode", - "redirectingToApp": "Mengalihkan ke aplikasi...", - "sshAuthenticationRequired": "Autentikasi SSH Diperlukan", - "sshNoKeyboardInteractive": "Autentikasi Interaktif Keyboard Tidak Tersedia", - "sshAuthenticationFailed": "Autentikasi Gagal", - "sshAuthenticationTimeout": "Batas Waktu Otentikasi", - "sshNoKeyboardInteractiveDescription": "Server ini tidak mendukung otentikasi interaktif keyboard. Harap berikan kata sandi atau kunci SSH Anda.", - "sshAuthFailedDescription": "Kredensial yang diberikan salah. Silakan coba lagi dengan kredensial yang valid.", - "sshTimeoutDescription": "Upaya otentikasi telah habis waktu. Silakan coba lagi.", - "sshProvideCredentialsDescription": "Silakan berikan kredensial SSH Anda untuk terhubung ke server ini.", - "sshPasswordDescription": "Masukkan kata sandi untuk koneksi SSH ini.", - "sshKeyPasswordDescription": "Jika kunci SSH Anda dienkripsi, masukkan kata sandi di sini.", - "passphraseRequired": "Kata Sandi Diperlukan", - "passphraseRequiredDescription": "Kunci SSH dienkripsi. Silakan masukkan kata sandi untuk membukanya.", - "back": "Kembali", - "firstUser": "Pengguna Pertama", - "firstUserMessage": "Anda adalah pengguna pertama dan akan dijadikan admin. Anda dapat melihat pengaturan admin di menu tarik-turun pengguna di sidebar. Jika Anda merasa ini adalah kesalahan, periksa log Docker, atau buat masalah di GitHub.", - "external": "Luar", - "loginWithExternal": "Masuk dengan Penyedia Eksternal", - "loginWithExternalDesc": "Masuk menggunakan penyedia identitas eksternal yang telah Anda konfigurasi.", - "externalNotSupportedInElectron": "Autentikasi eksternal belum didukung di aplikasi Electron. Silakan gunakan versi web untuk login OIDC.", - "resetPasswordButton": "Atur Ulang Kata Sandi", - "sendResetCode": "Kirim Kode Reset", - "resetCodeDesc": "Masukkan nama pengguna Anda untuk menerima kode pengaturan ulang kata sandi. Kode tersebut akan tercatat dalam log kontainer Docker.", - "resetCode": "Atur Ulang Kode", - "verifyCodeButton": "Verifikasi Kode", - "enterResetCode": "Masukkan kode 6 digit dari log kontainer Docker untuk pengguna:", - "newPassword": "Kata Sandi Baru", - "confirmNewPassword": "Konfirmasi Kata Sandi", - "enterNewPassword": "Masukkan kata sandi baru Anda untuk pengguna:", - "signUp": "Mendaftar", - "desktopApp": "Aplikasi Desktop", - "loggingInToDesktopApp": "Masuk ke aplikasi desktop", - "loadingServer": "Memuat server...", - "dataLossWarning": "Mengatur ulang kata sandi Anda dengan cara ini akan menghapus semua host SSH, kredensial, dan data terenkripsi lainnya yang telah Anda simpan. Tindakan ini tidak dapat dibatalkan. Gunakan cara ini hanya jika Anda lupa kata sandi dan belum masuk.", - "authenticationDisabled": "Autentikasi Dinonaktifkan", - "authenticationDisabledDesc": "Semua metode otentikasi saat ini dinonaktifkan. Silakan hubungi administrator Anda.", - "attemptsRemaining": "{{count}} percobaan tersisa" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifikasi Kunci Host SSH", - "keyChangedWarning": "Kunci Host SSH Berubah", - "firstConnectionTitle": "Ini adalah kali pertama terhubung ke host ini.", - "firstConnectionDescription": "Keaslian host ini tidak dapat dipastikan. Verifikasi apakah sidik jari sesuai dengan yang Anda harapkan.", - "keyChangedDescription": "Kunci host untuk server ini telah berubah sejak koneksi terakhir Anda. Ini bisa mengindikasikan masalah keamanan.", - "previousKey": "Kunci Sebelumnya", - "newFingerprint": "Sidik Jari Baru", - "fingerprint": "Sidik jari", - "verifyInstructions": "Jika Anda mempercayai host ini, klik Terima untuk melanjutkan dan simpan sidik jari ini untuk koneksi di masa mendatang.", - "securityWarning": "Peringatan Keamanan", - "acceptAndContinue": "Terima & Lanjutkan", - "acceptNewKey": "Terima Kunci Baru & Lanjutkan" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Tidak dapat terhubung ke basis data.", - "unknownError": "Kesalahan tidak dikenal", - "loginFailed": "Login gagal.", - "failedPasswordReset": "Gagal memulai pengaturan ulang kata sandi.", - "failedVerifyCode": "Gagal memverifikasi kode reset", - "failedCompleteReset": "Gagal menyelesaikan pengaturan ulang kata sandi.", - "invalidTotpCode": "Kode TOTP tidak valid", - "failedOidcLogin": "Gagal memulai login OIDC.", - "silentSigninOidcUnavailable": "Login senyap telah diminta, tetapi login OIDC tidak tersedia.", - "failedUserInfo": "Gagal mendapatkan informasi pengguna setelah login.", - "oidcAuthFailed": "Autentikasi OIDC gagal", - "invalidAuthUrl": "URL otorisasi yang diterima dari backend tidak valid.", - "requiredField": "Kolom ini wajib diisi.", - "minLength": "Panjang minimumnya adalah {{min}}", - "passwordMismatch": "Kata sandi tidak cocok", - "passwordLoginDisabled": "Login menggunakan nama pengguna/kata sandi saat ini dinonaktifkan.", - "sessionExpired": "Sesi telah berakhir - silakan masuk kembali.", - "totpRateLimited": "Pembatasan laju: Terlalu banyak upaya verifikasi TOTP. Silakan coba lagi nanti.", - "totpRateLimitedWithTime": "Pembatasan laju: Terlalu banyak upaya verifikasi TOTP. Harap tunggu {{time}} detik sebelum mencoba lagi.", - "resetCodeRateLimited": "Pembatasan laju: Terlalu banyak upaya verifikasi. Silakan coba lagi nanti.", - "resetCodeRateLimitedWithTime": "Pembatasan laju: Terlalu banyak upaya verifikasi. Harap tunggu {{time}} detik sebelum mencoba lagi.", - "authTokenSaveFailed": "Gagal menyimpan token otentikasi.", - "failedToLoadServer": "Server gagal dimuat." + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Pendaftaran akun baru saat ini dinonaktifkan oleh administrator. Silakan masuk atau hubungi administrator.", - "userNotAllowed": "Akun Anda tidak diizinkan untuk mendaftar. Silakan hubungi administrator.", - "databaseConnectionFailed": "Gagal terhubung ke server basis data", - "resetCodeSent": "Kode reset dikirim ke log Docker", - "codeVerified": "Kode berhasil diverifikasi.", - "passwordResetSuccess": "Kata sandi berhasil direset.", - "loginSuccess": "Login berhasil.", - "registrationSuccess": "Pendaftaran berhasil" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Terowongan desktop lokal yang menargetkan host SSH yang telah dikonfigurasi.", - "c2sTunnelPresets": "Pengaturan Awal Terowongan Klien", - "c2sTunnelPresetsDesc": "Simpan daftar terowongan lokal klien desktop ini sebagai preset server bernama, atau muat preset kembali ke klien ini.", - "c2sTunnelPresetsUnavailable": "Pengaturan terowongan klien hanya tersedia di klien desktop.", - "c2sPresetName": "Nama Preset", - "c2sPresetNamePlaceholder": "Nama preset klien", - "c2sPresetToLoad": "Preset untuk Memuat", - "c2sNoPresetSelected": "Tidak ada preset yang dipilih.", - "c2sNoPresets": "Tidak ada preset yang tersimpan.", - "c2sLoadPreset": "Memuat", - "c2sCurrentLocalConfig": "{{count}} terowongan klien lokal yang dikonfigurasi pada desktop ini.", - "c2sPresetSyncNote": "Preset adalah snapshot eksplisit; memuat salah satunya akan mengganti daftar terowongan klien lokal klien desktop ini.", - "c2sPresetSaved": "Pengaturan terowongan klien tersimpan.", - "c2sPresetLoaded": "Pengaturan terowongan klien dimuat secara lokal.", - "c2sPresetRenamed": "Pengaturan terowongan klien diubah namanya.", - "c2sPresetDeleted": "Pengaturan awal terowongan klien telah dihapus.", - "c2sPresetLoadError": "Gagal memuat preset terowongan klien" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Bahasa", - "keyPassword": "kata sandi kunci", - "pastePrivateKey": "Tempelkan kunci pribadi Anda di sini...", - "localListenerHost": "127.0.0.1 (dengarkan secara lokal)", - "localTargetHost": "127.0.0.1 (target pada komputer ini)", - "socksListenerHost": "127.0.0.1 (Pendengar SOCKS)", - "enterPassword": "Masukkan kata sandi Anda", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Dasbor", - "loading": "Memuat dasbor...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Mendukung", - "discord": "Perselisihan", - "serverOverview": "Gambaran Umum Server", - "version": "Versi", - "upToDate": "Terkini", - "updateAvailable": "Pembaruan Tersedia", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Waktu aktif", - "database": "Basis data", - "healthy": "Sehat", - "error": "Kesalahan", - "totalHosts": "Total Host", - "totalTunnels": "Total Tunnel", - "totalCredentials": "Kredensial Total", - "recentActivity": "Aktivitas Terkini", - "reset": "Mengatur ulang", - "loadingRecentActivity": "Memuat aktivitas terbaru...", - "noRecentActivity": "Tidak ada aktivitas terbaru.", - "quickActions": "Tindakan Cepat", - "addHost": "Tambahkan Host", - "addCredential": "Tambahkan Kredensial", - "adminSettings": "Pengaturan Admin", - "userProfile": "Profil Pengguna", - "serverStats": "Statistik Server", - "loadingServerStats": "Memuat statistik server...", - "noServerData": "Tidak ada data server yang tersedia.", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Sesuaikan Dasbor", - "dashboardSettings": "Pengaturan Dasbor", - "enableDisableCards": "Aktifkan/Nonaktifkan Kartu", - "resetLayout": "Atur Ulang ke Pengaturan Default", - "serverOverviewCard": "Gambaran Umum Server", - "recentActivityCard": "Aktivitas Terkini", - "networkGraphCard": "Grafik Jaringan", - "networkGraph": "Grafik Jaringan", - "quickActionsCard": "Tindakan Cepat", - "serverStatsCard": "Statistik Server", - "panelMain": "Utama", - "panelSide": "Samping", - "justNow": "baru saja" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABIL", - "hostsOnline": "Host Online", - "activeTunnels": "Terowongan Aktif", - "registerNewServer": "Daftarkan server baru", - "storeSshKeysOrPasswords": "Simpan kunci atau kata sandi SSH", - "manageUsersAndRoles": "Kelola pengguna dan peran", - "manageYourAccount": "Kelola akun Anda", - "hostStatus": "Status Host", - "noHostsConfigured": "Tidak ada host yang dikonfigurasi.", - "online": "ON LINE", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", "offline": "OFFLINE", - "onlineLower": "On line", - "nodes": "{{count}} node", - "add": "Menambahkan:", - "commandPalette": "Palet Perintah", - "done": "Selesai", - "editModeInstructions": "Seret kartu untuk mengubah urutan · Seret pembatas kolom untuk mengubah ukuran kolom · Seret tepi bawah kartu untuk mengubah ukuran tingginya · Gunakan tombol Sampah untuk menghapus", - "empty": "Kosong", - "clear": "Jernih" + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Tambahkan Host", - "addGroup": "Tambahkan Grup", - "addLink": "Tambahkan Tautan", - "zoomIn": "Perbesar", - "zoomOut": "Perkecil tampilan", - "resetView": "Atur Ulang Tampilan", - "selectHost": "Pilih Host", - "chooseHost": "Pilih tuan rumah...", - "parentGroup": "Kelompok Orang Tua", - "noGroup": "Tidak Ada Grup", - "groupName": "Nama Grup", - "color": "Warna", - "source": "Sumber", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Pindah ke Grup", - "selectGroup": "Pilih grup...", - "addConnection": "Tambahkan Koneksi", - "hostDetails": "Detail Tuan Rumah", - "removeFromGroup": "Hapus dari Grup", - "addHostHere": "Tambahkan Host di Sini", - "editGroup": "Grup Pengeditan", - "delete": "Menghapus", - "add": "Menambahkan", - "create": "Membuat", - "move": "Bergerak", - "connect": "Menghubungkan", - "createGroup": "Buat Grup", - "selectSourcePlaceholder": "Pilih Sumber...", - "selectTargetPlaceholder": "Pilih Target...", - "invalidFile": "Berkas tidak valid", - "hostAlreadyExists": "Host sudah ada dalam topologi.", - "connectionExists": "Koneksi sudah ada.", - "unknown": "Tidak dikenal", - "name": "Nama", - "ip": "AKU P", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", "status": "Status", - "failedToAddNode": "Gagal menambahkan node", - "sourceDifferentFromTarget": "Sumber dan target harus berbeda.", - "exportJSON": "Ekspor JSON", - "importJSON": "Impor JSON", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Pengelola File", + "fileManager": "File Manager", "tunnel": "Tunnel", - "docker": "Buruh pelabuhan", - "serverStats": "Statistik Server", - "noNodes": "Belum ada node" + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker tidak diaktifkan untuk host ini.", - "validating": "Memvalidasi Docker...", - "connecting": "Menghubungkan...", - "error": "Kesalahan", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Gagal terhubung ke Docker", - "containerStarted": "Kontainer {{name}} dimulai", - "failedToStartContainer": "Gagal memulai kontainer {{name}}", - "containerStopped": "Kontainer {{name}} berhenti", - "failedToStopContainer": "Gagal menghentikan kontainer {{name}}", - "containerRestarted": "Kontainer {{name}} dimulai ulang", - "failedToRestartContainer": "Gagal memulai ulang kontainer {{name}}", - "containerPaused": "Kontainer {{name}} berhenti", - "containerUnpaused": "Kontainer {{name}} tidak dijeda", - "failedToTogglePauseContainer": "Gagal mengubah status jeda untuk kontainer {{name}}", - "containerRemoved": "Kontainer {{name}} dihapus", - "failedToRemoveContainer": "Gagal menghapus kontainer {{name}}", - "image": "Gambar", - "ports": "Pelabuhan", - "noPorts": "Tidak ada port", - "start": "Awal", - "confirmRemoveContainer": "Apakah Anda yakin ingin menghapus kontainer '{{name}}'? Tindakan ini tidak dapat dibatalkan.", - "runningContainerWarning": "Peringatan: Kontainer ini sedang berjalan. Menghapusnya akan menghentikan kontainer terlebih dahulu.", - "loadingContainers": "Memuat kontainer...", - "manager": "Manajer Docker", - "autoRefresh": "Penyegaran Otomatis", - "timestamps": "Cap waktu", - "lines": "Garis", - "filterLogs": "Filter log...", - "refresh": "Menyegarkan", - "download": "Unduh", - "clear": "Jernih", - "logsDownloaded": "Log berhasil diunduh.", - "last50": "50 Terakhir", - "last100": "100 terakhir", - "last500": "500 Terakhir", - "last1000": "1000 terakhir", - "allLogs": "Semua Log", - "noLogsMatching": "Tidak ada log yang cocok dengan \"{{query}}\"", - "noLogsAvailable": "Tidak ada log yang tersedia.", - "noContainersFound": "Tidak ditemukan wadah apa pun", - "noContainersFoundHint": "Tidak ada kontainer Docker yang tersedia di host ini.", - "searchPlaceholder": "Cari kontainer...", - "allStatuses": "Semua Status", - "stateRunning": "Berlari", - "statePaused": "Dijeda", - "stateExited": "Keluar", - "stateRestarting": "Memulai ulang", - "noContainersMatchFilters": "Tidak ada wadah yang sesuai dengan filter Anda.", - "noContainersMatchFiltersHint": "Cobalah menyesuaikan kriteria pencarian atau filter Anda.", - "failedToFetchStats": "Gagal mengambil statistik kontainer", - "containerNotRunning": "Kontainer tidak berjalan", - "startContainerToViewStats": "Jalankan kontainer untuk melihat statistik.", - "loadingStats": "Memuat statistik...", - "errorLoadingStats": "Terjadi kesalahan saat memuat statistik.", - "noStatsAvailable": "Tidak ada statistik yang tersedia.", - "cpuUsage": "Penggunaan CPU", - "current": "Saat ini", - "memoryUsage": "Penggunaan Memori", - "networkIo": "Input/Output Jaringan", - "input": "Masukan", - "output": "Keluaran", - "blockIo": "Blok I/O", - "read": "Membaca", - "write": "Menulis", - "pids": "PID", - "containerInformation": "Informasi Kontainer", - "name": "Nama", - "id": "PENGENAL", - "state": "Negara", - "containerMustBeRunning": "Kontainer harus berjalan agar dapat mengakses konsol.", - "verificationCodePrompt": "Masukkan kode verifikasi", - "totpVerificationFailed": "Verifikasi TOTP gagal. Silakan coba lagi.", - "warpgateVerificationFailed": "Autentikasi Warpgate gagal. Silakan coba lagi.", - "connectedTo": "Terhubung ke {{containerName}}", - "disconnected": "Terputus", - "consoleError": "Kesalahan konsol", - "errorMessage": "Kesalahan: {{message}}", - "failedToConnect": "Gagal terhubung ke kontainer", - "console": "Menghibur", - "selectShell": "Pilih cangkang", - "bash": "Pesta", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", "sh": "sh", - "ash": "abu", - "connect": "Menghubungkan", - "disconnect": "Memutuskan", - "notConnected": "Tidak terhubung", - "clickToConnect": "Klik sambungkan untuk memulai sesi shell.", - "connectingTo": "Menghubungkan ke {{containerName}}...", - "containerNotFound": "Kontainer tidak ditemukan", - "backToList": "Kembali ke Daftar", - "logs": "Log", - "stats": "Statistik", - "consoleTab": "Menghibur", - "startContainerToAccess": "Jalankan kontainer untuk mengakses konsol." + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Umum", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Pengguna", - "sectionSessions": "Sesi", - "sectionRoles": "Peran", - "sectionDatabase": "Basis data", - "sectionApiKeys": "Kunci API", - "allowRegistration": "Izinkan Pendaftaran Pengguna", - "allowRegistrationDesc": "Izinkan pengguna baru mendaftar sendiri.", - "allowPasswordLogin": "Izinkan Login dengan Kata Sandi", - "allowPasswordLoginDesc": "Login dengan nama pengguna/kata sandi", - "oidcAutoProvision": "Penyediaan Otomatis OIDC", - "oidcAutoProvisionDesc": "Buat akun secara otomatis untuk pengguna OIDC bahkan ketika pendaftaran dinonaktifkan.", - "allowPasswordReset": "Izinkan Reset Kata Sandi", - "allowPasswordResetDesc": "Reset kode melalui log Docker", - "sessionTimeout": "Sesi Kedaluwarsa", - "hours": "jam", - "sessionTimeoutRange": "Minimal 1 jam · Maksimal 720 jam", - "monitoringDefaults": "Pemantauan Default", - "statusCheck": "Pemeriksaan Status", - "metrics": "Metrik", - "sec": "detik", - "logLevel": "Level Log", - "enableGuacamole": "Aktifkan Guacamole", - "enableGuacamoleDesc": "Desktop jarak jauh RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Konfigurasikan OpenID Connect untuk SSO. Kolom yang ditandai * wajib diisi.", - "oidcClientId": "ID Klien", - "oidcClientSecret": "Rahasia Klien", - "oidcAuthUrl": "URL Otorisasi", - "oidcIssuerUrl": "URL Penerbit", - "oidcTokenUrl": "URL Token", - "oidcUserIdentifier": "Jalur Pengidentifikasi Pengguna", - "oidcDisplayName": "Jalur Nama Tampilan", - "oidcScopes": "Lingkup", - "oidcUserinfoUrl": "Ganti URL Informasi Pengguna", - "oidcAllowedUsers": "Pengguna yang Diizinkan", - "oidcAllowedUsersDesc": "Satu email per baris. Biarkan kosong untuk mengizinkan semua email.", - "removeOidc": "Menghapus", - "usersCount": "{{count}} pengguna", - "createUser": "Membuat", - "newRole": "Peran Baru", - "roleName": "Nama", - "roleDisplayName": "Nama Tampilan", - "roleDescription": "Keterangan", - "rolesCount": "{{count}} peran", - "createRole": "Membuat", - "creating": "Membuat...", - "exportDatabase": "Ekspor Basis Data", - "exportDatabaseDesc": "Unduh cadangan semua host, kredensial, dan pengaturan.", - "export": "Ekspor", - "exporting": "Mengekspor...", - "importDatabase": "Impor Basis Data", - "importDatabaseDesc": "Pulihkan dari file cadangan .sqlite", - "importDatabaseSelected": "Dipilih: {{name}}", - "selectFile": "Pilih File", - "changeFile": "Mengubah", - "import": "Impor", - "importing": "Pengimporan...", - "apiKeysCount": "{{count}} kunci", - "newApiKey": "Kunci API Baru", - "apiKeyCreatedWarning": "Kunci telah dibuat - salin sekarang, kunci ini tidak akan ditampilkan lagi.", - "apiKeyName": "Nama", - "apiKeyUser": "Pengguna", - "apiKeySelectUser": "Pilih pengguna...", - "apiKeyExpiresAt": "Berakhir pada", - "createKey": "Buat Kunci", - "apiKeyNoExpiry": "Tidak ada tanggal kedaluwarsa", - "revokedBadge": "DICABUT", - "authTypeDual": "Otorisasi Ganda", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokal", + "authTypeLocal": "Local", "adminStatusAdministrator": "Administrator", - "adminStatusRegularUser": "Pengguna Biasa", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "Sistem", - "customBadge": "KEBIASAAN", - "youBadge": "ANDA", - "sessionsActive": "{{count}} aktif", - "sessionActive": "Aktif: {{time}}", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Semua", - "revokeAllSessionsSuccess": "Semua sesi untuk pengguna telah dicabut.", - "revokeAllSessionsFailed": "Gagal mencabut sesi", - "revokeSessionFailed": "Gagal mencabut sesi", - "addRole": "Tambahkan peran", - "noCustomRoles": "Tidak ada peran khusus yang ditentukan.", - "removeRoleFailed": "Gagal menghapus peran", - "assignRoleFailed": "Gagal menetapkan peran", - "deleteRoleFailed": "Gagal menghapus peran", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Akses penuh ke semua pengaturan admin.", - "userRoles": "Peran", - "revokeAllUserSessions": "Batalkan Semua Sesi", - "revokeAllUserSessionsDesc": "Paksa masuk ulang di semua perangkat", - "revoke": "Menarik kembali", - "deleteUserWarning": "Penghapusan pengguna ini bersifat permanen.", - "deleteUser": "Hapus {{username}}", - "deleting": "Menghapus...", - "deleteUserFailed": "Gagal menghapus pengguna", - "deleteUserSuccess": "Pengguna \"{{username}}\" dihapus", - "deleteRoleSuccess": "Peran \"{{name}}\" dihapus", - "revokeKeySuccess": "Kunci \"{{name}}\" dicabut", - "revokeKeyFailed": "Gagal mencabut kunci", - "copiedToClipboard": "Disalin ke papan klip", - "done": "Selesai", - "createUserTitle": "Buat Pengguna", - "createUserDesc": "Buat akun lokal baru.", - "createUserUsername": "Nama belakang", - "createUserPassword": "Kata sandi", - "createUserPasswordHint": "Minimal 6 karakter.", - "createUserEnterUsername": "Masukkan nama pengguna", - "createUserEnterPassword": "Masukkan kata sandi", - "createUserSubmit": "Buat Pengguna", - "editUserTitle": "Kelola Pengguna: {{username}}", - "editUserDesc": "Edit peran, status admin, sesi, dan pengaturan akun.", - "editUserUsername": "Nama belakang", - "editUserAuthType": "Jenis Otorisasi", - "editUserAdminStatus": "Status Admin", - "editUserUserId": "ID Pengguna", - "linkAccountTitle": "Hubungkan OIDC ke Akun Kata Sandi", - "linkAccountDesc": "Gabungkan akun OIDC {{username}} dengan akun lokal yang sudah ada.", - "linkAccountWarningTitle": "Ini akan:", - "linkAccountEffect1": "Hapus akun khusus OIDC.", - "linkAccountEffect2": "Tambahkan login OIDC ke akun target.", - "linkAccountEffect3": "Izinkan login melalui OIDC dan kata sandi.", - "linkAccountTargetUsername": "Nama Pengguna Target", - "linkAccountTargetPlaceholder": "Masukkan nama pengguna akun lokal yang ingin dihubungkan.", - "linkAccounts": "Tautkan Akun", - "linkAccountSuccess": "Akun OIDC terhubung ke \"{{username}}\"", - "linkAccountFailed": "Gagal menautkan akun OIDC", - "linkAccountInProgress": "Menghubungkan...", - "saving": "Penghematan...", - "updateRegistrationFailed": "Gagal memperbarui pengaturan pendaftaran", - "updatePasswordLoginFailed": "Gagal memperbarui pengaturan login kata sandi.", - "updateOidcAutoProvisionFailed": "Gagal memperbarui pengaturan penyediaan otomatis OIDC.", - "updatePasswordResetFailed": "Gagal memperbarui pengaturan pengaturan ulang kata sandi.", - "sessionTimeoutRange2": "Batas waktu sesi harus antara 1 dan 720 jam.", - "sessionTimeoutSaved": "Gagal menyimpan batas waktu sesi", - "sessionTimeoutSaveFailed": "Gagal menyimpan waktu habis sesi", - "monitoringIntervalInvalid": "Nilai interval tidak valid", - "monitoringSaved": "Pengaturan pemantauan tersimpan", - "monitoringSaveFailed": "Gagal menyimpan pengaturan pemantauan", - "guacamoleSaved": "Pengaturan guacamole tersimpan.", - "guacamoleSaveFailed": "Gagal menyimpan pengaturan Guacamole", - "guacamoleUpdateFailed": "Gagal memperbarui pengaturan Guacamole", - "logLevelUpdateFailed": "Gagal memperbarui level log.", - "oidcSaved": "Konfigurasi OIDC tersimpan", - "oidcSaveFailed": "Gagal menyimpan konfigurasi OIDC", - "oidcRemoved": "Konfigurasi OIDC dihapus", - "oidcRemoveFailed": "Gagal menghapus konfigurasi OIDC", - "createUserRequired": "Nama pengguna dan kata sandi diperlukan.", - "createUserPasswordTooShort": "Kata sandi harus minimal 6 karakter.", - "createUserSuccess": "Pengguna \"{{username}}\" membuat", - "createUserFailed": "Gagal membuat pengguna", - "updateAdminStatusFailed": "Gagal memperbarui status admin", - "allSessionsRevoked": "Semua sesi dibatalkan", - "revokeSessionsFailed": "Gagal mencabut sesi", - "createRoleRequired": "Nama dan nama tampilan wajib diisi.", - "createRoleSuccess": "Peran \"{{name}}\" dibuat", - "createRoleFailed": "Gagal membuat peran", - "apiKeyNameRequired": "Nama kunci wajib diisi.", - "apiKeyUserRequired": "ID pengguna diperlukan.", - "apiKeyCreatedSuccess": "Kunci API \"{{name}}\" berhasil dibuat", - "apiKeyCreateFailed": "Gagal membuat kunci API", - "exportSuccess": "Basis data berhasil diekspor.", - "exportFailed": "Ekspor basis data gagal", - "importSelectFile": "Silakan pilih file terlebih dahulu.", - "importCompleted": "Impor selesai: {{total}} item diimpor, {{skipped}} dilewati", - "importFailed": "Impor gagal: {{error}}", - "importError": "Impor basis data gagal" + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Tuan rumah", - "hostPlaceholder": "192.168.1.1 atau example.com", - "portLabel": "Pelabuhan", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nama belakang", - "usernamePlaceholder": "nama belakang", - "authLabel": "Otorisasi", - "passwordLabel": "Kata sandi", - "passwordPlaceholder": "kata sandi", - "privateKeyLabel": "Kunci Pribadi", - "privateKeyPlaceholder": "Tempelkan kunci pribadi...", - "credentialLabel": "Mandat", - "credentialPlaceholder": "Pilih kredensial yang tersimpan", - "connectToTerminal": "Hubungkan ke Terminal", - "connectToFiles": "Menghubungkan ke File" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Tidak ada terminal yang dipilih", - "noTerminalSelectedHint": "Buka tab terminal SSH untuk melihat riwayat perintahnya.", - "searchPlaceholder": "Riwayat pencarian...", - "clearAll": "Hapus Semua", - "noHistoryEntries": "Tidak ada entri riwayat.", - "trackingDisabled": "Pelacakan riwayat dinonaktifkan", - "trackingDisabledHint": "Aktifkan fitur ini di pengaturan profil Anda untuk merekam perintah." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Rekaman Kunci", - "recordToTerminals": "Rekam ke terminal", - "selectAll": "Semua", - "selectNone": "Tidak ada", - "noTerminalTabsOpen": "Tidak ada tab terminal yang terbuka.", - "selectTerminalsAbove": "Pilih terminal di atas", - "broadcastInputPlaceholder": "Ketik di sini untuk menyiarkan ketukan keyboard...", - "stopRecording": "Hentikan Perekaman", - "startRecording": "Mulai Merekam", - "settingsTitle": "Pengaturan", - "enableRightClickCopyPaste": "Aktifkan salin/tempel dengan klik kanan" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Tata Letak", - "selectLayoutAbove": "Pilih tata letak di atas", - "selectLayoutHint": "Pilih berapa banyak panel yang ingin ditampilkan.", - "panesTitle": "Kaca", - "openTabsTitle": "Buka Tab", - "dragTabsHint": "Seret tab ke panel di atas, atau gunakan Penugasan Cepat.", - "dropHere": "Jatuhkan di sini", - "emptyPane": "Kosong", - "dashboard": "Dasbor", - "clearSplitScreen": "Hapus Layar Terpisah", - "quickAssign": "Penugasan Cepat", - "alreadyAssigned": "Panel {{index}}", - "splitTab": "Tab Terpisah", - "addToSplit": "Tambahkan ke Split", - "removeFromSplit": "Hapus dari Split", - "assignToPane": "Tetapkan ke panel" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Buat Cuplikan", - "createSnippetDescription": "Buat cuplikan perintah baru untuk eksekusi cepat.", - "nameLabel": "Nama", - "namePlaceholder": "misalnya, Mulai ulang Nginx", - "descriptionLabel": "Keterangan", - "descriptionPlaceholder": "Deskripsi opsional", - "optional": "Opsional", - "folderLabel": "Map", - "noFolder": "Tidak ada folder (Tidak terkategorikan)", - "commandLabel": "Memerintah", - "commandPlaceholder": "misalnya, sudo systemctl restart nginx", - "cancel": "Membatalkan", - "createSnippetButton": "Buat Cuplikan", - "createFolderTitle": "Buat Folder", - "createFolderDescription": "Susun cuplikan Anda ke dalam folder.", - "folderNameLabel": "Nama Folder", - "folderNamePlaceholder": "misalnya, Perintah Sistem, Skrip Docker", - "folderColorLabel": "Warna Folder", - "folderIconLabel": "Ikon Folder", - "previewLabel": "Pratinjau", - "folderNameFallback": "Nama Folder", - "createFolderButton": "Buat Folder", - "targetTerminals": "Terminal Target", - "selectAll": "Semua", - "selectNone": "Tidak ada", - "noTerminalTabsOpen": "Tidak ada tab terminal yang terbuka.", - "searchPlaceholder": "Cuplikan pencarian...", - "newSnippet": "Cuplikan Baru", - "newFolder": "Folder Baru", - "run": "Berlari", - "noSnippetsInFolder": "Tidak ada cuplikan di folder ini", - "uncategorized": "Tidak dikategorikan", - "editSnippetTitle": "Edit Cuplikan", - "editSnippetDescription": "Perbarui cuplikan perintah ini", - "saveSnippetButton": "Simpan Perubahan", - "createSuccess": "Cuplikan berhasil dibuat.", - "createFailed": "Gagal membuat cuplikan", - "updateSuccess": "Cuplikan berhasil diperbarui.", - "updateFailed": "Gagal memperbarui cuplikan", - "deleteFailed": "Gagal menghapus cuplikan", - "folderCreateSuccess": "Folder berhasil dibuat.", - "folderCreateFailed": "Gagal membuat folder", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", "editFolderTitle": "Edit Folder", - "editFolderDescription": "Ganti nama atau ubah tampilan folder ini", - "saveFolderButton": "Simpan Perubahan", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", "editFolder": "Edit folder", - "deleteFolder": "Hapus folder", - "folderDeleteSuccess": "Folder \"{{name}}\" dihapus", - "folderDeleteFailed": "Gagal menghapus folder", - "folderEditSuccess": "Folder berhasil diperbarui.", - "folderEditFailed": "Gagal memperbarui folder", - "confirmRunMessage": "Jalankan \"{{name}}\"?", - "confirmRunButton": "Berlari", - "runSuccess": "Menjalankan \"{{name}}\" di {{count}} terminal", - "copySuccess": "\"{{name}}\" disalin ke papan klip", - "shareTitle": "Bagikan Cuplikan", - "shareUser": "Pengguna", - "shareRole": "Peran", - "selectUser": "Pilih pengguna...", - "selectRole": "Pilih peran...", - "shareSuccess": "Cuplikan berhasil dibagikan", - "shareFailed": "Cuplikan gagal dibagikan", - "revokeSuccess": "Akses dicabut", - "revokeFailed": "Gagal mencabut akses", - "currentAccess": "Akses Saat Ini", - "shareLoadError": "Gagal memuat data berbagi.", - "loading": "Memuat...", - "close": "Menutup", - "reorderFailed": "Gagal menyimpan urutan cuplikan" + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Akun", - "sectionAppearance": "Penampilan", - "sectionSecurity": "Keamanan", - "sectionApiKeys": "Kunci API", - "sectionC2sTunnels": "Terowongan C2S", - "usernameLabel": "Nama belakang", - "roleLabel": "Peran", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", "roleAdministrator": "Administrator", - "authMethodLabel": "Metode Otentikasi", - "authMethodLocal": "Lokal", - "twoFaLabel": "Otentikasi Dua Faktor (2FA)", - "twoFaOn": "Pada", - "twoFaOff": "Mati", - "versionLabel": "Versi", - "deleteAccount": "Hapus Akun", - "deleteAccountDescription": "Hapus akun Anda secara permanen", - "deleteButton": "Menghapus", - "deleteAccountPermanent": "Tindakan ini bersifat permanen dan tidak dapat dibatalkan.", - "deleteAccountWarning": "Semua sesi, host, kredensial, dan pengaturan akan dihapus secara permanen.", - "confirmPasswordDeletePlaceholder": "Masukkan kata sandi Anda untuk konfirmasi.", - "languageLabel": "Bahasa", - "themeLabel": "Tema", - "fontSizeLabel": "Ukuran Huruf", - "accentColorLabel": "Warna Aksen", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Pelengkapan Otomatis Perintah", - "commandAutocompleteDesc": "Tampilkan fitur pelengkapan otomatis saat mengetik.", - "historyTracking": "Pelacakan Riwayat", - "historyTrackingDesc": "Lacak perintah terminal", - "syntaxHighlighting": "Penyorotan Sintaksis", - "syntaxHighlightingDesc": "Sorot keluaran terminal", - "commandPalette": "Palet Perintah", - "commandPaletteDesc": "Aktifkan pintasan keyboard", - "reopenTabsOnLogin": "Buka Kembali Tab saat Masuk", - "reopenTabsOnLoginDesc": "Pulihkan tab yang terbuka saat Anda masuk atau menyegarkan halaman, bahkan dari perangkat lain.", - "confirmTabClose": "Konfirmasi Penutupan Tab", - "confirmTabCloseDesc": "Tanyakan terlebih dahulu sebelum menutup tab terminal.", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Tag Pembawa Acara", - "showHostTagsDesc": "Tampilkan tag di daftar host", - "hostTrayOnClick": "Klik untuk Memperluas Tindakan Host", - "hostTrayOnClickDesc": "Selalu tampilkan tombol koneksi; klik untuk memperluas opsi manajemen, bukan dengan mengarahkan kursor.", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Pertahankan agar bilah sisi kiri aplikasi selalu terbuka, alih-alih terbuka saat diarahkan kursor.", - "settingsSnippets": "Cuplikan", - "foldersCollapsed": "Folder Dilipat", - "foldersCollapsedDesc": "Secara default, folder akan diciutkan.", - "confirmExecution": "Konfirmasi Eksekusi", - "confirmExecutionDesc": "Konfirmasikan sebelum menjalankan cuplikan kode.", - "settingsUpdates": "Pembaruan", - "disableUpdateChecks": "Nonaktifkan Pemeriksaan Pembaruan", - "disableUpdateChecksDesc": "Berhenti memeriksa pembaruan.", - "totpAuthenticator": "Otentikator TOTP", - "totpEnabled": "Otentikasi dua faktor (2FA) diaktifkan.", - "totpDisabled": "Tambahkan keamanan login ekstra", - "disable": "Cacat", - "enable": "Memungkinkan", - "setupTotp": "Pengaturan TOTP", - "qrCode": "Kode QR", - "totpInstructions": "Pindai kode QR atau masukkan kode rahasia di aplikasi otentikasi Anda, lalu masukkan kode 6 digit.", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Memeriksa", - "changePassword": "Ubah Kata Sandi", - "currentPasswordLabel": "Kata Sandi Saat Ini", - "currentPasswordPlaceholder": "Kata sandi saat ini", - "newPasswordLabel": "Kata Sandi Baru", - "newPasswordPlaceholder": "Kata sandi baru", - "confirmPasswordLabel": "Konfirmasi Kata Sandi Baru", - "confirmPasswordPlaceholder": "Konfirmasi kata sandi baru", - "updatePassword": "Perbarui Kata Sandi", - "createApiKeyTitle": "Buat Kunci API", - "createApiKeyDescription": "Buat kunci API baru untuk akses terprogram.", - "apiKeyNameLabel": "Nama", - "apiKeyNamePlaceholder": "misalnya, CI Pipeline", - "expiryDateLabel": "Tanggal Kedaluwarsa", - "optional": "opsional", - "cancel": "Membatalkan", - "createKey": "Buat Kunci", - "apiKeyCount": "{{count}} kunci", - "newKey": "Kunci Baru", - "noApiKeys": "Belum ada kunci API.", - "apiKeyActive": "Aktif", - "apiKeyUsageHint": "Sertakan kunci Anda di dalam", - "apiKeyUsageHintHeader": "Judul.", - "apiKeyPermissionsHint": "Kunci mewarisi izin dari pengguna yang membuatnya.", - "roleUser": "Pengguna", - "authMethodDual": "Otorisasi Ganda", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Gagal memulai pengaturan TOTP", - "totpEnter6Digits": "Masukkan kode 6 digit", - "totpEnabledSuccess": "Autentikasi dua faktor diaktifkan", - "totpInvalidCode": "Kode tidak valid, silakan coba lagi.", - "totpDisableInputRequired": "Masukkan kode TOTP atau kata sandi Anda.", - "totpDisabledSuccess": "Autentikasi dua faktor dinonaktifkan", - "totpDisableFailed": "Gagal menonaktifkan 2FA.", - "totpDisableTitle": "Nonaktifkan otentikasi dua faktor (2FA).", - "totpDisablePlaceholder": "Masukkan kode TOTP atau kata sandi.", - "totpDisableConfirm": "Nonaktifkan otentikasi dua faktor (2FA).", - "totpContinueVerify": "Lanjutkan Verifikasi", - "totpVerifyTitle": "Verifikasi Kode", - "totpBackupTitle": "Kode Cadangan", - "totpDownloadBackup": "Unduh Kode Cadangan", - "done": "Selesai", - "secretCopied": "Rahasia disalin ke papan klip", - "apiKeyNameRequired": "Nama kunci wajib diisi.", - "apiKeyCreated": "Kunci API \"{{name}}\" berhasil dibuat", - "apiKeyCreateFailed": "Gagal membuat kunci API", - "apiKeyUser": "Pengguna", - "apiKeyExpires": "Berakhir", - "apiKeyRevoked": "Kunci API \"{{name}}\" dicabut", - "apiKeyRevokeFailed": "Gagal mencabut kunci API", - "passwordFieldsRequired": "Kata sandi lama dan baru wajib diisi.", - "passwordMismatch": "Kata sandi tidak cocok", - "passwordTooShort": "Kata sandi harus minimal 6 karakter.", - "passwordUpdated": "Kata sandi berhasil diperbarui.", - "passwordUpdateFailed": "Gagal memperbarui kata sandi", - "deletePasswordRequired": "Kata sandi diperlukan untuk menghapus akun Anda.", - "deleteFailed": "Gagal menghapus akun", - "deleting": "Menghapus...", - "colorPickerTooltip": "Buka pemilih warna", - "themeSystem": "Sistem", - "themeLight": "Lampu", - "themeDark": "Gelap", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", - "themeNord": "Utara", - "themeSolarized": "Disinari matahari", - "themeTokyoNight": "Malam Tokyo", - "themeOneDark": "Satu Kegelapan", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/it_IT.json b/src/ui/locales/translated/it_IT.json index 0456560c..9bc5af32 100644 --- a/src/ui/locales/translated/it_IT.json +++ b/src/ui/locales/translated/it_IT.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Cartelle", - "folder": "Cartella", + "folders": "Folders", + "folder": "Folder", "password": "Password", - "key": "Chiave", - "sshPrivateKey": "Chiave Privata Ssh", - "upload": "Carica", - "keyPassword": "Password Della Chiave", - "sshKey": "Chiave SSH", - "uploadPrivateKeyFile": "Carica File Chiave Privata", - "searchCredentials": "Cerca credenziali...", - "addCredential": "Aggiungi Credenziali", - "caCertificate": "Certificato CA (-cert.pub)", - "caCertificateDescription": "Opzionale: carica o incolla il file di certificato firmato CA (ad esempio id_ed25519-cert.pub). Richiesto quando il server SSH utilizza autorizzazioni basate su certificati.", - "uploadCertFile": "Carica file -cert.pub", - "clearCert": "Pulisci", - "certLoaded": "Certificato caricato", - "certPublicKeyLabel": "Certificato CA", - "certTypeLabel": "Tipo di certificato", - "pasteOrUploadCert": "Incolla o carica un certificato -cert.pub ...", - "hasCaCert": "Ha Certificato CA", - "noCaCert": "Nessun Certificato Ca" + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Impossibile caricare gli avvisi", - "failedToDismissAlert": "Impossibile annullare l'avviso" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configurazione Del Server", - "description": "Configura l'URL del server Termix per connettersi ai tuoi servizi di backend", - "serverUrl": "Url Del Server", - "enterServerUrl": "Inserisci un URL del server", - "saveFailed": "Impossibile salvare la configurazione", - "saveError": "Errore nel salvataggio della configurazione", - "saving": "Salvataggio...", - "saveConfig": "Salva Configurazione", - "helpText": "Inserisci l'URL in cui è in esecuzione il tuo server Termix (ad esempio, http://localhost:30001 o https://your-server.com)", - "changeServer": "Cambia Server", - "mustIncludeProtocol": "L'URL del server deve iniziare con http:// o ↓ ://", - "allowInvalidCertificate": "Consenti certificato non valido", - "allowInvalidCertificateDesc": "Da utilizzare esclusivamente per server self-hosted affidabili con certificati autofirmati o certificati IP.", - "useEmbedded": "Usa Server Locale", - "embeddedDesc": "Esegue Termix con il server locale integrato (nessun server remoto necessario)", - "embeddedConnecting": "Connessione al server locale...", - "embeddedNotReady": "Il server locale non è ancora pronto. Attendere un attimo e riprovare.", - "localServer": "Server Locale" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Errore Controllo Versione", - "checkFailed": "Impossibile controllare gli aggiornamenti", - "upToDate": "L'app è aggiornata", - "currentVersion": "Stai eseguendo la versione {{version}}", - "updateAvailable": "Aggiornamento Disponibile", - "newVersionAvailable": "È disponibile una nuova versione! Stai eseguendo {{current}}, ma {{latest}} è disponibile.", - "betaVersion": "Versione Beta", - "betaVersionDesc": "Stai eseguendo {{current}}, che è più recente dell'ultima versione stabile {{latest}}.", - "releasedOn": "Pubblicato il {{date}}", - "downloadUpdate": "Scarica Aggiornamento", - "checking": "Controllo aggiornamenti...", - "checkUpdates": "Controlla aggiornamenti", - "checkingUpdates": "Controllo aggiornamenti...", - "updateRequired": "Aggiornamento Richiesto" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Chiudi", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Continua", - "maintenance": "Manutenzione", + "continue": "Continue", + "maintenance": "Maintenance", "degraded": "Degraded", - "error": "Errore", - "warning": "Attenzione", - "unsavedChanges": "Modifiche non salvate", - "dismiss": "Ignora", - "loading": "Caricamento...", - "optional": "Facoltativo", - "connect": "Connetti", - "copied": "Copiato", - "connecting": "Connessione...", - "updateAvailable": "Aggiornamento Disponibile", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Apri in una nuova scheda", - "noReleases": "Nessuna Rilascio", - "updatesAndReleases": "Aggiornamenti E Rilasci", - "newVersionAvailable": "È disponibile una nuova versione ({{version}}).", - "failedToFetchUpdateInfo": "Recupero delle informazioni di aggiornamento non riuscito", - "preRelease": "Pre-rilascio", - "noReleasesFound": "Nessuna release trovata.", - "cancel": "Annulla", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", "username": "Username", - "login": "Accedi", - "register": "Registrati", + "login": "Login", + "register": "Register", "password": "Password", - "confirmPassword": "Conferma Password", - "back": "Indietro", - "save": "Salva", - "saving": "Salvataggio...", - "delete": "Elimina", - "rename": "Rinomina", - "edit": "Modifica", - "add": "Aggiungi", - "confirm": "Conferma", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", "no": "No", - "or": "O", - "next": "Successivo", - "previous": "Precedente", - "refresh": "Aggiorna", - "language": "Lingua", - "checking": "Controllo...", - "checkingDatabase": "Controllo connessione database...", - "checkingAuthentication": "Controllo autenticazione...", - "backendReconnected": "Connessione al server ripristinata", - "connectionDegraded": "Connessione al server persa, recupero…", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Rimuovi", - "create": "Crea", - "update": "Aggiorna", - "copy": "Copia", - "copyFailed": "Impossibile copiare negli appunti", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Ripristina", - "of": "di" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { "home": "Home", - "terminal": "Terminale", + "terminal": "Terminal", "docker": "Docker", - "tunnels": "Gallerie", - "fileManager": "Gestore File", - "serverStats": "Statistiche Server", - "admin": "Amministratore", - "userProfile": "Profilo Utente", - "splitScreen": "Schermo Diviso", - "confirmClose": "Chiudere questa sessione attiva?", - "close": "Chiudi", - "cancel": "Annulla", - "sshManager": "Gestore SSH", - "cannotSplitTab": "Impossibile dividere questa scheda", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copia Password", - "copySudoPassword": "Copia Password Sudo", - "passwordCopied": "Password copiata negli appunti", - "noPasswordAvailable": "Nessuna password disponibile", - "failedToCopyPassword": "Impossibile copiare la password", - "refreshTab": "Aggiorna connessione", - "openFileManager": "Apri File Manager", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", "dashboard": "Dashboard", - "networkGraph": "Grafico Di Rete", - "quickConnect": "Connessione Rapida", - "sshTools": "Strumenti SSH", - "history": "Storico", - "hosts": "Host", - "snippets": "Snippet", - "hostManager": "Gestore Host", - "credentials": "Credenziali", - "connections": "Connessioni", - "roleAdministrator": "Amministratore", - "roleUser": "Utente" + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Host", - "noHosts": "Nessun Host SSH", - "retry": "Riprova", - "refresh": "Aggiorna", - "optional": "Facoltativo", - "downloadSample": "Scarica Esempio", - "failedToDeleteHost": "Impossibile eliminare {{name}}", - "importSkipExisting": "Importazione (salta esistente)", - "connectionDetails": "Dettagli Della Connessione", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Desktop Remoto", - "port": "Porta", + "remoteDesktop": "Remote Desktop", + "port": "Port", "username": "Username", - "folder": "Cartella", - "tags": "Etichette", + "folder": "Folder", + "tags": "Tags", "pin": "Pin", - "addHost": "Aggiungi Host", - "editHost": "Modifica Host", - "cloneHost": "Clona Host", - "enableTerminal": "Abilita Terminale", - "enableTunnel": "Abilita Tunnel", - "enableFileManager": "Abilita Gestore File", - "enableDocker": "Abilita Docker", - "defaultPath": "Percorso Predefinito", - "connection": "Connessione", - "upload": "Carica", - "authentication": "Autenticazione", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", "password": "Password", - "key": "Chiave", - "credential": "Credenziali", - "none": "Nessuno", - "sshPrivateKey": "Chiave Privata Ssh", - "keyType": "Tipo Di Chiave", - "uploadFile": "Carica File", - "tabGeneral": "Generale", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Gallerie", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "File", - "tabStats": "Statistiche", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Condivisione", - "tabAuthentication": "Autenticazione", - "terminal": "Terminale", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Gestore File", - "serverStats": "Statistiche Server", - "status": "Stato", - "folderRenamed": "Cartella \"{{oldName}}\" rinominata in \"{{newName}}\" con successo", - "failedToRenameFolder": "Impossibile rinominare la cartella", - "movedToFolder": "Spostato in \"{{folder}}\"", - "editHostTooltip": "Modifica host", - "statusChecks": "Controlli Di Stato", - "metricsCollection": "Collezione Di Metriche", - "metricsInterval": "Intervallo Di Raccolta Metriche", - "metricsIntervalDesc": "Quanto spesso raccogliere statistiche del server (5s - 1h)", - "behavior": "Comportamento", - "themePreview": "Anteprima Tema", - "theme": "Tema", - "fontFamily": "Famiglia Carattere", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Spaziatura Lettera", - "lineHeight": "Altezza Linea", - "cursorStyle": "Stile Del Cursore", - "cursorBlink": "Collegamento Del Cursore", - "scrollbackBuffer": "Buffer Di Scorrimento", - "bellStyle": "Stile Campana", - "rightClickSelectsWord": "Click Destro Seleziona Parola", - "fastScrollModifier": "Modificatore Di Scorrimento Veloce", - "fastScrollSensitivity": "Sensibilità Di Scorrimento Veloce", - "sshAgentForwarding": "Inoltro Agente Ssh", - "backspaceMode": "Modalità Backspace", - "startupSnippet": "Snippet Di Avvio", - "selectSnippet": "Seleziona snippet", - "forceKeyboardInteractive": "Forza Tastiera-Interattiva", - "overrideCredentialUsername": "Ignora Il Nome Utente Credenziale", - "overrideCredentialUsernameDesc": "Usa il nome utente specificato sopra invece del nome utente delle credenziali", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", "jumpHostChain": "Jump Host Chain", - "portKnocking": "Colpo Porta", - "addKnock": "Aggiungi Porta", - "addProxyNode": "Aggiungi Nodo", - "proxyNode": "Nodo Proxy", - "proxyType": "Tipo Proxy", - "quickActions": "Azioni Rapide", - "sudoPasswordAutoFill": "Riempimento Automatico Password Sudo", - "sudoPassword": "Password Sudo", - "keepaliveInterval": "Intervallo Di Mantenimento (Ms)", - "moshCommand": "Comando MOSH", - "environmentVariables": "Variabili Di Ambiente", - "addVariable": "Aggiungi Variabile", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Copia URL Del Terminale", - "copyFileManagerUrl": "Copia URL Del Gestore File", - "copyRemoteDesktopUrl": "Copia Url Del Desktop Remoto", - "failedToConnect": "Connessione alla console non riuscita", - "connect": "Connetti", - "disconnect": "Disconnetti", - "start": "Inizia", - "enableStatusCheck": "Abilita Controllo Stato", - "enableMetrics": "Abilita Le Metriche", - "bulkUpdateFailed": "Aggiornamento di massa non riuscito", - "selectAll": "Seleziona Tutto", - "deselectAll": "Deseleziona Tutto", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Shell Sicuro", - "virtualNetwork": "Rete Virtuale", - "unencryptedShell": "Shell non cifrata", - "addressIp": "Indirizzo / IP", - "friendlyName": "Nome Amichevole", - "folderAndAdvanced": "Cartella & Avanzata", - "privateNotes": "Note Private", - "privateNotesPlaceholder": "Dettagli su questo server...", - "pinToTop": "Pin verso l'alto", - "pinToTopDesc": "Mostra sempre questo host in cima alla lista", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", "portKnockingSequence": "Port Knocking Sequence", - "addKnockBtn": "Aggiungi Knock", - "noPortKnocking": "Nessuna porta bussata configurata.", - "knockPort": "Porta Knock", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Ritardo Dopo (ms)", - "useSocks5Proxy": "Usa Proxy SOCKS5", - "useSocks5ProxyDesc": "Percorso connessione attraverso un server proxy", - "proxyHost": "Host Proxy", - "proxyPort": "Porta Proxy", - "proxyUsername": "Nome Utente Proxy", - "proxyPassword": "Password Proxy", - "proxySingleMode": "Singolo Proxy", - "proxyChainMode": "Catena Proxy", - "you": "Tu", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", "jumpHostChainLabel": "Jump Host Chain", - "addJumpBtn": "Aggiungi Salto", - "noJumpHosts": "Nessun host jump configurato.", - "selectAServer": "Seleziona un server...", - "sshPort": "Porta SSH", - "authMethod": "Metodo Autenticazione", - "storedCredential": "Credenziali Archiviate", - "selectACredential": "Seleziona una credenziale...", - "keyTypeLabel": "Tipo Di Chiave", - "keyTypeAuto": "Rilevamento Automatico", - "keyPasteTab": "Incolla", - "keyUploadTab": "Carica", - "keyFileLoaded": "File chiave caricato", - "keyUploadClick": "Clicca per caricare .pem / .key / .ppk", - "clearKey": "Pulisci chiave", - "keySaved": "Chiave SSH salvata", - "keyReplaceNotice": "incolla una nuova chiave qui sotto per sostituirla", - "keyPassphraseSaved": "Passphrase salvata, digita per cambiare", - "replaceKey": "Sostituisci chiave", - "forceKeyboardInteractiveLabel": "Forza Tastiera Interattiva", - "forceKeyboardInteractiveShortDesc": "Forza l'inserimento manuale della password anche se le chiavi sono presenti", - "terminalAppearance": "Aspetto Del Terminale", - "colorTheme": "Tema Colore", - "fontFamilyLabel": "Famiglia Carattere", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Stile Del Cursore", - "letterSpacingPx": "Spaziatura Lettera (px)", - "lineHeightLabel": "Altezza Linea", - "bellStyleLabel": "Stile Campana", - "backspaceModeLabel": "Modalità Backspace", - "cursorBlinking": "Scollegamento Del Cursore", - "cursorBlinkingDesc": "Abilita l'animazione lampeggiante per il cursore del terminale", - "rightClickSelectsWordLabel": "Clic Destro Seleziona Parola", - "rightClickSelectsWordShortDesc": "Seleziona la parola sotto il cursore nel clic destro", - "behaviorAndAdvanced": "Comportamento E Avanzate", - "scrollbackBufferLabel": "Buffer Di Scorrimento", - "scrollbackMaxLines": "Numero massimo di righe storiche", - "sshAgentForwardingLabel": "Inoltro Agente Ssh", - "sshAgentForwardingShortDesc": "Passa le tue chiavi SSH locali a questo host", - "enableAutoMosh": "Abilita Auto-Mosh", - "enableAutoMoshDesc": "Preferisci Mosh su SSH se disponibile", - "enableAutoTmux": "Abilita Auto-Tmux", - "enableAutoTmuxDesc": "Lancia o collega automaticamente alla sessione tmux", - "sudoPasswordAutoFillLabel": "Riempimento Automatico Password Sudo", - "sudoPasswordAutoFillShortDesc": "Fornisci automaticamente la password di sudo quando richiesto", - "sudoPasswordLabel": "Password Sudo", - "environmentVariablesLabel": "Variabili Di Ambiente", - "addVariableBtn": "Aggiungi Variabile", - "noEnvVars": "Nessuna variabile d' ambiente configurata.", - "fastScrollModifierLabel": "Modificatore Di Scorrimento Veloce", - "fastScrollSensitivityLabel": "Sensibilità Di Scorrimento Veloce", - "moshCommandLabel": "Comando Mosh", - "startupSnippetLabel": "Snippet Di Avvio", - "keepaliveIntervalLabel": "Intervallo di keep-alive (secondi)", - "maxKeepaliveMisses": "Max Misses Keepalive", - "tunnelSettings": "Impostazioni Tunnel", - "enableTunneling": "Abilita Tunneling", - "enableTunnelingDesc": "Abilita la funzionalità del tunnel SSH per questo host", - "serverTunnelsSection": "Tunnel Del Server", - "addTunnelBtn": "Aggiungi Tunnel", - "noTunnelsConfigured": "Nessun tunnel configurato.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Tipo Di Tunnel", - "tunnelModeLocalDesc": "Inoltra una porta locale a una porta sul server remoto (o un host da essa raggiungibile).", - "tunnelModeRemoteDesc": "Inoltra una porta sul server remoto a una porta locale sul tuo computer.", - "tunnelModeDynamicDesc": "Crea un proxy SOCKS5 su una porta locale per l'inoltro dinamico delle porte.", - "sameHost": "Questo host (tunnel diretto)", - "endpointHost": "Host Endpoint", - "endpointPort": "Porta Endpoint", - "bindHost": "Associa Host", - "sourcePort": "Porta Sorgente", - "maxRetries": "Riprova Massima", - "retryIntervalS": "Riprova Intervallo (s)", - "autoStartLabel": "Avvio Automatico", - "autoStartDesc": "Collega automaticamente questo tunnel quando l'host è caricato", - "tunnelConnecting": "Collegamento tunnel...", - "tunnelDisconnected": "Tunnel disconnesso", - "failedToConnectTunnel": "Impossibile connettersi", - "failedToDisconnectTunnel": "Disconnessione non riuscita", - "dockerIntegration": "Integrazione Docker", - "enableDockerMonitor": "Abilita Docker", - "enableDockerMonitorDesc": "Monitora e gestisci i contenitori su questo host tramite Docker", - "enableFileManagerMonitor": "Abilita Gestore File", - "enableFileManagerMonitorDesc": "Esplora e gestisce i file su questo host tramite SFTP", - "defaultPathLabel": "Percorso Predefinito", - "fileManagerPathHint": "La directory da aprire al lancio del file manager per questo host.", - "statusChecksLabel": "Controlli Di Stato", - "enableStatusChecks": "Abilita Controllo Stato", - "enableStatusChecksDesc": "Ping periodicamente questo host per verificare la disponibilità", - "useGlobalInterval": "Usa Intervallo Globale", - "useGlobalIntervalDesc": "Sovrascrivi con l'intervallo di controllo dello stato a livello server", - "checkIntervalS": "Verifica Intervallo (s)", - "checkIntervalDesc": "Secondi tra ogni ping di connettività", - "metricsCollectionLabel": "Collezione Di Metriche", - "enableMetricsLabel": "Abilita Le Metriche", - "enableMetricsDesc": "Raccogli l'utilizzo di CPU, RAM, disco e rete da questo host", - "useGlobalMetrics": "Usa Intervallo Globale", - "useGlobalMetricsDesc": "Sovrascrivi con l'intervallo di metriche a livello di server", - "metricsIntervalS": "Intervallo Di Metriche", - "metricsIntervalDesc2": "Secondi tra istantanee metriche", - "visibleWidgets": "Widget Visibili", - "cpuUsageLabel": "Utilizzo CPU", - "cpuUsageDesc": "CPU percentuale, media di carico, grafico linea di scintilla", - "memoryLabel": "Utilizzo Memoria", - "memoryDesc": "Utilizzo RAM, swap, memorizzato in cache", - "storageLabel": "Uso Del Disco", - "storageDesc": "Utilizzo disco per punto di montaggio", - "networkLabel": "Interfacce Di Rete", - "networkDesc": "Elenco delle interfacce e larghezza di banda", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", "uptimeLabel": "Uptime", - "uptimeDesc": "Tempo di funzionamento e avvio del sistema", - "systemInfoLabel": "Informazioni Di Sistema", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", "systemInfoDesc": "OS, kernel, hostname, architecture", - "recentLoginsLabel": "Login Recenti", - "recentLoginsDesc": "Login riuscito e fallito", - "topProcessesLabel": "Migliori Processi", - "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Porte Di Ascoltamento", - "listeningPortsDesc": "Apri porte con processo e stato", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, stato SELinux", - "quickActionsLabel": "Azioni Rapide", - "quickActionsToolbar": "Le azioni rapide appaiono come pulsanti nella barra degli strumenti Statistiche server per l'esecuzione di un comando con un clic.", - "noQuickActions": "Ancora nessuna azione rapida.", - "buttonLabel": "Etichetta pulsante", - "selectSnippetPlaceholder": "Seleziona snippet...", - "addActionBtn": "Aggiungi Azione", - "hostSharedSuccessfully": "Host condiviso con successo", - "failedToShareHost": "Impossibile condividere l'host", - "accessRevoked": "Accesso revocato", - "failedToRevokeAccess": "Impossibile revocare l'accesso", - "cancelBtn": "Annulla", - "savingBtn": "Salvataggio...", - "addHostBtn": "Aggiungi Host", - "hostUpdated": "Host aggiornato", - "hostCreated": "Host creato", - "failedToSave": "Impossibile salvare l'host", - "credentialUpdated": "Credenziali aggiornate", - "credentialCreated": "Credenziali create", - "failedToSaveCredential": "Impossibile salvare le credenziali", - "backToHosts": "Torna agli host", - "backToCredentials": "Torna alle credenziali", - "pinned": "Bloccato", - "noHostsFound": "Nessun host trovato", - "tryDifferentTerm": "Prova un termine diverso", - "addFirstHost": "Aggiungi il tuo primo host per iniziare", - "noCredentialsFound": "Nessuna credenziale trovata", - "addCredentialBtn": "Aggiungi Credenziali", - "updateCredentialBtn": "Aggiorna Credenziali", - "features": "Caratteristiche", - "noFolder": "(Nessun cartella)", - "deleteSelected": "Elimina", - "exitSelection": "Esci dalla selezione", - "importSkip": "Importazione (salta esistente)", - "importOverwrite": "Importa (sovrascrive)", - "collapseBtn": "Comprimi", - "importExportBtn": "Importa / Esporta", - "hostStatusesRefreshed": "Stato host aggiornato", - "failedToRefreshHosts": "Impossibile aggiornare gli host", - "movedHostTo": "Spostato {{host}} in \"{{folder}}\"", - "failedToMoveHost": "Impossibile spostare l'host", - "folderRenamedTo": "Cartella rinominata in \"{{name}}\"", - "deletedFolder": "Cartella eliminata \"{{name}}\"", - "failedToDeleteFolder": "Impossibile eliminare la cartella", - "deleteAllInFolder": "Eliminare tutti gli host in \"{{name}}\"? Questo non può essere annullato.", - "deletedHost": "Eliminato {{name}}", - "copiedToClipboard": "Copiato negli appunti", - "terminalUrlCopied": "URL del terminale copiato", - "fileManagerUrlCopied": "URL del gestore file copiato", - "tunnelUrlCopied": "URL del tunnel copiato", - "dockerUrlCopied": "URL Docker copiato", - "serverStatsUrlCopied": "Url delle Statistiche del Server copiato", - "rdpUrlCopied": "URL RDP copiato", - "vncUrlCopied": "URL VNC copiato", - "telnetUrlCopied": "URL Telnet copiato", - "remoteDesktopUrlCopied": "URL Desktop remoto copiato", - "expandActions": "Espandi le azioni", - "collapseActions": "Comprimi le azioni", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Pacchetto magico inviato a {{name}}", - "wakeOnLanError": "Invio del pacchetto magico non riuscito", - "cloneHostAction": "Clona Host", - "copyAddress": "Copia Indirizzo", - "copyLink": "Copia Collegamento", - "copyTerminalUrlAction": "Copia URL Del Terminale", - "copyFileManagerUrlAction": "Copia URL Del Gestore File", - "copyTunnelUrlAction": "Copia URL Tunnel", - "copyDockerUrlAction": "Copia URL Docker", - "copyServerStatsUrlAction": "Copia Url Statistiche Server", - "copyRdpUrlAction": "Copia URL RDP", - "copyVncUrlAction": "Copia URL VNC", - "copyTelnetUrlAction": "Copia L'Url Telnet", - "copyRemoteDesktopUrlAction": "Copia Url Del Desktop Remoto", - "deleteCredentialConfirm": "Eliminare le credenziali \"{{name}}\"?", - "deletedCredential": "Eliminato {{name}}", - "deploySSHKeyTitle": "Dispiega Chiave SSH", - "deployingBtn": "Distribuzione...", - "deployBtn": "Dispiega", - "failedToDeployKey": "Impossibile implementare la chiave", - "deleteHostsConfirm": "Eliminare {{count}} host{{plural}}? Questo non può essere annullato.", - "movedToRoot": "Spostato nella root", - "failedToMoveHosts": "Impossibile spostare gli host", - "enableTerminalFeature": "Abilita Terminale", - "disableTerminalFeature": "Disabilita Terminale", - "enableFilesFeature": "Abilita File", - "disableFilesFeature": "Disabilita File", - "enableTunnelsFeature": "Abilita Gallerie", - "disableTunnelsFeature": "Disabilita Tunnel", - "enableDockerFeature": "Abilita Docker", - "disableDockerFeature": "Disabilita Docker", - "addTagsPlaceholder": "Aggiungi tag...", - "authDetails": "Dettagli Di Autenticazione", - "credType": "Tipo", - "generateKeyPairDesc": "Genera una nuova coppia di chiavi, sia le chiavi private che quelle pubbliche verranno riempite automaticamente.", - "generatingKey": "Generazione...", - "generateLabel": "Genera {{label}}", - "uploadFileBtn": "Carica file", - "keyPassphraseOptional": "Passphrase Chiave (Opzionale)", - "sshPublicKeyOptional": "Chiave Pubblica Ssh (Opzionale)", - "publicKeyGenerated": "Chiave pubblica generata", - "failedToGeneratePublicKey": "Impossibile ricavare la chiave pubblica", - "publicKeyCopied": "Chiave pubblica copiata", - "keyPairGenerated": "{{label}} coppia di chiavi generata", - "failedToGenerateKeyPair": "Impossibile generare la coppia di chiavi", - "searchHostsPlaceholder": "Cerca host, indirizzi, tag…", - "searchCredentialsPlaceholder": "Cerca credenziali…", - "refreshBtn": "Aggiorna", - "addTag": "Aggiungi tag...", - "deleteConfirmBtn": "Elimina", - "tunnelRequirementsText": "Il server SSH deve avere GatewayPorts sì, AllowTcpInoltro sì, e PermitRootLogin sì impostato in /etc/ssh/sshd_config.", - "deleteHostConfirm": "Eliminare \"{{name}}\"?", - "enableAtLeastOneProtocol": "Abilita almeno un protocollo sopra per configurare le impostazioni di autenticazione e connessione.", - "keyPassphrase": "Passphrase Chiave", - "connectBtn": "Connetti", - "disconnectBtn": "Disconnetti", - "basicInformation": "Informazioni Di Base", - "authDetailsSection": "Dettagli Di Autenticazione", - "credTypeLabel": "Tipo", - "hostsTab": "Host", - "credentialsTab": "Credenziali", - "selectMultiple": "Seleziona più", - "selectHosts": "Seleziona host", - "connectionLabel": "Connessione", - "authenticationLabel": "Autenticazione", - "generateKeyPairTitle": "Genera Coppia Chiavi", - "generateKeyPairDescription": "Genera una nuova coppia di chiavi, sia le chiavi private che quelle pubbliche verranno riempite automaticamente.", - "generateFromPrivateKey": "Genera da chiave privata", - "refreshBtn2": "Aggiorna", - "exitSelectionTitle": "Esci dalla selezione", - "exportAll": "Esporta Tutto", - "addHostBtn2": "Aggiungi Host", - "addCredentialBtn2": "Aggiungi Credenziali", - "checkingHostStatuses": "Controllo stato host...", - "pinnedSection": "Bloccato", - "hostsExported": "Host esportati con successo", - "exportFailed": "Impossibile esportare gli host", - "sampleDownloaded": "File di esempio scaricato", - "failedToDeleteCredential2": "Impossibile eliminare le credenziali", - "noFolderOption": "(Nessun cartella)", - "nSelected": "{{count}} selezionato", - "featuresMenu": "Caratteristiche", - "moveMenu": "Sposta", - "cancelSelection": "Annulla", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", - "targetHostLabel": "Host Destinazione", - "selectHostOption": "Seleziona un host...", - "keyDeployedSuccess": "Chiave distribuita con successo", - "failedToDeployKey2": "Impossibile implementare la chiave", - "deletedCount": "Host eliminati {{count}}", - "failedToDeleteCount": "Impossibile eliminare gli host {{count}}", - "duplicatedHost": "Duplicato \"{{name}}\"", - "failedToDuplicateHost": "Impossibile duplicare l'host", - "updatedCount": "Host {{count}} aggiornati", - "friendlyNameLabel": "Nome Amichevole", - "descriptionLabel": "Descrizione", - "loadingHost": "Caricamento host...", - "loadingHosts": "Caricamento host...", - "loadingCredentials": "Caricamento credenziali...", - "noHostsYet": "Ancora nessun host", - "noHostsMatchSearch": "Nessun host corrisponde alla tua ricerca", - "hostNotFound": "Host non trovato", - "searchHosts": "Cerca host...", - "sortHosts": "Ordina gli host", - "sortDefault": "Ordine predefinito", - "sortNameAsc": "Nome (A → Z)", - "sortNameDesc": "Nome (Z → A)", - "sortIpAsc": "Indirizzo IP (ascendente)", - "sortIpDesc": "Indirizzo IP (Descrizione)", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", "sortOnlineFirst": "Online First", - "sortOfflineFirst": "Prima offline", - "sortPinnedFirst": "Fissato per primo", - "filterHosts": "Host di filtro", - "filterClearAll": "Filtri trasparenti", - "filterStatusGroup": "Stato", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fissato", - "filterAuthGroup": "Tipo di autorizzazione", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", "filterAuthPassword": "Password", - "filterAuthKey": "Chiave SSH", - "filterAuthCredential": "Credenziali", - "filterAuthNone": "Nessuno", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protocollo", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Internet", - "filterFeaturesGroup": "Caratteristiche", - "filterFeatureTerminal": "terminale", - "filterFeatureFileManager": "Gestore file", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etichette", - "shareHost": "Condividi Host", - "shareHostTitle": "Condividi: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Questo host deve usare una credenziale per abilitare la condivisione. Modificare l'host e assegnare prima una credenziale." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Connessione", - "authentication": "Autenticazione", - "connectionSettings": "Impostazioni Di Connessione", - "displaySettings": "Impostazioni Di Visualizzazione", - "audioSettings": "Impostazioni Audio", - "rdpPerformance": "Prestazioni RDP", - "deviceRedirection": "Reindirizzamento Dispositivo", - "session": "Sessione", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Appunti", - "sessionRecording": "Registrazione Sessione", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Impostazioni VNC", - "terminalSettings": "Impostazioni Del Terminale", - "rdpPort": "Porta RDP", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", "username": "Username", "password": "Password", - "domain": "Dominio", - "securityMode": "Modalità Di Sicurezza", - "colorDepth": "Profondità Colore", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Altezza", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Metodo Di Ridimensiona", - "clientName": "Nome Client", - "initialProgram": "Programma Iniziale", - "serverLayout": "Layout Del Server", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Porta Del Gateway", - "gatewayUsername": "Nome Utente Gateway", - "gatewayPassword": "Password Del Gateway", - "gatewayDomain": "Dominio Gateway", - "remoteAppProgram": "Programma RemoteApp", - "workingDirectory": "Directory Di Lavoro", - "arguments": "Argomenti", - "normalizeLineEndings": "Normalizza Riga Endings", - "recordingPath": "Percorso Di Registrazione", - "recordingName": "Nome Registrazione", - "macAddress": "Indirizzo MAC", - "broadcastAddress": "Indirizzo Di Trasmissione", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Tempo Di Attesa (s)", - "driveName": "Nome Unità", - "drivePath": "Percorso Unità", - "ignoreCertificate": "Ignora Certificato", - "ignoreCertificateDesc": "Consenti connessioni agli host con certificati autofirmati", - "forceLossless": "Forza Senza Perdita", - "forceLosslessDesc": "Forza la codifica delle immagini senza perdita (qualità superiore, larghezza di banda)", - "disableAudio": "Disabilita Audio", - "disableAudioDesc": "Silenzia tutto l'audio dalla sessione remota", - "enableAudioInput": "Abilita Ingresso Audio (Microfono)", - "enableAudioInputDesc": "Inoltra il microfono locale alla sessione remota", - "wallpaper": "Sfondo", - "wallpaperDesc": "Mostra sfondo desktop (disabilitando migliora le prestazioni)", - "theming": "Temi", - "themingDesc": "Abilita temi e stili visivi", - "fontSmoothing": "Sfumatura Carattere", - "fontSmoothingDesc": "Abilita il rendering dei caratteri ClearType", - "fullWindowDrag": "Trascina A Finestra Completa", - "fullWindowDragDesc": "Mostra i contenuti delle finestre durante il trascinamento", - "desktopComposition": "Composizione Desktop", - "desktopCompositionDesc": "Abilita effetti vetro Aero", - "menuAnimations": "Animazioni Menu", - "menuAnimationsDesc": "Abilita dissolvenza menu e animazioni delle diapositive", - "disableBitmapCaching": "Disabilita Bitmap Caching", - "disableBitmapCachingDesc": "Disattiva la cache bitmap (può aiutare con i difetti)", - "disableOffscreenCaching": "Disabilita Cache Fuori Schermo", - "disableOffscreenCachingDesc": "Disattiva cache fuori schermo", - "disableGlyphCaching": "Disabilita Glyph Caching", - "disableGlyphCachingDesc": "Disattiva la cache dei glifi", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Usa pipeline grafica RemoteFX", - "enablePrinting": "Abilita Stampa", - "enablePrintingDesc": "Reindirizza le stampanti locali alla sessione remota", - "enableDriveRedirection": "Abilita Reindirizzamento Unità", - "enableDriveRedirectionDesc": "Mappa una cartella locale come unità nella sessione remota", - "createDrivePath": "Crea Percorso Drive", - "createDrivePathDesc": "Crea automaticamente la cartella se non esiste", - "disableDownload": "Disabilita Download", - "disableDownloadDesc": "Impedisci di scaricare file dalla sessione remota", - "disableUpload": "Disabilita Caricamento", - "disableUploadDesc": "Impedisci di caricare file nella sessione remota", - "enableTouch": "Abilita Touch", - "enableTouchDesc": "Abilita l'inoltro touch input", - "consoleSession": "Sessione Console", - "consoleSessionDesc": "Connetti alla console (sessione 0) invece di una nuova sessione", - "sendWolPacket": "Invia Pacchetto WOL", - "sendWolPacketDesc": "Invia un pacchetto magico per risvegliare questo host prima di connettersi", - "disableCopy": "Disabilita Copia", - "disableCopyDesc": "Impedisci la copia del testo dalla sessione remota", - "disablePaste": "Disabilita Incolla", - "disablePasteDesc": "Impedisci di incollare il testo nella sessione remota", - "createPathIfMissing": "Crea tracciato se mancante", - "createPathIfMissingDesc": "Crea automaticamente la directory di registrazione", - "excludeOutput": "Escludi Output", - "excludeOutputDesc": "Non registrare l'output dello schermo (solo metadati)", - "excludeMouse": "Escludi Il Mouse", - "excludeMouseDesc": "Non registrare i movimenti del mouse", - "includeKeystrokes": "Includi Tasti", - "includeKeystrokesDesc": "Registra le sequenze di tasti grezze oltre all'uscita dello schermo", - "vncPort": "Porta VNC", - "vncPassword": "Password VNC", - "vncUsernameOptional": "Nome Utente (Opzionale)", - "vncLeaveBlank": "Lascia vuoto se non richiesto", - "cursorMode": "Modalità Cursore", - "swapRedBlue": "Scambia Rosso/Blu", - "swapRedBlueDesc": "Scambia i canali di colore rosso e blu (risolve alcuni problemi di colore)", - "readOnly": "Sola Lettura", - "readOnlyDesc": "Visualizza lo schermo remoto senza inviare alcun input", - "telnetPort": "Porta Telnet", - "terminalType": "Tipo Di Terminale", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Schema Colore", - "backspaceKey": "Tasto Backspace", - "saveHostFirst": "Salva prima l'host.", - "sharingOptionsAfterSave": "Le opzioni di condivisione sono disponibili dopo che l'host è stato salvato.", - "sharingLoadError": "Impossibile caricare la condivisione dei dati. Controlla la tua connessione e riprova.", - "shareHostSection": "Condividi Host", - "shareWithUser": "Condividi con l'utente", - "shareWithRole": "Condividi con il ruolo", - "selectUser": "Seleziona Utente", - "selectRole": "Seleziona Ruolo", - "selectUserOption": "Seleziona un utente...", - "selectRoleOption": "Seleziona un ruolo...", - "permissionLevel": "Livello Di Autorizzazione", - "expiresInHours": "Scade in (ore)", - "noExpiryPlaceholder": "Lasciare vuoto per nessuna scadenza", - "shareBtn": "Condividi", - "currentAccess": "Accesso Corrente", - "typeHeader": "Tipo", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Permesso", - "grantedByHeader": "Concesso Da", - "expiresHeader": "Scade", - "noAccessEntries": "Ancora nessun accesso.", - "expiredLabel": "Scaduto", - "neverLabel": "Mai", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Annulla", - "savingBtn": "Salvataggio...", - "updateHostBtn": "Aggiorna Host", - "addHostBtn": "Aggiungi Host" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Cerca host, comandi o impostazioni...", - "quickActions": "Azioni Rapide", - "hostManager": "Gestore Host", - "hostManagerDesc": "Gestire, aggiungere o modificare host", - "addNewHost": "Aggiungi Nuovo Host", - "addNewHostDesc": "Registra un nuovo host", - "adminSettings": "Impostazioni Amministratore", - "adminSettingsDesc": "Configura le preferenze di sistema e gli utenti", - "userProfile": "Profilo Utente", - "userProfileDesc": "Gestisci il tuo account e le preferenze", - "addCredential": "Aggiungi Credenziali", - "addCredentialDesc": "Memorizza le chiavi o le password SSH", - "recentActivity": "Attività Recenti", - "serversAndHosts": "Server E Host", - "noHostsFound": "Nessun host trovato corrispondente \"{{search}}\"", - "links": "Collegamenti", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Seleziona", - "toggleWith": "Commuta con" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Nessuna scheda assegnata", - "focusedPane": "Riquadro attivo" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Nessuna connessione", - "noConnectionsDesc": "Apri un terminale, un gestore di file o un desktop remoto per visualizzare le connessioni qui", - "connectedFor": "Connesso per {{duration}}", - "connected": "Collegato", - "disconnected": "Disconnesso", - "closeTab": "Chiudi la scheda", - "closeConnection": "Stretto legame", - "forgetTab": "Dimenticare", - "removeBackground": "Rimuovere", - "reconnect": "Ricollega", - "reopenTab": "Riaprire", - "sectionOpen": "Aprire", - "sectionBackground": "Sfondo", - "backgroundDesc": "Le sessioni rimangono attive per 30 minuti dopo la disconnessione e possono essere ristabilita.", - "persisted": "Persisteva in sottofondo", - "expiresIn": "Scade tra {{duration}}", - "search": "Cerca connessioni...", - "noSearchResults": "Nessun risultato corrispondente alla ricerca" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Connessione alla sessione {{type}}...", - "connectionError": "Errore di connessione", - "connectionFailed": "Connessione fallita", - "failedToConnect": "Recupero del token di connessione non riuscito", - "hostNotFound": "Host non trovato", - "noHostSelected": "Nessun host selezionato", - "reconnect": "Riconnetti", - "retry": "Riprova", - "guacdUnavailable": "Il servizio desktop remoto (guacd) non è disponibile. Assicurarsi che guacd sia in esecuzione e accessibile e configurato correttamente nelle impostazioni di amministrazione.", - "ctrlAltDel": "Ctrl+Alt+Canc", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl+Alt+Canc", - "winL": "Win+L (Blocca Schermo)", - "winKey": "Chiave Windows", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Maiusc", - "win": "Vinci", - "stickyActive": "{{key}} (latched - click per rilasciare)", - "stickyInactive": "{{key}} (clicca per agganciare)", - "esc": "Fuga", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", "home": "Home", - "end": "Fine", - "pageUp": "Pagina Su", - "pageDown": "Pagina Giù", - "arrowUp": "Freccia Su", - "arrowDown": "Freccia Giù", - "arrowLeft": "Freccia A Sinistra", - "arrowRight": "Freccia A Destra", - "fnToggle": "Tasti Funzione", - "reconnect": "Riconnetti Sessione", - "collapse": "Comprimi barra degli strumenti", - "expand": "Espandi barra strumenti", - "dragHandle": "Trascina per riposizionare" + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Connetti a Host", - "clear": "Pulisci", - "paste": "Incolla", - "reconnect": "Riconnetti", - "connectionLost": "Connessione persa", - "connected": "Connesso", - "clipboardWriteFailed": "Impossibile copiare negli appunti. Assicurarsi che la pagina sia servita su HTTPS o localhost.", - "clipboardReadFailed": "Lettura dagli appunti non riuscita. Assicurarsi che i permessi degli appunti siano concessi.", - "clipboardHttpWarning": "Incolla richiede HTTPS. Usa Ctrl+Shift+V o serve Termix su HTTPS.", - "unknownError": "Errore sconosciuto", - "websocketError": "Errore di connessione WebSocket", - "connecting": "Connessione...", - "noHostSelected": "Nessun host selezionato", - "reconnecting": "Riconnessione... ({{attempt}}/{{max}})", - "reconnected": "Riconnesso con successo", - "tmuxSessionCreated": "sessione tmux creata: {{name}}", - "tmuxSessionAttached": "Sessione tmux collegata: {{name}}", - "tmuxUnavailable": "tmux non è installato sull'host remoto, tornando alla shell standard", - "tmuxSessionPickerTitle": "tmux Sessioni", - "tmuxSessionPickerDesc": "Sessioni tmux esistenti trovate su questo host. Selezionane una per ricollegare o creare una nuova sessione.", - "tmuxWindows": "Finestre", - "tmuxWindowCount": "Finestra {{count}}", - "tmuxAttached": "Client allegati", - "tmuxAttachedCount": "{{count}} allegato", - "tmuxLastActivity": "Ultima attività", - "tmuxTimeJustNow": "proprio ora", - "tmuxTimeMinutes": "{{count}}m fa", - "tmuxTimeHours": "{{count}}h fa", - "tmuxTimeDays": "{{count}}fa", - "tmuxCreateNew": "Avvia nuova sessione", - "tmuxCopyHint": "Regola la selezione e premi Invio per copiare negli appunti", - "tmuxDetach": "Staccare dalla sessione tmux", - "tmuxDetached": "Separato dalla sessione tmux", - "maxReconnectAttemptsReached": "Massimi tentativi di riconnessione raggiunti", - "closeTab": "Chiudi", - "connectionTimeout": "Timeout connessione", - "terminalTitle": "Terminale - {{host}}", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Esecuzione {{command}} - {{host}}", - "totpRequired": "Autenticazione A Due Fattori Richiesto", - "totpCodeLabel": "Codice Di Verifica", - "totpVerify": "Verifica", - "warpgateAuthRequired": "Richiesta Autenticazione Warpgate", - "warpgateSecurityKey": "Chiave Di Sicurezza", - "warpgateAuthUrl": "Url Di Autenticazione", - "warpgateOpenBrowser": "Apri nel browser", - "warpgateContinue": "Ho Completato L'Autenticazione", - "opksshAuthRequired": "Richiesta Autenticazione OPKSSH", - "opksshAuthDescription": "Completa l'autenticazione nel tuo browser per continuare. Questa sessione rimarrà valida per 24 ore.", - "opksshOpenBrowser": "Apri Browser per Autenticare", - "opksshWaitingForAuth": "In attesa di autenticazione nel browser...", - "opksshAuthenticating": "Elaborazione autenticazione...", - "opksshTimeout": "Autenticazione scaduta. Riprova.", - "opksshAuthFailed": "Autenticazione non riuscita. Controlla le tue credenziali e riprova.", - "opksshSignInWith": "Accedi con {{provider}}", - "sudoPasswordPopupTitle": "Inserire Password?", - "websocketAbnormalClose": "Connessione chiusa inaspettatamente. Questo potrebbe essere dovuto a un problema di configurazione SSL o proxy inverso. Controllare i log del server.", - "connectionLogTitle": "Registro Di Connessione", - "connectionLogCopy": "Copia i log negli appunti", - "connectionLogEmpty": "Ancora nessun log di connessione", - "connectionLogWaiting": "In attesa dei log di connessione...", - "connectionLogCopied": "Log di connessione copiati negli appunti", - "connectionLogCopyFailed": "Impossibile copiare i log negli appunti", - "connectionRejected": "Connessione rifiutata dal server. Controlla la tua autenticazione e configurazione di rete.", - "hostKeyRejected": "Verifica della chiave host SSH rifiutata. Connessione annullata.", - "sessionTakenOver": "La sessione è stata aperta in un'altra scheda. Riconnessione..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nessun host selezionato", - "initializingEditor": "Inizializzazione dell'editor...", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", "file": "File", - "folder": "Cartella", - "uploadFile": "Carica File", - "downloadFile": "Scarica", - "extractArchive": "Estrai Archivio", - "extractingArchive": "Estrazione {{name}}...", - "archiveExtractedSuccessfully": "{{name}} estratto con successo", - "extractFailed": "Estrazione fallita", - "compressFile": "Comprimi File", - "compressFiles": "Comprimi File", - "compressFilesDesc": "Comprimi gli elementi {{count}} in un archivio", - "archiveName": "Nome Archivio", - "enterArchiveName": "Inserisci il nome dell'archivio...", - "compressionFormat": "Formato Compressione", - "selectedFiles": "File selezionati", - "andMoreFiles": "e {{count}} altro...", - "compress": "Comprimi", - "compressingFiles": "Compressione {{count}} elementi in {{name}}...", - "filesCompressedSuccessfully": "{{name}} creato con successo", - "compressFailed": "Compressione fallita", - "edit": "Modifica", - "preview": "Anteprima", - "previous": "Precedente", - "next": "Successivo", - "pageXOfY": "Pagina {{current}} di {{total}}", - "zoomOut": "Zoom Indietro", - "zoomIn": "Zoom Avanti", - "newFile": "Nuovo File", - "newFolder": "Nuova Cartella", - "rename": "Rinomina", - "uploading": "Caricamento...", - "uploadingFile": "Caricamento {{name}}...", - "fileName": "Nome File", - "folderName": "Nome Cartella", - "fileUploadedSuccessfully": "File \"{{name}}\" caricato con successo", - "failedToUploadFile": "Impossibile caricare il file", - "fileDownloadedSuccessfully": "File \"{{name}}\" scaricato con successo", - "failedToDownloadFile": "Impossibile scaricare il file", - "fileCreatedSuccessfully": "File \"{{name}}\" creato con successo", - "folderCreatedSuccessfully": "Cartella \"{{name}}\" creata con successo", - "failedToCreateItem": "Creazione dell'elemento non riuscita", - "operationFailed": "Operazione {{operation}} fallita per {{name}}: {{error}}", - "failedToResolveSymlink": "Risoluzione del collegamento simbolico non riuscita", - "itemsDeletedSuccessfully": "{{count}} elementi eliminati con successo", - "failedToDeleteItems": "Impossibile eliminare gli elementi", - "sudoPasswordRequired": "Richiesta Password Amministratore", - "enterSudoPassword": "Inserisci la password di sudo per continuare questa operazione", - "sudoPassword": "Password di Sudo", - "sudoOperationFailed": "Operazione Sudo fallita", - "sudoAuthFailed": "Autenticazione Sudo non riuscita", - "dragFilesToUpload": "Trascina i file qui per caricare", - "emptyFolder": "Questa cartella è vuota", - "searchFiles": "Cerca file...", - "upload": "Carica", - "selectHostToStart": "Seleziona un host per avviare la gestione dei file", - "sshRequiredForFileManager": "Il gestore file richiede SSH. Questo host non ha SSH abilitato.", - "failedToConnect": "Connessione a SSH non riuscita", - "failedToLoadDirectory": "Impossibile caricare la directory", - "noSSHConnection": "Nessuna connessione SSH disponibile", - "copy": "Copia", - "cut": "Taglia", - "paste": "Incolla", - "copyPath": "Copia Percorso", - "copyPaths": "Copia Tracciati", - "delete": "Elimina", - "properties": "Proprietà", - "refresh": "Aggiorna", - "downloadFiles": "Scarica i file {{count}} nel browser", - "copyFiles": "Copia {{count}} elementi", - "cutFiles": "Taglia {{count}} elementi", - "deleteFiles": "Elimina {{count}} elementi", - "filesCopiedToClipboard": "{{count}} elementi copiati negli appunti", - "filesCutToClipboard": "{{count}} elementi tagliati negli appunti", - "pathCopiedToClipboard": "Percorso copiato negli appunti", - "pathsCopiedToClipboard": "{{count}} percorsi copiati negli appunti", - "failedToCopyPath": "Impossibile copiare il percorso negli appunti", - "movedItems": "Spostati elementi {{count}}", - "failedToDeleteItem": "Impossibile eliminare l'elemento", - "itemRenamedSuccessfully": "{{type}} rinominato con successo", - "failedToRenameItem": "Impossibile rinominare l'elemento", - "download": "Scarica", - "permissions": "Permessi", - "size": "Dimensione", - "modified": "Modificato", - "path": "Percorso", - "confirmDelete": "Sei sicuro di voler eliminare {{name}}?", - "permissionDenied": "Permesso negato", - "serverError": "Errore Del Server", - "fileSavedSuccessfully": "File salvato con successo", - "failedToSaveFile": "Impossibile salvare il file", - "confirmDeleteSingleItem": "Sei sicuro di voler eliminare definitivamente \"{{name}}\"?", - "confirmDeleteMultipleItems": "Sei sicuro di voler eliminare definitivamente gli elementi {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Sei sicuro di voler eliminare definitivamente gli elementi {{count}} ? Questo include le cartelle e il loro contenuto.", - "confirmDeleteFolder": "Sei sicuro di voler eliminare definitivamente la cartella \"{{name}}\" e tutti i suoi contenuti?", - "permanentDeleteWarning": "Questa azione non può essere annullata. Gli elementi verranno eliminati definitivamente dal server.", - "recent": "Recenti", - "pinned": "Bloccato", - "folderShortcuts": "Scorciatoie Cartella", - "failedToReconnectSSH": "Impossibile riconnettere la sessione SSH", - "openTerminalHere": "Apri Terminale Qui", - "run": "Esegui", - "openTerminalInFolder": "Apri terminale in questa cartella", - "openTerminalInFileLocation": "Apri il terminale alla posizione del file", - "runningFile": "Esecuzione - {{file}}", - "onlyRunExecutableFiles": "Può eseguire solo file eseguibili", - "directories": "Directory", - "removedFromRecentFiles": "Rimosso \"{{name}}\" dai file recenti", - "removeFailed": "Rimozione fallita", - "unpinnedSuccessfully": "Sbloccato \"{{name}}\" con successo", - "unpinFailed": "Sblocco fallito", - "removedShortcut": "Scorciatoia rimossa \"{{name}}\"", - "removeShortcutFailed": "Rimuovi scorciatoia non riuscita", - "clearedAllRecentFiles": "Cancellati tutti i file recenti", - "clearFailed": "Pulizia fallita", - "removeFromRecentFiles": "Rimuovi dai file recenti", - "clearAllRecentFiles": "Cancella tutti i file recenti", - "unpinFile": "Sblocca file", - "removeShortcut": "Rimuovi scorciatoia", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", "pinFile": "Pin file", - "addToShortcuts": "Aggiungi alle scorciatoie", - "pasteFailed": "Incolla non riuscita", - "noUndoableActions": "Nessuna azione annullabile", - "undoCopySuccess": "Operazione di copia annullata: eliminati i file copiati {{count}}", - "undoCopyFailedDelete": "Annullamento fallito: Impossibile eliminare i file copiati", - "undoCopyFailedNoInfo": "Annullamento fallito: Impossibile trovare le informazioni del file copiato", - "undoMoveSuccess": "Operazione di spostamento non fatto: i file {{count}} spostati di nuovo alla posizione originale", - "undoMoveFailedMove": "Annullamento non riuscito: impossibile spostare indietro i file", - "undoMoveFailedNoInfo": "Annullamento fallito: Impossibile trovare le informazioni del file spostato", - "undoDeleteNotSupported": "L'operazione di eliminazione non può essere annullata: i file sono stati eliminati definitivamente dal server", - "undoTypeNotSupported": "Tipo di operazione annullata non supportato", - "undoOperationFailed": "Operazione annullamento fallita", - "unknownError": "Errore sconosciuto", - "confirm": "Conferma", - "find": "Trova...", - "replace": "Sostituisci", - "downloadInstead": "Scarica Invece", - "keyboardShortcuts": "Tasti Scorciatoie", - "searchAndReplace": "Cerca E Sostituisci", - "editing": "Modifica", - "search": "Cerca", - "findNext": "Trova Successivo", - "findPrevious": "Trova Precedente", - "save": "Salva", - "selectAll": "Seleziona Tutto", - "undo": "Annulla", - "redo": "Ripeti", - "moveLineUp": "Sposta In Alto", - "moveLineDown": "Sposta In Basso", - "toggleComment": "Attiva/Disattiva Commento", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Impossibile caricare l'immagine", - "startTyping": "Inizia a digitare...", - "unknownSize": "Dimensione sconosciuta", - "fileIsEmpty": "Il file è vuoto", - "largeFileWarning": "Avviso File Grande", - "largeFileWarningDesc": "Questo file è di dimensione {{size}} , il che potrebbe causare problemi di performance quando aperto come testo.", - "fileNotFoundAndRemoved": "File \"{{name}}\" non trovato ed è stato rimosso dai file recenti/bloccati", - "failedToLoadFile": "Impossibile caricare il file: {{error}}", - "serverErrorOccurred": "Si è verificato un errore del server. Riprova più tardi.", - "autoSaveFailed": "Salvataggio automatico fallito", - "fileAutoSaved": "File salvato automaticamente", - "moveFileFailed": "Spostamento di {{name}} non riuscito", - "moveOperationFailed": "Operazione di spostamento fallita", - "canOnlyCompareFiles": "È possibile confrontare solo due file", - "comparingFiles": "Confrontare i file: {{file1}} e {{file2}}", - "dragFailed": "Operazione di trascinamento fallita", - "filePinnedSuccessfully": "File \"{{name}}\" fissato con successo", - "pinFileFailed": "Impossibile bloccare il file", - "fileUnpinnedSuccessfully": "File \"{{name}}\" sbloccato con successo", - "unpinFileFailed": "Sblocco del file non riuscito", - "shortcutAddedSuccessfully": "Scorciatoia cartella \"{{name}}\" aggiunto con successo", - "addShortcutFailed": "Impossibile aggiungere scorciatoia", - "operationCompletedSuccessfully": "{{operation}} {{count}} oggetti con successo", - "operationCompleted": "{{operation}} {{count}} elementi", - "downloadFileSuccess": "File {{name}} scaricato con successo", - "downloadFileFailed": "Download non riuscito", - "moveTo": "Sposta in {{name}}", - "diffCompareWith": "Confronta con {{name}}", - "dragOutsideToDownload": "Trascina la finestra esterna per scaricare (file{{count}})", - "newFolderDefault": "NuovaCartella", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Elementi {{count}} spostati con successo in {{target}}", - "move": "Sposta", - "searchInFile": "Cerca nel file (Ctrl+F)", - "showKeyboardShortcuts": "Mostra scorciatoie da tastiera", - "startWritingMarkdown": "Inizia a scrivere il tuo contenuto di markdown...", - "loadingFileComparison": "Caricamento confronto file...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Confronta", - "sideBySide": "Lato laterale", - "inline": "Incorporato", - "fileComparison": "Confronto file: {{file1}} vs {{file2}}", - "fileTooLarge": "File troppo grande: {{error}}", - "sshConnectionFailed": "Connessione SSH non riuscita. Controlla la tua connessione a {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Impossibile caricare il file: {{error}}", - "connecting": "Connessione...", - "connectedSuccessfully": "Connesso con successo", - "totpVerificationFailed": "Verifica TOTP fallita", - "warpgateVerificationFailed": "Autenticazione Warpgate fallita", - "authenticationFailed": "Autenticazione non riuscita", - "verificationCodePrompt": "Codice di verifica:", - "changePermissions": "Cambia Permessi", - "currentPermissions": "Permessi Attuali", - "owner": "Proprietario", - "group": "Gruppo", - "others": "Altri", - "read": "Leggi", - "write": "Scrivi", - "execute": "Esegui", - "permissionsChangedSuccessfully": "Permessi modificati con successo", - "failedToChangePermissions": "Modifica dei permessi non riuscita", - "name": "Nome", - "sortByName": "Nome", - "sortByDate": "Data Di Modifica", - "sortBySize": "Dimensione", - "ascending": "Crescente", - "descending": "Decrescente", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", "root": "Root", - "new": "Nuovo", - "sortBy": "Ordina Per", - "items": "Oggetti", - "selected": "Selezionato", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", - "octal": "Ottale", - "storage": "Archiviazione", - "disk": "Disco", - "used": "Usato", - "of": "di", - "toggleSidebar": "Attiva/Disattiva Barra Laterale", - "cannotLoadPdf": "Impossibile caricare il PDF", - "pdfLoadError": "Si è verificato un errore durante il caricamento del file PDF.", - "loadingPdf": "Caricamento PDF...", - "loadingPage": "Caricamento pagina..." + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copia nell'host…", - "moveToHost": "Spostati sull'host…", - "copyItemsToHost": "Copia {{count}} elementi nell'host…", - "moveItemsToHost": "Sposta {{count}} elementi nell'host…", - "noHostsConnected": "Non sono disponibili altri host per la gestione dei file.", - "noHostsConnectedHint": "Aggiungi un altro host SSH con File Manager abilitato in Host Manager.", - "selectDestinationHost": "Seleziona l'host di destinazione", - "destinationPath": "Percorso di destinazione", - "recentDestinations": "Destinazioni recenti", - "collapseRecentDestinations": "Nascondi le destinazioni recenti", - "expandRecentDestinations": "Espandi le destinazioni recenti", - "browseFolders": "Esplora le cartelle di destinazione", - "browseDestination": "Sfoglia o inserisci il percorso", - "confirmCopy": "Copia", - "confirmMove": "Mossa", - "transferring": "Trasferimento…", - "compressing": "Compressione…", - "extracting": "Estrazione di…", - "transferringItems": "Trasferimento di {{current}} di {{total}} elementi…", - "transferSuccess": "Trasferimento completato", - "transferError": "Trasferimento non riuscito", - "transferPartial": "Trasferimento completato con {{count}} errori", - "transferPartialHint": "Impossibile trasferire: {{paths}}", - "itemsSummary": "{{count}} elementi", - "destMustBeDirectory": "La destinazione deve essere una directory per trasferimenti di più elementi", - "selectThisFolder": "Seleziona questa cartella", - "browsePathWillBeCreated": "Questa cartella non esiste ancora. Verrà creata all'avvio del trasferimento.", - "browsePathError": "Impossibile aprire questo percorso sull'host di destinazione.", - "goUp": "Salire", - "copyFolderToHost": "Copia la cartella sull'host…", - "moveFolderToHost": "Sposta la cartella sull'host…", - "hostReady": "Pronto", - "hostConnecting": "Connessione in corso…", - "hostDisconnected": "Non connesso", - "hostAuthRequired": "Autenticazione richiesta: apri prima Gestione file su questo host", - "hostConnectionFailed": "Connessione fallita", - "metricsTitle": "Tempi di trasferimento", - "metricsPrepare": "Prepara la destinazione: {{duration}}", - "metricsCompress": "Comprimi sulla sorgente: {{duration}}", - "metricsHopSourceRead": "Sorgente → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → destinazione (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → destinazione (locale): {{throughput}}", - "metricsTransfer": "Da un capo all'altro: {{throughput}} ({{duration}})", - "metricsExtract": "Estrai nella destinazione: {{duration}}", - "metricsSourceDelete": "Rimuovi dalla sorgente: {{duration}}", - "metricsTotal": "Totale: {{duration}}", - "progressCompressing": "Compressione sull'host sorgente…", - "progressExtracting": "Estrazione sulla destinazione…", - "progressTransferring": "Trasferimento dati…", - "progressReconnecting": "Riconnessione…", - "parallelSegmentsLabel": "Corsie di trasferimento parallele", - "parallelSegmentsOption": "{{count}} corsie", - "parallelSegmentsHint": "I file di grandi dimensioni vengono suddivisi in blocchi da 256 MB. Più corsie utilizzano connessioni separate (come l'avvio di più trasferimenti) per una maggiore velocità di trasmissione complessiva.", - "progressTotalSpeed": "{{speed}} totale ({{lanes}} corsie)", - "progressTransferringItems": "Trasferimento dei file ({{current}} di {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} file", - "sourceNotDeletedPartial": "File sorgente conservati (trasferimento parziale)", - "jumpHostLimitation": "Entrambi gli host devono essere raggiungibili dal server Termix. Il routing diretto da host a host non è supportato.", - "cancel": "Cancellare", - "methodLabel": "Metodo di trasferimento", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Archivio Tar", - "methodItemSftp": "SFTP per file", - "methodAutoHint": "Il sistema sceglie tra la compressione tar o il trasferimento SFTP per singolo file in base al numero di file, alle dimensioni e alla comprimibilità. I singoli file utilizzano sempre il trasferimento SFTP in streaming.", - "methodTarHint": "Comprimere sul file sorgente, trasferire un archivio ed estrarre il file sulla destinazione. Richiede tar su entrambi gli host Unix.", - "methodItemSftpHint": "Trasferisci ogni file singolarmente tramite SFTP. Funziona su tutti i sistemi operativi, incluso Windows.", - "methodPreviewLoading": "Calcolo del metodo di trasferimento…", - "methodPreviewError": "Impossibile visualizzare in anteprima il metodo di trasferimento. Il server sceglierà comunque un metodo all'avvio.", - "methodPreviewWillUseTar": "Verrà utilizzato: archivio Tar", - "methodPreviewWillUseItemSftp": "Verrà utilizzato: SFTP per file", - "methodPreviewScanSummary": "{{fileCount}} file, {{totalSize}} totale (scansionati sull'host sorgente).", - "methodItemSftpLimitation": "Ogni file utilizza lo stesso flusso SFTP come copia di un singolo file, uno dopo l'altro. L'avanzamento viene combinato per tutti i file, quindi la barra si muove lentamente in caso di file di grandi dimensioni.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Hai scelto SFTP per singolo file.", - "user_tar": "Hai scelto l'archivio tar.", - "tar_unavailable": "Tar non è disponibile su uno o entrambi gli host; in alternativa, verrà utilizzato il trasferimento di file tramite SFTP.", - "windows_host": "Si utilizza un sistema host Windows; non viene utilizzato il comando tar.", - "auto_multi_large": "Automatico: più file, incluso un file di grandi dimensioni ({{largestSize}}) con dati comprimibili, vengono raggruppati in un unico trasferimento.", - "auto_single_large_in_archive": "Automatico: un file di grandi dimensioni ({{largestSize}}) in questo set — SFTP per file.", - "auto_many_incompressible": "Automatico: dati perlopiù incomprimibili — SFTP per file.", - "auto_many_files": "Auto: molti file ({{fileCount}}) — tar riduce l'overhead per file.", - "auto_default": "Automatico: SFTP per file per questo set." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Cancellare", - "progressCancelling": "Annullamento…", - "progressStalled": "In stallo", - "resumedHint": "Riconnessione stabilita a un trasferimento attivo avviato in un'altra finestra.", - "transferCancelled": "Trasferimento annullato", - "transferCancelledCopyHint": "È possibile che alcuni file parziali rimangano nella destinazione.", - "transferCancelledMoveHint": "È possibile che rimangano file parziali nella destinazione. I file di origine non sono stati rimossi.", - "cleanupDestFiles": "destinazione di pulizia", - "cleanupDestFilesSuccess": "Rimossi i file parziali dalla destinazione", - "cleanupDestFilesPartial": "Alcuni file parziali non sono stati rimossi", - "cleanupDestFilesNothing": "Non c'è nulla da pulire a destinazione.", - "cleanupDestFilesError": "Pulizia fallita", - "retryTransfer": "Riprova", - "retryTransferError": "Tentativo fallito", - "transferFailedRetryHint": "Alcuni dati sono stati conservati nella destinazione. Il tentativo di connessione riprenderà non appena la connessione sarà ripristinata." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Nessun Tunnel Ssh", - "createFirstTunnelMessage": "Non hai ancora creato nessun tunnel SSH. Configura le connessioni del tunnel nel Gestore Host per iniziare.", - "connected": "Connesso", - "disconnected": "Disconnesso", - "connecting": "Connessione...", - "error": "Errore", - "canceling": "Annullamento...", - "connect": "Connetti", - "disconnect": "Disconnetti", - "cancel": "Annulla", - "port": "Porta", - "localPort": "Porta Locale", - "remotePort": "Porta Remota", - "currentHostPort": "Porta Host Corrente", - "endpointPort": "Porta Endpoint", - "bindIp": "IP Locale", - "endpointSshConfig": "Configurazione SSH Endpoint", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", "endpointSshHost": "Endpoint SSH Host", - "endpointSshHostPlaceholder": "Seleziona un host configurato", - "endpointSshHostRequired": "Selezionare un host SSH endpoint per ogni tunnel client.", - "attempt": "Tentativo di {{current}} di {{max}}", - "nextRetryIn": "Prossimo riprovare in {{seconds}} secondi", - "clientTunnels": "Tunnel Client", - "clientTunnel": "Tunnel Client", - "addClientTunnel": "Aggiungi Tunnel Client", - "noClientTunnels": "Nessun tunnel client configurato su questo desktop.", - "tunnelName": "Nome Del Tunnel", - "remoteHost": "Host Remoto", - "autoStart": "Avvio Automatico", - "clientAutoStartDesc": "Avvia quando questo client desktop si apre e rimane connesso.", - "clientManualStartDesc": "Usa Start e Stop da questa riga. Termix non lo aprirà automaticamente.", - "clientRemoteServerNote": "L'inoltro remoto potrebbe richiedere AllowTcpForwarding e GatewayPorts sul server SSH endpoint. La porta remota si chiude quando questo desktop si disconnette.", - "clientTunnelStarted": "Tunnel client avviato", - "clientTunnelStopped": "Tunnel client fermato", - "tunnelTestSucceeded": "Test tunnel riuscito", - "tunnelTestFailed": "Test tunnel fallito", - "localSaved": "Tunnel client salvati", - "localSaveError": "Impossibile salvare i tunnel del client locale", - "invalidBindIp": "L'IP locale deve essere un indirizzo IPv4 valido.", - "invalidLocalTargetIp": "L'IP di destinazione locale deve essere un indirizzo IPv4 valido.", - "invalidLocalPort": "La porta locale deve essere compresa tra 1 e 65535.", - "invalidRemotePort": "La porta remota deve essere compresa tra 1 e 65535.", - "invalidLocalTargetPort": "La porta locale di destinazione deve essere compresa tra 1 e 65535.", - "invalidEndpointPort": "Il porto di endpoint deve essere compreso tra 1 e 65535.", - "duplicateAutoStartBind": "Solo un tunnel client di avvio automatico può usare {{bind}}.", - "manualControlError": "Impossibile aggiornare lo stato del tunnel.", - "active": "Attivo", - "start": "Inizia", - "stop": "Ferma", - "test": "Prova", - "type": "Tipo Di Tunnel", - "typeLocal": "Locale (-L)", - "typeRemote": "Remoto (-R)", - "typeDynamic": "Dinamico (-D)", - "typeServerLocalDesc": "Host attuale all'endpoint.", - "typeServerRemoteDesc": "Endpoint torna all'host corrente.", - "typeClientLocalDesc": "Computer locale all'endpoint.", - "typeClientRemoteDesc": "Endpoint torna al computer locale.", - "typeClientDynamicDesc": "SOCKS sul computer locale.", - "typeDynamicDesc": "Avanti SOCKS5 CONNECT traffico attraverso SSH", - "forwardDescriptionServerLocal": "Host corrente {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host corrente {{sourcePort}}.", - "forwardDescriptionServerDynamic": "CALZINI sull'host corrente {{sourcePort}}.", - "forwardDescriptionClientLocal": "{{sourcePort}} → remoto {{endpointPort}}.", - "forwardDescriptionClientRemote": "{{sourcePort}} → locale {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS sulla porta locale {{sourcePort}}.", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "{{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → locale {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", - "route": "Percorso:", - "lastStarted": "Ultimo avvio", - "lastTested": "Ultimo test", - "lastError": "Ultimo errore", - "maxRetries": "Riprova Massima", - "maxRetriesDescription": "Quantità massima di tentativi di riprova.", - "retryInterval": "Intervallo Di Riprova (Secondi)", - "retryIntervalDescription": "Tempo di attesa tra i tentativi di riprova.", - "local": "Locale", - "remote": "Remoto", - "destination": "Destinazione", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", "host": "Host", - "mode": "Modalità", - "noHostSelected": "Nessun host selezionato", - "working": "Lavorando..." + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Memoria", - "disk": "Disco", - "network": "Rete", + "memory": "Memory", + "disk": "Disk", + "network": "Network", "uptime": "Uptime", - "processes": "Processi", - "available": "Disponibile", - "free": "Gratis", - "connecting": "Connessione...", - "connectionFailed": "Connessione al server non riuscita", - "naCpus": "CPU N/A", - "cpuCores_one": "Nucleo {{count}}", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Utilizzo CPU", - "memoryUsage": "Utilizzo Memoria", - "diskUsage": "Uso Del Disco", - "failedToFetchHostConfig": "Impossibile recuperare la configurazione dell'host", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", "serverOffline": "Server Offline", - "cannotFetchMetrics": "Impossibile recuperare le metriche dal server fuori rete", - "totpFailed": "Verifica TOTP fallita", - "noneAuthNotSupported": "Le statistiche del server non supportano il tipo di autenticazione 'nessuno'.", - "load": "Carica", - "systemInfo": "Informazioni Di Sistema", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Sistema Operativo", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "secondi", - "networkInterfaces": "Interfacce Di Rete", - "noInterfacesFound": "Nessuna interfaccia di rete trovata", - "noProcessesFound": "Nessun processo trovato", - "loginStats": "Statistiche Di Accesso SSH", - "noRecentLoginData": "Nessun dato di accesso recente", - "executingQuickAction": "Esecuzione {{name}}...", - "quickActionSuccess": "{{name}} completato con successo", - "quickActionFailed": "{{name}} fallito", - "quickActionError": "Esecuzione di {{name}} non riuscita", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Porte Di Ascoltamento", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Porta", - "address": "Indirizzo", - "process": "Processo", - "noData": "Nessun dato di porte in ascolto" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inattivo", - "policy": "Politica", - "rules": "regole", - "noData": "Dati firewall non disponibili", - "action": "Azione", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Porta", - "source": "Fonte", - "anywhere": "Ovunque" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Carica Media", - "swap": "Scambia", - "architecture": "Architettura", - "refresh": "Aggiorna", - "retry": "Riprova" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Gestione SSH e desktop remoto self-hosted", - "loginTitle": "Accedi a Termix", - "registerTitle": "Crea Account", - "forgotPassword": "Password Dimenticata?", - "rememberMe": "Ricorda il dispositivo per 30 giorni (include TOTP)", - "noAccount": "Non hai un account?", - "hasAccount": "Hai già un account?", - "twoFactorAuth": "Autenticazione A Due Fattori", - "enterCode": "Inserisci codice di verifica", - "backupCode": "Oppure utilizzare il codice di backup", - "verifyCode": "Verifica Codice", - "redirectingToApp": "Reindirizzamento all'app...", - "sshAuthenticationRequired": "Autenticazione SSH richiesta", - "sshNoKeyboardInteractive": "Autenticazione Tastiera-Interattiva Non Disponibile", - "sshAuthenticationFailed": "Autenticazione Non Riuscita", - "sshAuthenticationTimeout": "Timeout Autenticazione", - "sshNoKeyboardInteractiveDescription": "Il server non supporta l'autenticazione interattiva con tastiera. Si prega di fornire la password o la chiave SSH.", - "sshAuthFailedDescription": "Le credenziali fornite non sono corrette. Riprova con credenziali valide.", - "sshTimeoutDescription": "Il tentativo di autenticazione è scaduto. Riprova.", - "sshProvideCredentialsDescription": "Fornisci le tue credenziali SSH per connetterti a questo server.", - "sshPasswordDescription": "Inserire la password per questa connessione SSH.", - "sshKeyPasswordDescription": "Se la tua chiave SSH è crittografata, inserisci qui la frase segreta.", - "passphraseRequired": "Passphrase Richiesto", - "passphraseRequiredDescription": "La chiave SSH è crittografata. Inserisci la frase segreta per sbloccarla.", - "back": "Indietro", - "firstUser": "Primo Utente", - "firstUserMessage": "Sei il primo utente e verrà creato un amministratore. È possibile visualizzare le impostazioni di amministratore nella barra laterale a discesa. Se pensi che si tratti di un errore, controlla i registri docker o crea un problema GitHub.", - "external": "Esterno", - "loginWithExternal": "Accedi con Provider esterno", - "loginWithExternalDesc": "Accedi utilizzando il tuo provider di identità esterno configurato", - "externalNotSupportedInElectron": "L'autenticazione esterna non è ancora supportata nell'app Electron. Si prega di utilizzare la versione web per l'accesso OIDC.", - "resetPasswordButton": "Reimposta Password", - "sendResetCode": "Invia Codice Di Ripristino", - "resetCodeDesc": "Inserisci il tuo nome utente per ricevere un codice di reimpostazione della password. Il codice verrà registrato nei log del contenitore docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verifica Codice", - "enterResetCode": "Inserire il codice a 6 cifre dai registri del contenitore docker per l'utente:", - "newPassword": "Nuova Password", - "confirmNewPassword": "Conferma Password", - "enterNewPassword": "Inserisci la tua nuova password per l'utente:", - "signUp": "Registrati", - "desktopApp": "App Desktop", - "loggingInToDesktopApp": "Accesso all'app desktop", - "loadingServer": "Caricamento server...", - "dataLossWarning": "Reimpostare la password in questo modo eliminerà tutti gli host SSH salvati, le credenziali e altri dati crittografati. Questa azione non può essere annullata. Usa questa opzione solo se hai dimenticato la password e non hai effettuato l'accesso.", - "authenticationDisabled": "Autenticazione Disabilitata", - "authenticationDisabledDesc": "Tutti i metodi di autenticazione sono attualmente disabilitati. Contatta l'amministratore.", - "attemptsRemaining": "{{count}} tentativi rimanenti" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifica La Chiave Host Ssh", - "keyChangedWarning": "Chiave Host Ssh Cambiata", - "firstConnectionTitle": "Prima volta che ci si connette a questo host", - "firstConnectionDescription": "L'autenticità di questo host non può essere stabilita. Verifica che l'impronta digitale corrisponda a quanto ci si aspetta.", - "keyChangedDescription": "La chiave host di questo server è cambiata dall'ultima connessione. Questo potrebbe indicare un problema di sicurezza.", - "previousKey": "Chiave Precedente", - "newFingerprint": "Nuova Impronta Digitale", - "fingerprint": "Impronta", - "verifyInstructions": "Se ti fidi di questo host, fai clic su Accetta per continuare e salvare questa impronta digitale per le connessioni future.", - "securityWarning": "Avviso Di Sicurezza", - "acceptAndContinue": "Accetta E Continua", - "acceptNewKey": "Accetta La Nuova Chiave E Continua" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Impossibile connettersi al database", - "unknownError": "Errore sconosciuto", - "loginFailed": "Accesso fallito", - "failedPasswordReset": "Impossibile avviare il ripristino della password", - "failedVerifyCode": "Impossibile verificare il codice di ripristino", - "failedCompleteReset": "Impossibile completare il ripristino della password", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Avvio del login OIDC non riuscito", - "silentSigninOidcUnavailable": "L'accesso silenzioso è stato richiesto, ma l'accesso OIDC non è disponibile.", - "failedUserInfo": "Recupero delle informazioni utente dopo il login non riuscito", - "oidcAuthFailed": "Autenticazione OIDC non riuscita", - "invalidAuthUrl": "URL di autorizzazione non valido ricevuto dal backend", - "requiredField": "Questo campo è obbligatorio", - "minLength": "La lunghezza minima è {{min}}", - "passwordMismatch": "Le password non corrispondono", - "passwordLoginDisabled": "Nome utente/password di accesso è attualmente disabilitato", - "sessionExpired": "Sessione scaduta - si prega di accedere di nuovo", - "totpRateLimited": "Tasso limitato: troppi tentativi di verifica TOTP. Riprova più tardi.", - "totpRateLimitedWithTime": "Tentativi limitati: troppi tentativi di verifica TOTP. Attendi {{time}} secondi prima di riprovare.", - "resetCodeRateLimited": "Tasso limitato: Troppi tentativi di verifica. Riprova più tardi.", - "resetCodeRateLimitedWithTime": "Tasso limitato: Troppi tentativi di verifica. Si prega di attendere {{time}} secondi prima di riprovare.", - "authTokenSaveFailed": "Impossibile salvare il token di autenticazione", - "failedToLoadServer": "Caricamento del server non riuscito" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "La registrazione del nuovo account è attualmente disabilitata da un amministratore. Effettua il login o contatta un amministratore.", - "userNotAllowed": "Il tuo account non è autorizzato a registrarsi. Contatta un amministratore.", - "databaseConnectionFailed": "Connessione al server del database non riuscita", - "resetCodeSent": "Reimposta il codice inviato ai registri Docker", - "codeVerified": "Codice verificato con successo", - "passwordResetSuccess": "Password reimpostata con successo", - "loginSuccess": "Login riuscito", - "registrationSuccess": "Registrazione completata" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Tunnels desktop locali targeting host SSH configurati.", - "c2sTunnelPresets": "Impostazioni Tunnel Client", - "c2sTunnelPresetsDesc": "Salva l'elenco dei tunnel locali di questo client desktop come preimpostazione server con nome o carica nuovamente un preset in questo client.", - "c2sTunnelPresetsUnavailable": "Le preimpostazioni del tunnel client sono disponibili solo nel client desktop.", - "c2sPresetName": "Nome Predefinito", - "c2sPresetNamePlaceholder": "Nome preset client", + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", "c2sPresetToLoad": "Preset To Load", - "c2sNoPresetSelected": "Nessuna preimpostazione selezionata", - "c2sNoPresets": "Nessun preset salvato", - "c2sLoadPreset": "Carica", - "c2sCurrentLocalConfig": "{{count}} tunnel client locali configurati su questo desktop.", - "c2sPresetSyncNote": "Le preimpostazioni sono istantanee esplicite; il caricamento di una sostituisce l'elenco dei tunnel dei client locali di questo client desktop.", - "c2sPresetSaved": "Preimpostazione tunnel client salvata", - "c2sPresetLoaded": "Preimpostazione tunnel client caricata localmente", - "c2sPresetRenamed": "Preimpostazione tunnel client rinominata", - "c2sPresetDeleted": "Preimpostazione tunnel client eliminata", - "c2sPresetLoadError": "Impossibile caricare le preimpostazioni del tunnel client" + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Lingua", - "keyPassword": "password della chiave", - "pastePrivateKey": "Incolla qui la tua chiave privata...", - "localListenerHost": "127.0.0.1 (ascolta localmente)", - "localTargetHost": "127.0.0.1 (obiettivo su questo computer)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Inserisci la tua password", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "Dashboard", - "loading": "Caricamento dashboard...", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Supporto", + "support": "Support", "discord": "Discord", - "serverOverview": "Panoramica Del Server", - "version": "Versione", - "upToDate": "Fino alla data", - "updateAvailable": "Aggiornamento Disponibile", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", "uptime": "Uptime", "database": "Database", - "healthy": "Sano", - "error": "Errore", - "totalHosts": "Totale Host", - "totalTunnels": "Totale Gallerie", - "totalCredentials": "Credenziali Totali", - "recentActivity": "Attività Recenti", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Caricamento attività recente...", - "noRecentActivity": "Nessuna attività recente", - "quickActions": "Azioni Rapide", - "addHost": "Aggiungi Host", - "addCredential": "Aggiungi Credenziali", - "adminSettings": "Impostazioni Amministratore", - "userProfile": "Profilo Utente", - "serverStats": "Statistiche Server", - "loadingServerStats": "Caricamento statistiche server...", - "noServerData": "Nessun dato server disponibile", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizza Dashboard", - "dashboardSettings": "Impostazioni Dashboard", - "enableDisableCards": "Abilita/Disabilita Carte", - "resetLayout": "Ripristina predefinito", - "serverOverviewCard": "Panoramica Del Server", - "recentActivityCard": "Attività Recenti", - "networkGraphCard": "Grafico Di Rete", - "networkGraph": "Grafico Di Rete", - "quickActionsCard": "Azioni Rapide", - "serverStatsCard": "Statistiche Server", - "panelMain": "Principale", - "panelSide": "Lato", - "justNow": "proprio ora" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { "stable": "STABLE", "hostsOnline": "Hosts Online", - "activeTunnels": "Gallerie Attive", - "registerNewServer": "Registra un nuovo server", - "storeSshKeysOrPasswords": "Memorizza le chiavi o le password SSH", - "manageUsersAndRoles": "Gestisci utenti e ruoli", - "manageYourAccount": "Gestisci il tuo account", - "hostStatus": "Stato Host", - "noHostsConfigured": "Nessun host configurato", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Aggiungere:", - "commandPalette": "Tavolozza Comandi", - "done": "Fatto", - "editModeInstructions": "Trascina le carte per riordinare · Trascina il divisore delle colonne per ridimensionare le colonne · Trascina il bordo inferiore di una scheda per ridimensionarne l'altezza · Cestino per rimuoverlo", - "empty": "Vuoto", - "clear": "Pulisci" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Aggiungi Host", - "addGroup": "Aggiungi Gruppo", - "addLink": "Aggiungi Collegamento", - "zoomIn": "Zoom Avanti", - "zoomOut": "Zoom Indietro", - "resetView": "Reimposta Vista", - "selectHost": "Seleziona Host", - "chooseHost": "Scegli un host...", - "parentGroup": "Gruppo Genitore", - "noGroup": "Nessun Gruppo", - "groupName": "Nome Gruppo", - "color": "Colore", - "source": "Fonte", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Sposta nel gruppo", - "selectGroup": "Seleziona gruppo...", - "addConnection": "Aggiungi Connessione", - "hostDetails": "Dettagli Host", - "removeFromGroup": "Rimuovi dal gruppo", - "addHostHere": "Aggiungi Host Qui", - "editGroup": "Modifica Gruppo", - "delete": "Elimina", - "add": "Aggiungi", - "create": "Crea", - "move": "Sposta", - "connect": "Connetti", - "createGroup": "Crea Gruppo", - "selectSourcePlaceholder": "Seleziona Sorgente...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "File Non Valido", - "hostAlreadyExists": "L'host è già nella topologia", - "connectionExists": "Connessione già esistente", - "unknown": "Sconosciuto", - "name": "Nome", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Stato", - "failedToAddNode": "Impossibile aggiungere il nodo", - "sourceDifferentFromTarget": "Origine e destinazione devono essere diversi", - "exportJSON": "Esporta JSON", - "importJSON": "Importa JSON", - "terminal": "Terminale", - "fileManager": "Gestore File", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Statistiche Server", - "noNodes": "Ancora nessun nodo" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Il docker non è abilitato per questo host", - "validating": "Convalida Docker...", - "connecting": "Connessione...", - "error": "Errore", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Impossibile connettersi al docker", - "containerStarted": "Contenitore {{name}} iniziato", - "failedToStartContainer": "Avvio del contenitore {{name}} non riuscito", - "containerStopped": "Contenitore {{name}} fermato", - "failedToStopContainer": "Arresto del contenitore {{name}} non riuscito", - "containerRestarted": "Contenitore {{name}} riavviato", - "failedToRestartContainer": "Riavvio del contenitore {{name}} non riuscito", - "containerPaused": "Contenitore {{name}} in pausa", - "containerUnpaused": "Contenitore {{name}} non in pausa", - "failedToTogglePauseContainer": "Impossibile attivare/disattivare lo stato di pausa per il contenitore {{name}}", - "containerRemoved": "Contenitore {{name}} rimosso", - "failedToRemoveContainer": "Rimozione del contenitore {{name}} non riuscita", - "image": "Immagine", - "ports": "Porte", - "noPorts": "Nessuna porta", - "start": "Inizia", - "confirmRemoveContainer": "Sei sicuro di voler rimuovere il contenitore '{{name}}'? Questa azione non può essere annullata.", - "runningContainerWarning": "Attenzione: Questo contenitore è attualmente in esecuzione. Rimuovendolo si fermerà prima il contenitore.", - "loadingContainers": "Caricamento contenitori...", - "manager": "Gestore Docker", - "autoRefresh": "Aggiornamento Automatico", - "timestamps": "Timestamp", - "lines": "Linee", - "filterLogs": "Filtro registro...", - "refresh": "Aggiorna", - "download": "Scarica", - "clear": "Pulisci", - "logsDownloaded": "Log scaricato con successo", - "last50": "Ultime 50", - "last100": "Ultimi 100", - "last500": "Ultime 500", - "last1000": "Ultime 1000", - "allLogs": "Tutti I Log", - "noLogsMatching": "Nessun log corrispondente \"{{query}}\"", - "noLogsAvailable": "Nessun log disponibile", - "noContainersFound": "Nessun contenitore trovato", - "noContainersFoundHint": "Nessun contenitore Docker disponibile su questo host", - "searchPlaceholder": "Cerca contenitori...", - "allStatuses": "Tutti Gli Stati", - "stateRunning": "Esecuzione", - "statePaused": "Pausa", - "stateExited": "Uscita", - "stateRestarting": "Riavvio", - "noContainersMatchFilters": "Nessun contenitore corrisponde ai tuoi filtri", - "noContainersMatchFiltersHint": "Prova a regolare i criteri di ricerca o filtro", - "failedToFetchStats": "Recupero statistiche container non riuscito", - "containerNotRunning": "Contenitore non in esecuzione", - "startContainerToViewStats": "Avvia il contenitore per visualizzare le statistiche", - "loadingStats": "Caricamento statistiche...", - "errorLoadingStats": "Errore nel caricare le statistiche", - "noStatsAvailable": "Nessuna statistica disponibile", - "cpuUsage": "Utilizzo CPU", - "current": "Corrente", - "memoryUsage": "Utilizzo Memoria", - "networkIo": "Rete I/O", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", "output": "Output", - "blockIo": "Blocco I/O", - "read": "Leggi", - "write": "Scrivi", - "pids": "PID", - "containerInformation": "Informazioni Contenitore", - "name": "Nome", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Stato", - "containerMustBeRunning": "Il contenitore deve essere in esecuzione per accedere alla console", - "verificationCodePrompt": "Inserisci codice di verifica", - "totpVerificationFailed": "Verifica TOTP non riuscita. Riprova.", - "warpgateVerificationFailed": "Autenticazione Warpgate non riuscita. Riprova.", - "connectedTo": "Connesso a {{containerName}}", - "disconnected": "Disconnesso", - "consoleError": "Errore di console", - "errorMessage": "Errore: {{message}}", - "failedToConnect": "Impossibile connettersi al contenitore", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", "console": "Console", - "selectShell": "Seleziona shell", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "ceneri", - "connect": "Connetti", - "disconnect": "Disconnetti", - "notConnected": "Non connesso", - "clickToConnect": "Fare clic su Connetti per avviare una sessione shell", - "connectingTo": "Connessione a {{containerName}}...", - "containerNotFound": "Contenitore non trovato", - "backToList": "Torna alla lista", - "logs": "Registri", - "stats": "Statistiche", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Avvia il contenitore per accedere alla console" + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Generale", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Utenti", - "sectionSessions": "Sessioni", - "sectionRoles": "Ruoli", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", "sectionDatabase": "Database", - "sectionApiKeys": "Chiavi API", - "allowRegistration": "Consenti Registrazione Utente", - "allowRegistrationDesc": "Lascia che i nuovi utenti si auto-registrino", - "allowPasswordLogin": "Consenti Password Login", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", "allowPasswordLoginDesc": "Username/password login", - "oidcAutoProvision": "Auto-Fornitura OIDC", - "oidcAutoProvisionDesc": "Creazione automatica di account per gli utenti OIDC anche quando la registrazione è disabilitata", - "allowPasswordReset": "Consenti Reset Password", - "allowPasswordResetDesc": "Ripristina il codice tramite i registri Docker", - "sessionTimeout": "Timeout Sessione", - "hours": "ore", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Predefiniti Di Monitoraggio", - "statusCheck": "Controllo Stato", - "metrics": "Metriche", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", "sec": "sec", - "logLevel": "Livello Registro", - "enableGuacamole": "Abilita Guacamolo", - "enableGuacamoleDesc": "Desktop remoto RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Configurare OpenID Connect per SSO. I campi contrassegnati con * sono obbligatori.", - "oidcClientId": "ID Client", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", "oidcClientSecret": "Client Secret", - "oidcAuthUrl": "Url Di Autorizzazione", - "oidcIssuerUrl": "Url Dell'Emittente", - "oidcTokenUrl": "URL Token", - "oidcUserIdentifier": "Percorso Identificatore Utente", - "oidcDisplayName": "Visualizza Percorso Nome", - "oidcScopes": "Ambiti", - "oidcUserinfoUrl": "Sovrascrivi L'Url Di Userinfo", - "oidcAllowedUsers": "Utenti Consentiti", - "oidcAllowedUsersDesc": "Una email per riga. Lasciare vuoto per consentire a tutti.", - "removeOidc": "Rimuovi", - "usersCount": "{{count}} utenti", - "createUser": "Crea", - "newRole": "Nuovo Ruolo", - "roleName": "Nome", - "roleDisplayName": "Nome Visualizzato", - "roleDescription": "Descrizione", - "rolesCount": "{{count}} ruoli", - "createRole": "Crea", - "creating": "Creazione...", - "exportDatabase": "Esporta Database", - "exportDatabaseDesc": "Scarica un backup di tutti gli host, credenziali e impostazioni", - "export": "Esporta", - "exporting": "Esportazione...", - "importDatabase": "Importa Database", - "importDatabaseDesc": "Ripristina da un file di backup .sqlite", - "importDatabaseSelected": "Selezionato: {{name}}", - "selectFile": "Seleziona File", - "changeFile": "Cambia", - "import": "Importa", - "importing": "Importazione...", - "apiKeysCount": "{{count}} chiavi", - "newApiKey": "Nuova Chiave Api", - "apiKeyCreatedWarning": "Chiave creata - copiala ora, non verrà mostrata di nuovo.", - "apiKeyName": "Nome", - "apiKeyUser": "Utente", - "apiKeySelectUser": "Seleziona un utente...", - "apiKeyExpiresAt": "Scade Il", - "createKey": "Crea Chiave", - "apiKeyNoExpiry": "Nessuna scadenza", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Autenticazione Doppio", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Locale", - "adminStatusAdministrator": "Amministratore", - "adminStatusRegularUser": "Utente Regolare", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", "customBadge": "CUSTOM", - "youBadge": "VOI", - "sessionsActive": "{{count}} attivo", - "sessionActive": "Attivo: {{time}}", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tutti", - "revokeAllSessionsSuccess": "Tutte le sessioni per l'utente revocato", - "revokeAllSessionsFailed": "Impossibile revocare le sessioni", - "revokeSessionFailed": "Impossibile revocare la sessione", - "addRole": "Aggiungi ruolo", - "noCustomRoles": "Nessun ruolo personalizzato definito", - "removeRoleFailed": "Impossibile rimuovere il ruolo", - "assignRoleFailed": "Impossibile assegnare il ruolo", - "deleteRoleFailed": "Impossibile eliminare il ruolo", - "userAdminAccess": "Amministratore", - "userAdminAccessDesc": "Accesso completo a tutte le impostazioni admin", - "userRoles": "Ruoli", - "revokeAllUserSessions": "Revoca Tutte Le Sessioni", - "revokeAllUserSessionsDesc": "Forza il re-login su tutti i dispositivi", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "L'eliminazione di questo utente è permanente.", - "deleteUser": "Elimina {{username}}", - "deleting": "Eliminazione...", - "deleteUserFailed": "Impossibile eliminare l'utente", - "deleteUserSuccess": "Utente \"{{username}}\" eliminato", - "deleteRoleSuccess": "Ruolo \"{{name}}\" eliminato", - "revokeKeySuccess": "Chiave \"{{name}}\" revocata", - "revokeKeyFailed": "Impossibile revocare la chiave", - "copiedToClipboard": "Copiato negli appunti", - "done": "Fatto", - "createUserTitle": "Crea Utente", - "createUserDesc": "Crea un nuovo account locale.", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", "createUserUsername": "Username", "createUserPassword": "Password", - "createUserPasswordHint": "Minimo 6 caratteri.", - "createUserEnterUsername": "Inserisci nome utente", - "createUserEnterPassword": "Inserire la password", - "createUserSubmit": "Crea Utente", - "editUserTitle": "Gestisci Utente: {{username}}", - "editUserDesc": "Modifica ruoli, stato amministratore, sessioni e impostazioni account.", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", "editUserUsername": "Username", - "editUserAuthType": "Tipo Autenticazione", - "editUserAdminStatus": "Stato Amministratore", - "editUserUserId": "Id Utente", - "linkAccountTitle": "Collega OIDC all'account Password", - "linkAccountDesc": "Unisci l'account OIDC {{username}} con un account locale esistente.", - "linkAccountWarningTitle": "Ciò permetterà:", - "linkAccountEffect1": "Elimina l'account solo OIDC", - "linkAccountEffect2": "Aggiunge l'accesso OIDC all'account di destinazione", - "linkAccountEffect3": "Consenti l'accesso sia OIDC che password", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Inserisci il nome utente dell'account locale a cui collegarsi", - "linkAccounts": "Collega Account", - "linkAccountSuccess": "Account OIDC collegato a \"{{username}}\"", - "linkAccountFailed": "Impossibile collegare l'account OIDC", - "linkAccountInProgress": "Collegamento...", - "saving": "Salvataggio...", - "updateRegistrationFailed": "Impossibile aggiornare l'impostazione della registrazione", - "updatePasswordLoginFailed": "Impossibile aggiornare l'impostazione del login password", - "updateOidcAutoProvisionFailed": "Impossibile aggiornare l'impostazione di auto-fornitura OIDC", - "updatePasswordResetFailed": "Impossibile aggiornare l'impostazione di reimpostazione password", - "sessionTimeoutRange2": "Il timeout della sessione deve essere compreso tra 1 e 720 ore", - "sessionTimeoutSaved": "Timeout sessione salvato", - "sessionTimeoutSaveFailed": "Impossibile salvare il timeout della sessione", - "monitoringIntervalInvalid": "Valori di intervallo non validi", - "monitoringSaved": "Impostazioni di monitoraggio salvate", - "monitoringSaveFailed": "Impossibile salvare le impostazioni di monitoraggio", - "guacamoleSaved": "Impostazioni di Guacamole salvate", - "guacamoleSaveFailed": "Salvataggio delle impostazioni di Guacamole non riuscito", - "guacamoleUpdateFailed": "Aggiornamento delle impostazioni di Guacamole non riuscito", - "logLevelUpdateFailed": "Impossibile aggiornare il livello di log", - "oidcSaved": "Configurazione OIDC salvata", - "oidcSaveFailed": "Salvataggio della configurazione OIDC non riuscito", - "oidcRemoved": "Configurazione OIDC rimossa", - "oidcRemoveFailed": "Rimozione della configurazione OIDC non riuscita", - "createUserRequired": "Nome utente e password sono richiesti", - "createUserPasswordTooShort": "La password deve contenere almeno 6 caratteri", - "createUserSuccess": "Utente \"{{username}}\" creato", - "createUserFailed": "Impossibile creare l'utente", - "updateAdminStatusFailed": "Impossibile aggiornare lo stato dell'amministratore", - "allSessionsRevoked": "Tutte le sessioni revocate", - "revokeSessionsFailed": "Impossibile revocare le sessioni", - "createRoleRequired": "Il nome e il nome visualizzato sono obbligatori", - "createRoleSuccess": "Ruolo \"{{name}}\" creato", - "createRoleFailed": "Impossibile creare il ruolo", - "apiKeyNameRequired": "Nome chiave obbligatorio", - "apiKeyUserRequired": "L'ID utente è obbligatorio", - "apiKeyCreatedSuccess": "Chiave API \"{{name}}\" creata", - "apiKeyCreateFailed": "Impossibile creare la chiave API", - "exportSuccess": "Database esportato con successo", - "exportFailed": "Esportazione database fallita", - "importSelectFile": "Per favore seleziona prima un file", - "importCompleted": "Importazione completata: {{total}} elementi importati, {{skipped}} saltato", - "importFailed": "Importazione fallita: {{error}}", - "importError": "Importazione del database fallita" + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { "hostLabel": "Host", - "hostPlaceholder": "192.168.1.1 o example.com", - "portLabel": "Porta", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", "usernameLabel": "Username", "usernamePlaceholder": "username", - "authLabel": "Autenticazione", + "authLabel": "Auth", "passwordLabel": "Password", "passwordPlaceholder": "password", - "privateKeyLabel": "Chiave Privata", - "privateKeyPlaceholder": "Incolla chiave privata...", - "credentialLabel": "Credenziali", - "credentialPlaceholder": "Seleziona una credenziale salvata", - "connectToTerminal": "Connetti al terminale", - "connectToFiles": "Connetti ai file" + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nessun terminale selezionato", - "noTerminalSelectedHint": "Apre una scheda terminale SSH per visualizzare la sua cronologia dei comandi", - "searchPlaceholder": "Cronologia ricerca...", - "clearAll": "Cancella Tutto", - "noHistoryEntries": "Nessuna voce della cronologia", - "trackingDisabled": "Il tracciamento della cronologia è disabilitato", - "trackingDisabledHint": "Abilitarlo nelle impostazioni del profilo per registrare i comandi." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Registrazione Della Chiave", - "recordToTerminals": "Registra sui terminali", - "selectAll": "Tutti", - "selectNone": "Nessuno", - "noTerminalTabsOpen": "Nessuna scheda terminale aperta", - "selectTerminalsAbove": "Seleziona i terminali sopra", - "broadcastInputPlaceholder": "Digita qui per trasmettere le sequenze...", - "stopRecording": "Interrompi Registrazione", - "startRecording": "Inizia La Registrazione", - "settingsTitle": "Impostazioni", - "enableRightClickCopyPaste": "Abilita copia/incolla clic destro" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { "layoutTitle": "Layout", - "selectLayoutAbove": "Seleziona un layout sopra", - "selectLayoutHint": "Scegli quanti riquadri visualizzare", - "panesTitle": "Riquadri", - "openTabsTitle": "Apri Schede", - "dragTabsHint": "Trascina le schede nei riquadri sopra oppure usa Assegnazione rapida", - "dropHere": "Rilascia qui", - "emptyPane": "Vuoto", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", "dashboard": "Dashboard", - "clearSplitScreen": "Cancella Schermo Diviso", - "quickAssign": "Assegnazione rapida", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", "alreadyAssigned": "Pane {{index}}", - "splitTab": "Scheda divisa", - "addToSplit": "Aggiungi a Divide", - "removeFromSplit": "Rimuovi da Split", - "assignToPane": "Assegna al riquadro" + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Crea Snippet", - "createSnippetDescription": "Crea una nuova snippet di comando per l'esecuzione rapida", - "nameLabel": "Nome", - "namePlaceholder": "es. Riavvia Nginx", - "descriptionLabel": "Descrizione", - "descriptionPlaceholder": "Descrizione facoltativa", - "optional": "Facoltativo", - "folderLabel": "Cartella", - "noFolder": "Nessuna cartella (non categorizzata)", - "commandLabel": "Comando", - "commandPlaceholder": "es. sudo systemctl riavvia nginx", - "cancel": "Annulla", - "createSnippetButton": "Crea Snippet", - "createFolderTitle": "Crea Cartella", - "createFolderDescription": "Organizza i pezzetti di codice nelle cartelle", - "folderNameLabel": "Nome Cartella", - "folderNamePlaceholder": "Ad esempio, Comandi di sistema, Script Docker", - "folderColorLabel": "Colore Cartella", - "folderIconLabel": "Icona Cartella", - "previewLabel": "Anteprima", - "folderNameFallback": "Nome Cartella", - "createFolderButton": "Crea Cartella", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Tutti", - "selectNone": "Nessuno", - "noTerminalTabsOpen": "Nessuna scheda terminale aperta", - "searchPlaceholder": "Cerca snippet...", - "newSnippet": "Nuovo Snippet", - "newFolder": "Nuova Cartella", - "run": "Esegui", - "noSnippetsInFolder": "Nessuna snippet in questa cartella", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", "uncategorized": "Uncategorized", - "editSnippetTitle": "Modifica Snippet", - "editSnippetDescription": "Aggiorna questa snippet di comando", - "saveSnippetButton": "Salva Modifiche", - "createSuccess": "Snippet creato con successo", - "createFailed": "Creazione snippet non riuscita", - "updateSuccess": "Snippet aggiornato con successo", - "updateFailed": "Aggiornamento snippet non riuscito", - "deleteFailed": "Impossibile eliminare la snippet", - "folderCreateSuccess": "Cartella creata con successo", - "folderCreateFailed": "Creazione della cartella non riuscita", - "editFolderTitle": "Modifica Cartella", - "editFolderDescription": "Rinomina o cambia l'aspetto di questa cartella", - "saveFolderButton": "Salva Modifiche", - "editFolder": "Modifica cartella", - "deleteFolder": "Elimina cartella", - "folderDeleteSuccess": "Cartella \"{{name}}\" eliminata", - "folderDeleteFailed": "Impossibile eliminare la cartella", - "folderEditSuccess": "Cartella aggiornata con successo", - "folderEditFailed": "Impossibile aggiornare la cartella", - "confirmRunMessage": "Eseguire \"{{name}}\"?", - "confirmRunButton": "Correre", - "runSuccess": "Ran \"{{name}}\" in {{count}} terminale(i)", - "copySuccess": "Copiato \"{{name}}\" negli appunti", - "shareTitle": "Condividi Snippet", - "shareUser": "Utente", - "shareRole": "Ruolo", - "selectUser": "Seleziona un utente...", - "selectRole": "Seleziona un ruolo...", - "shareSuccess": "Snippet condiviso con successo", - "shareFailed": "Condivisione snippet non riuscita", - "revokeSuccess": "Accesso revocato", - "revokeFailed": "Impossibile revocare l'accesso", - "currentAccess": "Accesso Corrente", - "shareLoadError": "Impossibile caricare i dati di condivisione", - "loading": "Caricamento...", - "close": "Chiudi", - "reorderFailed": "Impossibile salvare l'ordine degli snippet" + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", "sectionAccount": "Account", - "sectionAppearance": "Aspetto", - "sectionSecurity": "Sicurezza", - "sectionApiKeys": "Chiavi API", - "sectionC2sTunnels": "Gallerie C2S", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", "usernameLabel": "Username", - "roleLabel": "Ruolo", - "roleAdministrator": "Amministratore", - "authMethodLabel": "Metodo Autenticazione", - "authMethodLocal": "Locale", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Acceso", + "twoFaOn": "On", "twoFaOff": "Off", - "versionLabel": "Versione", - "deleteAccount": "Elimina Account", - "deleteAccountDescription": "Elimina definitivamente il tuo account", - "deleteButton": "Elimina", - "deleteAccountPermanent": "Questa azione è permanente e non può essere annullata.", - "deleteAccountWarning": "Tutte le sessioni, gli host, le credenziali e le impostazioni verranno eliminati definitivamente.", - "confirmPasswordDeletePlaceholder": "Inserisci la tua password per confermare", - "languageLabel": "Lingua", - "themeLabel": "Tema", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Colore Accento", - "settingsTerminal": "Terminale", - "commandAutocomplete": "Completamento Automatico Dei Comandi", - "commandAutocompleteDesc": "Mostra completamento automatico durante la digitazione", - "historyTracking": "Tracciamento Cronologia", - "historyTrackingDesc": "Comandi del terminale traccia", - "syntaxHighlighting": "Evidenziazione Sintassi", - "syntaxHighlightingDesc": "Evidenzia uscita terminale", - "commandPalette": "Tavolozza Comandi", - "commandPaletteDesc": "Abilita scorciatoia da tastiera", - "reopenTabsOnLogin": "Riapri le schede all'accesso", - "reopenTabsOnLoginDesc": "Ripristina le schede aperte quando accedi o aggiorni la pagina, anche da un altro dispositivo.", - "confirmTabClose": "Conferma Chiusura Scheda", - "confirmTabCloseDesc": "Chiedi prima di chiudere le schede del terminale", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Mostra Tag Host", - "showHostTagsDesc": "Mostra i tag nella lista host", - "hostTrayOnClick": "Fai clic per espandere le azioni dell'host", - "hostTrayOnClickDesc": "Mostra sempre i pulsanti di connessione; fai clic per espandere le opzioni di gestione anziché passare il mouse sopra", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Mantieni la barra laterale sinistra sempre espansa, anziché espanderla al passaggio del mouse.", - "settingsSnippets": "Snippet", - "foldersCollapsed": "Cartelle Compensate", - "foldersCollapsedDesc": "Comprimi le cartelle come predefinite", - "confirmExecution": "Conferma Esecuzione", - "confirmExecutionDesc": "Conferma prima di eseguire snippet", - "settingsUpdates": "Aggiornamenti", - "disableUpdateChecks": "Disabilita Controllo Aggiornamenti", - "disableUpdateChecksDesc": "Interrompi il controllo degli aggiornamenti", - "totpAuthenticator": "Autenticatore TOTP", - "totpEnabled": "2FA è abilitato", - "totpDisabled": "Aggiungi sicurezza extra per l'accesso", - "disable": "Disabilita", - "enable": "Abilita", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scansiona il codice QR o inserisci il segreto nell'app di autenticazione, quindi inserisci il codice a 6 cifre", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifica", - "changePassword": "Cambia Password", - "currentPasswordLabel": "Password Attuale", - "currentPasswordPlaceholder": "Password attuale", - "newPasswordLabel": "Nuova Password", - "newPasswordPlaceholder": "Nuova password", - "confirmPasswordLabel": "Conferma Nuova Password", - "confirmPasswordPlaceholder": "Conferma la nuova password", - "updatePassword": "Aggiorna Password", - "createApiKeyTitle": "Crea Chiave API", - "createApiKeyDescription": "Genera una nuova chiave API per l'accesso programmatico.", - "apiKeyNameLabel": "Nome", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "opzionale", - "cancel": "Annulla", - "createKey": "Crea Chiave", - "apiKeyCount": "{{count}} chiavi", - "newKey": "Nuova Chiave", - "noApiKeys": "Ancora nessuna chiave API.", - "apiKeyActive": "Attivo", - "apiKeyUsageHint": "Includi la tua chiave nella", - "apiKeyUsageHintHeader": "intestazione.", - "apiKeyPermissionsHint": "Le chiavi ereditano i permessi dell'utente creatore.", - "roleUser": "Utente", - "authMethodDual": "Autenticazione Doppio", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Avvio della configurazione TOTP non riuscito", - "totpEnter6Digits": "Inserisci un codice a 6 cifre", - "totpEnabledSuccess": "Autenticazione a due fattori abilitata", - "totpInvalidCode": "Codice non valido, riprova", - "totpDisableInputRequired": "Inserisci il codice TOTP o la password", - "totpDisabledSuccess": "Autenticazione a due fattori disabilitata", - "totpDisableFailed": "Impossibile disattivare 2FA", - "totpDisableTitle": "Disabilita 2FA", - "totpDisablePlaceholder": "Inserisci il codice TOTP o la password", - "totpDisableConfirm": "Disabilita 2FA", - "totpContinueVerify": "Continua a verificare", - "totpVerifyTitle": "Verifica Codice", - "totpBackupTitle": "Codici Backup", - "totpDownloadBackup": "Scarica Codici Di Backup", - "done": "Fatto", - "secretCopied": "Segreto copiato negli appunti", - "apiKeyNameRequired": "Nome chiave obbligatorio", - "apiKeyCreated": "Chiave API \"{{name}}\" creata", - "apiKeyCreateFailed": "Impossibile creare la chiave API", - "apiKeyUser": "Utente", - "apiKeyExpires": "Scade", - "apiKeyRevoked": "Chiave API \"{{name}}\" revocata", - "apiKeyRevokeFailed": "Impossibile revocare la chiave API", - "passwordFieldsRequired": "Le password attuali e nuove sono richieste", - "passwordMismatch": "Le password non corrispondono", - "passwordTooShort": "La password deve contenere almeno 6 caratteri", - "passwordUpdated": "Password aggiornata correttamente", - "passwordUpdateFailed": "Impossibile aggiornare la password", - "deletePasswordRequired": "La password è necessaria per eliminare il tuo account", - "deleteFailed": "Impossibile eliminare l'account", - "deleting": "Eliminazione...", - "colorPickerTooltip": "Apri selettore colore", - "themeSystem": "Sistema", - "themeLight": "Chiaro", - "themeDark": "Scuro", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizzato", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Un Oscuro", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ja_JP.json b/src/ui/locales/translated/ja_JP.json index b07c8013..9bc5af32 100644 --- a/src/ui/locales/translated/ja_JP.json +++ b/src/ui/locales/translated/ja_JP.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "フォルダ", - "folder": "フォルダ", - "password": "パスワード", - "key": "キー", - "sshPrivateKey": "SSHプライベートキー", - "upload": "アップロード", - "keyPassword": "キーパスワード", - "sshKey": "SSH キー", - "uploadPrivateKeyFile": "プライベートキーファイルをアップロード", - "searchCredentials": "資格情報を検索...", - "addCredential": "証明書の追加", - "caCertificate": "CA 証明書 (-cert.pub)", - "caCertificateDescription": "オプション: CA 署名証明書ファイルをアップロードまたは貼り付けます (例: id_ed25519-cert.pub)。 SSHサーバーが証明書ベースの認証を使用する場合に必要です。", - "uploadCertFile": "-cert.pub ファイルをアップロード", - "clearCert": "クリア", - "certLoaded": "証明書が読み込まれました", - "certPublicKeyLabel": "CA 証明書", - "certTypeLabel": "証明書の種類", - "pasteOrUploadCert": "-cert.pub 証明書を貼り付けまたはアップロード...", - "hasCaCert": "CA 証明書", - "noCaCert": "CA証明書がありません" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "アラートの読み込みに失敗しました", - "failedToDismissAlert": "アラートを解除できませんでした" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "サーバーの設定", - "description": "バックエンドサービスに接続するTermixサーバーのURLを設定する", - "serverUrl": "サーバー URL", - "enterServerUrl": "サーバーの URL を入力してください", - "saveFailed": "設定の保存に失敗しました", - "saveError": "設定の保存中にエラーが発生しました", - "saving": "保存中...", - "saveConfig": "設定を保存", - "helpText": "Termixサーバーが稼働しているURLを入力してください(例:http://localhost:30001、https://your-server.com)", - "changeServer": "サーバーの変更", - "mustIncludeProtocol": "サーバーのURLはhttp://またはhttps://で始まる必要があります", - "allowInvalidCertificate": "無効な証明書を許可する", - "allowInvalidCertificateDesc": "自己署名証明書またはIPアドレス証明書を使用する、信頼できる自己ホスト型サーバーでのみ使用してください。", - "useEmbedded": "ローカルサーバーを使用", - "embeddedDesc": "内蔵のローカルサーバー(リモートサーバーは必要ありません)でTermixを実行", - "embeddedConnecting": "ローカルサーバーに接続中...", - "embeddedNotReady": "ローカルサーバーの準備ができていません。しばらく待ってからもう一度やり直してください。", - "localServer": "ローカルサーバー" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "バージョンチェックエラー", - "checkFailed": "更新の確認に失敗しました", - "upToDate": "アプリは最新です", - "currentVersion": "あなたはバージョン {{version}}を実行しています", - "updateAvailable": "アップデートがあります", - "newVersionAvailable": "新しいバージョンが利用可能です!あなたは {{current}}を実行していますが、 {{latest}} は利用可能です。", - "betaVersion": "ベータバージョン", - "betaVersionDesc": "あなたは最新の安定版 {{current}}より新しい {{latest}}を実行しています。", - "releasedOn": "{{date}} でリリース", - "downloadUpdate": "アップデートをダウンロード", - "checking": "アップデートを確認しています...", - "checkUpdates": "アップデートの確認", - "checkingUpdates": "アップデートを確認しています...", - "updateRequired": "更新が必要です" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "閉じる", + "close": "Close", "minimize": "Minimize", - "online": "オンライン", - "offline": "オフライン", - "continue": "続ける", - "maintenance": "メンテナンス", - "degraded": "劣化しました", - "error": "エラー", - "warning": "警告", - "unsavedChanges": "保存されていない変更", - "dismiss": "却下する", - "loading": "読み込み中...", - "optional": "省略可能", - "connect": "接続する", - "copied": "コピーしました", - "connecting": "接続中...", - "updateAvailable": "アップデートがあります", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "新しいタブで開く", - "noReleases": "リリースなし", - "updatesAndReleases": "更新とリリース", - "newVersionAvailable": "新しいバージョン({{version}})が利用可能です。", - "failedToFetchUpdateInfo": "更新情報の取得に失敗しました", - "preRelease": "プレリリース", - "noReleasesFound": "リリースが見つかりません。", - "cancel": "キャンセル", - "username": "ユーザー名", - "login": "ログイン", - "register": "登録", - "password": "パスワード", - "confirmPassword": "パスワードの確認", - "back": "戻る", - "save": "保存", - "saving": "保存中...", - "delete": "削除", - "rename": "名前の変更", - "edit": "編集", - "add": "追加", - "confirm": "確認する", - "no": "いいえ", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", "or": "OR", - "next": "次へ", - "previous": "前", - "refresh": "更新", - "language": "言語", - "checking": "確認しています...", - "checkingDatabase": "データベース接続を確認しています...", - "checkingAuthentication": "認証中char@@0", - "backendReconnected": "サーバー接続が復元されました", - "connectionDegraded": "サーバー接続が失われました。復旧中です…", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "削除", - "create": "作成", - "update": "更新", - "copy": "コピー", - "copyFailed": "クリップボードにコピーできませんでした", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "復元", - "of": "/" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "ホーム", - "terminal": "ターミナル", + "home": "Home", + "terminal": "Terminal", "docker": "Docker", - "tunnels": "トンネル", - "fileManager": "ファイルマネージャー", - "serverStats": "サーバーの統計", - "admin": "管理者", - "userProfile": "ユーザープロフィール", - "splitScreen": "画面の分割", - "confirmClose": "このアクティブなセッションを閉じますか?", - "close": "閉じる", - "cancel": "キャンセル", - "sshManager": "SSH マネージャー", - "cannotSplitTab": "このタブは分割できません", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "パスワードをコピー", - "copySudoPassword": "Sudoパスワードをコピー", - "passwordCopied": "パスワードをクリップボードにコピーしました", - "noPasswordAvailable": "パスワードがありません", - "failedToCopyPassword": "パスワードのコピーに失敗しました", - "refreshTab": "接続を更新", - "openFileManager": "ファイルマネージャを開く", - "dashboard": "ダッシュボード", - "networkGraph": "ネットワークグラフ", - "quickConnect": "クイック接続", - "sshTools": "SSH ツール", - "history": "沿革", - "hosts": "ホスト", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", "snippets": "Snippets", - "hostManager": "ホスト マネージャー", - "credentials": "資格情報", - "connections": "つながり", - "roleAdministrator": "管理者", - "roleUser": "ユーザー" + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "ホスト", - "noHosts": "SSHホストがありません", - "retry": "再試行する", - "refresh": "更新", - "optional": "省略可能", - "downloadSample": "サンプルをダウンロード", - "failedToDeleteHost": "{{name}} の削除に失敗しました", - "importSkipExisting": "インポート (スキップが既存)", - "connectionDetails": "接続詳細", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "リモートデスクトップ", - "port": "ポート", - "username": "ユーザー名", - "folder": "フォルダ", - "tags": "タグ", - "pin": "ピン留めする", - "addHost": "ホストを追加", - "editHost": "ホストを編集", - "cloneHost": "ホストを複製", - "enableTerminal": "ターミナルを有効にする", - "enableTunnel": "トンネルを有効化", - "enableFileManager": "ファイルマネージャーを有効にする", - "enableDocker": "Docker を有効にする", - "defaultPath": "既定のパス", - "connection": "接続", - "upload": "アップロード", - "authentication": "認証", - "password": "パスワード", - "key": "キー", - "credential": "資格情報", - "none": "なし", - "sshPrivateKey": "SSHプライベートキー", - "keyType": "キーの種類", - "uploadFile": "ファイルをアップロード", - "tabGeneral": "全般", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "トンネル", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "ファイル", - "tabStats": "統計", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "共有", - "tabAuthentication": "認証", - "terminal": "ターミナル", - "tunnel": "トンネル", - "fileManager": "ファイルマネージャー", - "serverStats": "サーバーの統計", - "status": "ステータス", - "folderRenamed": "フォルダ \"{{oldName}}\" の名前を \"{{newName}}\" に変更しました", - "failedToRenameFolder": "フォルダの名前を変更できませんでした", - "movedToFolder": "\"{{folder}} \"に移動しました", - "editHostTooltip": "ホストを編集", - "statusChecks": "状態チェック", - "metricsCollection": "メトリックコレクション", - "metricsInterval": "メトリックコレクションの間隔", - "metricsIntervalDesc": "サーバーの統計情報を収集する頻度(5秒~1時間)", - "behavior": "動作", - "themePreview": "テーマのプレビュー", - "theme": "テーマ", - "fontFamily": "フォントファミリー", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "文字間隔", - "lineHeight": "行の高さ", - "cursorStyle": "カーソルスタイル", - "cursorBlink": "カーソルブリンク", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", "scrollbackBuffer": "Scrollback Buffer", - "bellStyle": "ベルスタイル", - "rightClickSelectsWord": "右クリックで単語を選択", - "fastScrollModifier": "高速スクロールの変更", - "fastScrollSensitivity": "高速スクロール感度", - "sshAgentForwarding": "SSH エージェント転送", - "backspaceMode": "バックスペースモード", - "startupSnippet": "スタートアップスニペットを開く", - "selectSnippet": "スニペットを選択", - "forceKeyboardInteractive": "キーボード対話を強制する", - "overrideCredentialUsername": "認証情報のユーザー名を上書きする", - "overrideCredentialUsernameDesc": "資格情報のユーザー名の代わりに上記のユーザー名を使用してください", - "jumpHostChain": "ジャンプホスト チェーン", - "portKnocking": "ポートノッキング(ポートノッキング)", - "addKnock": "ポートを追加", - "addProxyNode": "ノードを追加", - "proxyNode": "プロキシノード", - "proxyType": "プロキシタイプ", - "quickActions": "クイックアクション", - "sudoPasswordAutoFill": "Sudoパスワード自動入力", - "sudoPassword": "Sudoのパスワード", - "keepaliveInterval": "保持間隔 (ms)", - "moshCommand": "MOSHコマンド", - "environmentVariables": "環境変数", - "addVariable": "変数を追加", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "端末の URL をコピー", - "copyFileManagerUrl": "ファイルマネージャーの URL をコピー", - "copyRemoteDesktopUrl": "リモートデスクトップの URL をコピー", - "failedToConnect": "コンソールに接続できませんでした", - "connect": "接続する", - "disconnect": "接続を解除", - "start": "開始", - "enableStatusCheck": "ステータスチェックを有効にする", - "enableMetrics": "メトリックを有効にする", - "bulkUpdateFailed": "一括更新に失敗しました", - "selectAll": "すべて選択", - "deselectAll": "すべての選択を解除", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "安全なシェル", - "virtualNetwork": "仮想ネットワーク", - "unencryptedShell": "非暗号化シェル", - "addressIp": "アドレス / IP", - "friendlyName": "フレンドリー名", - "folderAndAdvanced": "フォルダと詳細設定", - "privateNotes": "プライベートノート", - "privateNotesPlaceholder": "このサーバーに関する詳細...", - "pinToTop": "上部に固定", - "pinToTopDesc": "このホストをリストの一番上に常に表示", - "portKnockingSequence": "ポートノックシーケンス", - "addKnockBtn": "ノックを追加", - "noPortKnocking": "設定されたポートがありません。", - "knockPort": "ノックポート", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", "delayAfterMs": "Delay After (ms)", - "useSocks5Proxy": "SOCKS5 プロキシを使用する", - "useSocks5ProxyDesc": "プロキシ サーバー経由での接続をルート", - "proxyHost": "プロキシホスト", - "proxyPort": "プロキシポート", - "proxyUsername": "プロキシユーザー名", - "proxyPassword": "プロキシパスワード", - "proxySingleMode": "単一のプロキシ", - "proxyChainMode": "プロキシチェーン", - "you": "あなたです", - "jumpHostChainLabel": "ジャンプホスト チェーン", - "addJumpBtn": "ジャンプを追加", - "noJumpHosts": "ジャンプホストが設定されていません。", - "selectAServer": "サーバーを選択...", - "sshPort": "SSH ポート", - "authMethod": "認証方法", - "storedCredential": "保存された資格情報", - "selectACredential": "資格情報を選択...", - "keyTypeLabel": "キーの種類", - "keyTypeAuto": "自動検出", - "keyPasteTab": "貼り付け", - "keyUploadTab": "アップロード", - "keyFileLoaded": "キーファイルが読み込まれました", - "keyUploadClick": "クリックして .pem / .key / .ppk をアップロードします", - "clearKey": "キーをクリア", - "keySaved": "SSHキーを保存しました", - "keyReplaceNotice": "新しいキーを置き換えるには、以下に新しいキーを貼り付けます", - "keyPassphraseSaved": "パスフレーズを保存しました。変更するにはパスワードを入力してください", - "replaceKey": "キーを置き換え", - "forceKeyboardInteractiveLabel": "強制的にキーボードをインタラクティブにする", - "forceKeyboardInteractiveShortDesc": "キーが存在する場合でも手動パスワード入力を強制する", - "terminalAppearance": "端末の外観", - "colorTheme": "色のテーマ", - "fontFamilyLabel": "フォントファミリー", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "カーソルスタイル", - "letterSpacingPx": "文字間隔 (px)", - "lineHeightLabel": "行の高さ", - "bellStyleLabel": "ベルスタイル", - "backspaceModeLabel": "バックスペースモード", - "cursorBlinking": "カーソルのBlinking", - "cursorBlinkingDesc": "端末カーソルの点滅アニメーションを有効にする", - "rightClickSelectsWordLabel": "右クリックで単語を選択", - "rightClickSelectsWordShortDesc": "右クリック時にカーソルの下にある単語を選択します", - "behaviorAndAdvanced": "ビヘイビア&アドバンスト", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", "scrollbackBufferLabel": "Scrollback Buffer", - "scrollbackMaxLines": "履歴に保持される行の最大数", - "sshAgentForwardingLabel": "SSH エージェント転送", - "sshAgentForwardingShortDesc": "ローカル SSH キーをこのホストに渡します", - "enableAutoMosh": "自動モスを有効にする", - "enableAutoMoshDesc": "利用可能な場合はMosh を SSH よりも優先します", - "enableAutoTmux": "自動Tmuxを有効にする", - "enableAutoTmuxDesc": "tmux セッションに自動的に起動またはアタッチする", - "sudoPasswordAutoFillLabel": "Sudoパスワード自動入力", - "sudoPasswordAutoFillShortDesc": "プロンプトが表示されたら自動的に sudo パスワードを提供します", - "sudoPasswordLabel": "Sudoのパスワード", - "environmentVariablesLabel": "環境変数", - "addVariableBtn": "変数を追加", - "noEnvVars": "環境変数が設定されていません。", - "fastScrollModifierLabel": "高速スクロールの変更", - "fastScrollSensitivityLabel": "高速スクロール感度", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", "moshCommandLabel": "Mosh Command", - "startupSnippetLabel": "スタートアップスニペットを開く", - "keepaliveIntervalLabel": "キープアライブ間隔(秒)", - "maxKeepaliveMisses": "最大Keep生存ミス数", - "tunnelSettings": "トンネルの設定", - "enableTunneling": "トンネリングを有効にする", - "enableTunnelingDesc": "このホストの SSH トンネル機能を有効にする", - "serverTunnelsSection": "サーバートンネル", - "addTunnelBtn": "トンネルを追加", - "noTunnelsConfigured": "トンネルが設定されていません。", - "tunnelLabel": "トンネル {{number}}", - "tunnelType": "トンネルタイプ", - "tunnelModeLocalDesc": "ローカルポートをリモートサーバー(またはそこから到達可能なホスト)のポートに転送します。", - "tunnelModeRemoteDesc": "リモートサーバーのポートをマシンのローカルポートに転送します。", - "tunnelModeDynamicDesc": "ダイナミックポート転送用のローカルポートに SOCKS5 プロキシを作成します。", - "sameHost": "このホスト(ダイレクトトンネル)", - "endpointHost": "エンドポイントホスト", - "endpointPort": "エンドポイントポート", - "bindHost": "バインドホスト", - "sourcePort": "ソースポート", - "maxRetries": "最大再試行回数", - "retryIntervalS": "再試行間隔", - "autoStartLabel": "自動開始", - "autoStartDesc": "ホストがロードされたときに自動的にこのトンネルを接続する", - "tunnelConnecting": "トンネル接続中...", - "tunnelDisconnected": "トンネルが切断されました", - "failedToConnectTunnel": "接続に失敗しました", - "failedToDisconnectTunnel": "切断に失敗しました", - "dockerIntegration": "Docker 統合", - "enableDockerMonitor": "Docker を有効にする", - "enableDockerMonitorDesc": "Docker経由でこのホスト上のコンテナを監視・管理", - "enableFileManagerMonitor": "ファイルマネージャーを有効にする", - "enableFileManagerMonitorDesc": "SFTP経由でこのホスト上のファイルを参照して管理します", - "defaultPathLabel": "既定のパス", - "fileManagerPathHint": "ファイルマネージャがこのホストを起動したときに開くディレクトリ。", - "statusChecksLabel": "状態チェック", - "enableStatusChecks": "ステータスチェックを有効にする", - "enableStatusChecksDesc": "定期的にこのホストをpingして可用性を確認します", - "useGlobalInterval": "グローバルインターバルを使用", - "useGlobalIntervalDesc": "サーバ全体の状態チェック間隔で上書きする", - "checkIntervalS": "チェック間隔", - "checkIntervalDesc": "各接続Ping間の秒", - "metricsCollectionLabel": "メトリックコレクション", - "enableMetricsLabel": "メトリックを有効にする", - "enableMetricsDesc": "このホストからCPU、RAM、ディスク、およびネットワーク使用状況を収集します", - "useGlobalMetrics": "グローバルインターバルを使用", - "useGlobalMetricsDesc": "サーバー全体のメトリック間隔で上書き", - "metricsIntervalS": "メトリック間隔", - "metricsIntervalDesc2": "メトリックスナップショット間の秒", - "visibleWidgets": "表示されるウィジェット", - "cpuUsageLabel": "CPU 使用率", - "cpuUsageDesc": "CPUパーセント、負荷平均、スパークライングラフ", - "memoryLabel": "メモリ使用量", - "memoryDesc": "RAM使用量、スワップ、キャッシュ", - "storageLabel": "ディスクの使用量", - "storageDesc": "マウントポイントあたりのディスク使用率", - "networkLabel": "ネットワークインターフェース", - "networkDesc": "インターフェイスリストと帯域幅の設定", - "uptimeLabel": "稼働時間", - "uptimeDesc": "システムの稼働時間と起動時間", - "systemInfoLabel": "システム情報", - "systemInfoDesc": "OS、カーネル、ホスト名、アーキテクチャ", - "recentLoginsLabel": "最近のログイン", - "recentLoginsDesc": "ログインイベントに失敗しました", - "topProcessesLabel": "トッププロセス", - "topProcessesDesc": "PID, CPU, MEM%, コマンド", - "listeningPortsLabel": "ポートを聴いています", - "listeningPortsDesc": "プロセスと状態でポートを開く", - "firewallLabel": "ファイアウォール", - "firewallDesc": "ファイアウォール、AppArmor、SELinux の状態", - "quickActionsLabel": "クイックアクション", - "quickActionsToolbar": "クイックアクションは、ワンクリックコマンドの実行のためにサーバー統計ツールバーのボタンとして表示されます。", - "noQuickActions": "クイックアクションはまだありません。", - "buttonLabel": "ボタンのラベル", - "selectSnippetPlaceholder": "スニペットを選択...", - "addActionBtn": "アクションを追加", - "hostSharedSuccessfully": "ホストが正常に共有されました", - "failedToShareHost": "ホストの共有に失敗しました", - "accessRevoked": "アクセスが取り消されました", - "failedToRevokeAccess": "アクセスの取り消しに失敗しました", - "cancelBtn": "キャンセル", - "savingBtn": "保存中...", - "addHostBtn": "ホストを追加", - "hostUpdated": "ホストが更新されました", - "hostCreated": "ホストが作成されました", - "failedToSave": "ホストの保存に失敗しました", - "credentialUpdated": "認証情報が更新されました", - "credentialCreated": "認証情報が作成されました", - "failedToSaveCredential": "資格情報の保存に失敗しました", - "backToHosts": "ホストに戻る", - "backToCredentials": "資格情報に戻る", - "pinned": "ピン留めしました", - "noHostsFound": "ホストが見つかりません", - "tryDifferentTerm": "別の用語を試してください", - "addFirstHost": "最初のホストを追加して開始します", - "noCredentialsFound": "資格情報が見つかりません", - "addCredentialBtn": "証明書の追加", - "updateCredentialBtn": "証明書を更新", - "features": "特徴", - "noFolder": "(フォルダがありません)", - "deleteSelected": "削除", - "exitSelection": "選択を終了", - "importSkip": "インポート (スキップが既存)", - "importOverwrite": "インポート (上書き)", - "collapseBtn": "折りたたむ", - "importExportBtn": "インポート / エクスポート", - "hostStatusesRefreshed": "ホストステータスが更新されました", - "failedToRefreshHosts": "ホストの更新に失敗しました", - "movedHostTo": "{{host}} を \"{{folder}}\" に移動しました", - "failedToMoveHost": "ホストの移動に失敗しました", - "folderRenamedTo": "フォルダの名前を \"{{name}}\" に変更しました", - "deletedFolder": "フォルダ「{{name}}」を削除しました", - "failedToDeleteFolder": "フォルダの削除に失敗しました", - "deleteAllInFolder": "\"{{name}}\" のすべてのホストを削除しますか?元に戻すことはできません。", - "deletedHost": "{{name}} を削除しました", - "copiedToClipboard": "クリップボードにコピーしました", - "terminalUrlCopied": "端末の URL がコピーされました", - "fileManagerUrlCopied": "ファイルマネージャーのURLをコピーしました", - "tunnelUrlCopied": "トンネルURLをコピーしました", - "dockerUrlCopied": "Docker URL がコピーされました", - "serverStatsUrlCopied": "サーバー統計の URL がコピーされました", - "rdpUrlCopied": "RDP URL がコピーされました", - "vncUrlCopied": "VNC URLをコピーしました", - "telnetUrlCopied": "Telnet URL をコピーしました", - "remoteDesktopUrlCopied": "リモートデスクトップの URL をコピーしました", - "expandActions": "アクションを展開する", - "collapseActions": "折りたたみアクション", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "{{name}}宛てにマジックパケットが送信されました。", - "wakeOnLanError": "マジックパケットの送信に失敗しました", - "cloneHostAction": "ホストを複製", - "copyAddress": "アドレスをコピー", - "copyLink": "リンクをコピー", - "copyTerminalUrlAction": "端末の URL をコピー", - "copyFileManagerUrlAction": "ファイルマネージャーの URL をコピー", - "copyTunnelUrlAction": "トンネルURLをコピー", - "copyDockerUrlAction": "Docker URL をコピー", - "copyServerStatsUrlAction": "サーバー統計の URL をコピー", - "copyRdpUrlAction": "RDP URL をコピー", - "copyVncUrlAction": "VNC URLをコピー", - "copyTelnetUrlAction": "Telnet URL をコピー", - "copyRemoteDesktopUrlAction": "リモートデスクトップの URL をコピー", - "deleteCredentialConfirm": "資格情報 \"{{name}}\" を削除しますか?", - "deletedCredential": "{{name}} を削除しました", - "deploySSHKeyTitle": "SSH キーをデプロイします", - "deployingBtn": "デプロイ中...", - "deployBtn": "デプロイ", - "failedToDeployKey": "キーのデプロイに失敗しました", - "deleteHostsConfirm": "{{count}} ホスト{{plural}}を削除しますか? この操作は元に戻せません。", - "movedToRoot": "ルートに移動しました", - "failedToMoveHosts": "ホストの移動に失敗しました", - "enableTerminalFeature": "ターミナルを有効にする", - "disableTerminalFeature": "端末を無効にする", - "enableFilesFeature": "ファイルを有効にする", - "disableFilesFeature": "ファイルを無効にする", - "enableTunnelsFeature": "トンネルを有効化", - "disableTunnelsFeature": "トンネルを無効化", - "enableDockerFeature": "Docker を有効にする", - "disableDockerFeature": "Dockerを無効にする", - "addTagsPlaceholder": "タグを追加...", - "authDetails": "認証の詳細", - "credType": "タイプ", - "generateKeyPairDesc": "新しい鍵ペアを生成すると、秘密鍵と公開鍵の両方が自動的に入力されます。", - "generatingKey": "生成中...", - "generateLabel": "{{label}} を生成", - "uploadFileBtn": "ファイルをアップロード", - "keyPassphraseOptional": "キーのパスフレーズ(オプション)", - "sshPublicKeyOptional": "SSH Public Key (省略可能)", - "publicKeyGenerated": "公開鍵が生成されました", - "failedToGeneratePublicKey": "公開鍵の派生に失敗しました", - "publicKeyCopied": "公開鍵をコピーしました", - "keyPairGenerated": "{{label}} キーのペアが生成されました", - "failedToGenerateKeyPair": "キーペアの生成に失敗しました", - "searchHostsPlaceholder": "ホスト、アドレス、タグを検索…", - "searchCredentialsPlaceholder": "検索資格情報…", - "refreshBtn": "更新", - "addTag": "タグを追加...", - "deleteConfirmBtn": "削除", - "tunnelRequirementsText": "SSH サーバーは GatewayPorts はい、 AllowTcpForwarding はい、 PermitRootLogin が /etc/ssh/sshd_config に設定されている必要があります。", - "deleteHostConfirm": "「{{name}}」を削除しますか?", - "enableAtLeastOneProtocol": "上記の少なくとも1つのプロトコルを有効にして認証と接続設定を構成します。", - "keyPassphrase": "キーのパスワード", - "connectBtn": "接続する", - "disconnectBtn": "接続を解除", - "basicInformation": "基本情報", - "authDetailsSection": "認証の詳細", - "credTypeLabel": "タイプ", - "hostsTab": "ホスト", - "credentialsTab": "資格情報", - "selectMultiple": "複数を選択", - "selectHosts": "ホストを選択", - "connectionLabel": "接続", - "authenticationLabel": "認証", - "generateKeyPairTitle": "キーペアを生成", - "generateKeyPairDescription": "新しい鍵ペアを生成すると、秘密鍵と公開鍵の両方が自動的に入力されます。", - "generateFromPrivateKey": "プライベートキーから生成", - "refreshBtn2": "更新", - "exitSelectionTitle": "選択を終了", - "exportAll": "すべてエクスポート", - "addHostBtn2": "ホストを追加", - "addCredentialBtn2": "証明書の追加", - "checkingHostStatuses": "ホストのステータスを確認しています...", - "pinnedSection": "ピン留めしました", - "hostsExported": "ホストのエクスポートに成功しました", - "exportFailed": "ホストのエクスポートに失敗しました", - "sampleDownloaded": "サンプルファイルをダウンロードしました", - "failedToDeleteCredential2": "資格情報の削除に失敗しました", - "noFolderOption": "(フォルダがありません)", - "nSelected": "{{count}} が選択されました", - "featuresMenu": "特徴", - "moveMenu": "移動", - "cancelSelection": "キャンセル", - "deployDialogDesc": "ホストの authorized_key に {{name}} をデプロイします。", - "targetHostLabel": "ターゲットホスト", - "selectHostOption": "ホストを選択...", - "keyDeployedSuccess": "キーが正常にデプロイされました", - "failedToDeployKey2": "キーのデプロイに失敗しました", - "deletedCount": "削除された {{count}} ホスト", - "failedToDeleteCount": "{{count}} ホストの削除に失敗しました", - "duplicatedHost": "重複 \"{{name}}\"", - "failedToDuplicateHost": "ホストの複製に失敗しました", - "updatedCount": "更新された {{count}} ホスト", - "friendlyNameLabel": "フレンドリー名", - "descriptionLabel": "説明", - "loadingHost": "ホストを読み込んでいます...", - "loadingHosts": "ホストを読み込み中...", - "loadingCredentials": "資格情報を読み込んでいます...", - "noHostsYet": "まだホストがありません", - "noHostsMatchSearch": "検索条件に一致するホストがありません", - "hostNotFound": "ホストが見つかりません", - "searchHosts": "ホストを検索...", - "sortHosts": "ホストをソートする", - "sortDefault": "デフォルトオーダー", - "sortNameAsc": "名前(A~Z)", - "sortNameDesc": "名前(Z → A)", - "sortIpAsc": "IPアドレス(ASC)", - "sortIpDesc": "IPアドレス(説明)", - "sortOnlineFirst": "オンラインファースト", - "sortOfflineFirst": "オフライン優先", - "sortPinnedFirst": "ピン留めされた最初の", - "filterHosts": "ホストをフィルタリング", - "filterClearAll": "フィルターをクリア", - "filterStatusGroup": "状態", - "filterOnline": "オンライン", - "filterOffline": "オフライン", - "filterPinned": "ピン留め", - "filterAuthGroup": "認証タイプ", - "filterAuthPassword": "パスワード", - "filterAuthKey": "SSHキー", - "filterAuthCredential": "資格証明書", - "filterAuthNone": "なし", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "プロトコル", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特徴", - "filterFeatureTerminal": "ターミナル", - "filterFeatureFileManager": "ファイルマネージャー", - "filterFeatureTunnel": "トンネル", - "filterFeatureDocker": "ドッカー", - "filterTagsGroup": "タグ", - "shareHost": "ホストを共有", - "shareHostTitle": "シェア: {{name}}", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "このホストは共有を有効にするために資格情報を使用する必要があります。ホストを編集し、最初に資格情報を割り当ててください。" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "接続", - "authentication": "認証", - "connectionSettings": "接続設定", - "displaySettings": "表示設定", - "audioSettings": "音声設定", - "rdpPerformance": "RDP パフォーマンス", - "deviceRedirection": "端末のリダイレクト", - "session": "セッション", - "gateway": "ゲートウェイ", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", "clipboard": "Clipboard", - "sessionRecording": "セッション録音", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC 設定", - "terminalSettings": "端末の設定", - "rdpPort": "RDPポート", - "username": "ユーザー名", - "password": "パスワード", - "domain": "ドメイン", - "securityMode": "セキュリティモード", - "colorDepth": "色の深さ", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "高さ", + "height": "Height", "dpi": "DPI", - "resizeMethod": "リサイズ方法", - "clientName": "クライアント名", - "initialProgram": "初期プログラム", - "serverLayout": "サーバーレイアウト", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "ゲートウェイポート", - "gatewayUsername": "ゲートウェイユーザー名", - "gatewayPassword": "ゲートウェイパスワード", - "gatewayDomain": "ゲートウェイドメイン", - "remoteAppProgram": "RemoteApp プログラム", - "workingDirectory": "作業ディレクトリ", - "arguments": "引数", - "normalizeLineEndings": "行末を正規化", - "recordingPath": "録画パス", - "recordingName": "録音名", - "macAddress": "MACアドレス", - "broadcastAddress": "放送アドレス", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "待機時間", - "driveName": "ドライブ名", - "drivePath": "ドライブパス", - "ignoreCertificate": "証明書を無視", - "ignoreCertificateDesc": "自己署名証明書を持つホストへの接続を許可する", - "forceLossless": "無損失力", - "forceLosslessDesc": "強制ロスレス画像エンコーディング(高画質、高帯域幅)", - "disableAudio": "音声を無効にする", - "disableAudioDesc": "リモートセッションからすべてのオーディオをミュート", - "enableAudioInput": "オーディオ入力を有効にする (マイク)", - "enableAudioInputDesc": "ローカルマイクをリモートセッションに転送する", - "wallpaper": "壁紙", - "wallpaperDesc": "デスクトップの壁紙を表示 (パフォーマンスを向上させる)", - "theming": "テーマ", - "themingDesc": "視覚的なテーマとスタイルを有効にする", - "fontSmoothing": "フォントのスムージング", - "fontSmoothingDesc": "ClearTypeフォントレンダリングを有効にする", - "fullWindowDrag": "ウィンドウ全体のドラッグ", - "fullWindowDragDesc": "ドラッグ中にウィンドウの内容を表示する", - "desktopComposition": "デスクトップの構成", - "desktopCompositionDesc": "エアロガラス効果を有効にする", - "menuAnimations": "メニューアニメーション", - "menuAnimationsDesc": "メニューのフェードとスライドアニメーションを有効にする", - "disableBitmapCaching": "ビットマップキャッシュを無効にする", - "disableBitmapCachingDesc": "ビットマップキャッシュをオフにする (不具合が発生する可能性があります)", - "disableOffscreenCaching": "オフスクリーンキャッシュを無効にする", - "disableOffscreenCachingDesc": "画面キャッシュをオフにする", - "disableGlyphCaching": "グリフキャッシュを無効にする", - "disableGlyphCachingDesc": "グリフキャッシュをオフにする", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "RemoteFXグラフィックスパイプラインを使用", - "enablePrinting": "印刷を有効化", - "enablePrintingDesc": "ローカルプリンタをリモートセッションにリダイレクトする", - "enableDriveRedirection": "ドライブリダイレクトを有効にする", - "enableDriveRedirectionDesc": "リモートセッション内のドライブとしてローカルフォルダをマップ", - "createDrivePath": "ドライブパスの作成", - "createDrivePathDesc": "フォルダが存在しない場合は自動的に作成します", - "disableDownload": "ダウンロードを無効化", - "disableDownloadDesc": "リモートセッションからのファイルのダウンロードを防止", - "disableUpload": "アップロード無効", - "disableUploadDesc": "リモートセッションへのファイルのアップロードを防止", - "enableTouch": "タッチを有効化", - "enableTouchDesc": "タッチ入力の転送を有効にする", - "consoleSession": "コンソールセッション", - "consoleSessionDesc": "新しいセッションの代わりにコンソール(セッション0)に接続します", - "sendWolPacket": "WOLパケットを送信", - "sendWolPacketDesc": "接続する前にこのホストをスリープ解除するために魔法のパケットを送信する", - "disableCopy": "コピーを無効化", - "disableCopyDesc": "リモートセッションからのテキストのコピーを防止", - "disablePaste": "貼り付けを無効にする", - "disablePasteDesc": "リモートセッションにテキストを貼り付けないようにする", - "createPathIfMissing": "不足している場合にパスを作成", - "createPathIfMissingDesc": "録画ディレクトリを自動的に作成", - "excludeOutput": "アウトプットを除外", - "excludeOutputDesc": "画面出力を記録しない (メタデータのみ)", - "excludeMouse": "マウスを除外", - "excludeMouseDesc": "マウスの動きを記録しない", - "includeKeystrokes": "キーストロークを含める", - "includeKeystrokesDesc": "画面出力に加えて、RAWキーストロークを記録", - "vncPort": "VNC ポート", - "vncPassword": "VNC パスワード", - "vncUsernameOptional": "ユーザー名 (オプション)", - "vncLeaveBlank": "必要でない場合は空白のままにする", - "cursorMode": "カーソルモード", - "swapRedBlue": "赤/青の入れ替え", - "swapRedBlueDesc": "赤と青のカラーチャンネルを入れ替えます (色の問題を修正します)", - "readOnly": "読み取り専用", - "readOnlyDesc": "入力を送信せずにリモート画面を表示する", - "telnetPort": "Telnetポート", - "terminalType": "端末の種類", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "配色設定", - "backspaceKey": "バックスペースキー", - "saveHostFirst": "最初にホストを保存します。", - "sharingOptionsAfterSave": "ホストが保存されると、共有オプションが利用できます。", - "sharingLoadError": "共有データの読み込みに失敗しました。接続を確認して再度お試し下さい。", - "shareHostSection": "ホストを共有", - "shareWithUser": "ユーザーと共有", - "shareWithRole": "ロールで共有", - "selectUser": "ユーザーを選択", - "selectRole": "ロールを選択", - "selectUserOption": "ユーザーを選択...", - "selectRoleOption": "役割を選択...", - "permissionLevel": "権限レベル", - "expiresInHours": "有効期限 (時間)", - "noExpiryPlaceholder": "有効期限がない場合は空白のままにする", - "shareBtn": "共有", - "currentAccess": "現在のアクセス", - "typeHeader": "タイプ", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "アクセス許可", - "grantedByHeader": "許可された", - "expiresHeader": "期限切れ", - "noAccessEntries": "まだアクセスエントリがありません。", - "expiredLabel": "期限切れ", - "neverLabel": "一切なし", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "キャンセル", - "savingBtn": "保存中...", - "updateHostBtn": "ホストを更新", - "addHostBtn": "ホストを追加" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "ホスト、コマンド、設定を検索...", - "quickActions": "クイックアクション", - "hostManager": "ホスト マネージャー", - "hostManagerDesc": "ホストの管理、追加、編集", - "addNewHost": "新しいホストを追加", - "addNewHostDesc": "新しいホストを登録する", - "adminSettings": "管理者設定", - "adminSettingsDesc": "システム設定とユーザーの設定", - "userProfile": "ユーザープロフィール", - "userProfileDesc": "アカウントと設定の管理", - "addCredential": "証明書の追加", - "addCredentialDesc": "SSH キーまたはパスワードを保存", - "recentActivity": "最近のアクティビティ", - "serversAndHosts": "サーバーとホスト", - "noHostsFound": "{{search}} に一致するホストが見つかりませんでした", - "links": "リンク", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "選択", - "toggleWith": "表示/非表示" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "タブが割り当てられていません", - "focusedPane": "アクティブペイン" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "接続なし", - "noConnectionsDesc": "ターミナル、ファイルマネージャ、またはリモートデスクトップを開いて、ここに接続状況を確認してください。", - "connectedFor": "{{duration}} まで接続しました", - "connected": "接続済み", - "disconnected": "接続が切断されました", - "closeTab": "タブを閉じる", - "closeConnection": "密接なつながり", - "forgetTab": "忘れる", - "removeBackground": "取り除く", - "reconnect": "再接続", - "reopenTab": "再開", - "sectionOpen": "開ける", - "sectionBackground": "背景", - "backgroundDesc": "セッションは切断後30分間維持され、再接続が可能です。", - "persisted": "バックグラウンドで継続", - "expiresIn": "有効期限は {{duration}}です", - "search": "接続を検索...", - "noSearchResults": "検索条件に一致する接続はありません" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "{{type}} セッションに接続中...", - "connectionError": "接続エラー", - "connectionFailed": "接続に失敗しました", - "failedToConnect": "コネクショントークンの取得に失敗しました", - "hostNotFound": "ホストが見つかりません", - "noHostSelected": "ホストが選択されていません", - "reconnect": "再接続", - "retry": "再試行する", - "guacdUnavailable": "リモートデスクトップサービス (guacd) は利用できません。guacd が実行され、管理者の設定で正しく設定されていることを確認してください。", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (LockScreen)", - "winKey": "Windowsキー", - "ctrl": "(Ctrl)", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", "shift": "Shift", - "win": "勝利する", - "stickyActive": "{{key}} (ラッチされた - クリックしてリリース)", - "stickyInactive": "{{key}} (クリックでラッチ)", - "esc": "エスケープ", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "ホーム", - "end": "終了", + "home": "Home", + "end": "End", "pageUp": "Page Up", "pageDown": "Page Down", - "arrowUp": "上矢印キー", - "arrowDown": "下矢印キー", - "arrowLeft": "左矢印", - "arrowRight": "右矢印キー", - "fnToggle": "関数キー", - "reconnect": "セッションの再接続", - "collapse": "ツールバーを閉じる", - "expand": "ツールバーを展開", - "dragHandle": "ドラッグして位置を変更" + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "ホストに接続", - "clear": "クリア", - "paste": "貼り付け", - "reconnect": "再接続", - "connectionLost": "接続が切断されました", - "connected": "接続しました", - "clipboardWriteFailed": "クリップボードにコピーできませんでした。ページがHTTPSまたはlocalhostで配信されていることを確認してください。", - "clipboardReadFailed": "クリップボードからの読み取りに失敗しました。クリップボードのアクセス許可が付与されていることを確認してください。", - "clipboardHttpWarning": "貼り付けには HTTPS が必要です。Ctrl+Shift+V を使用するか、HTTPS 経由で Termix を提供します。", - "unknownError": "不明なエラーが発生しました", - "websocketError": "WebSocket 接続エラー", - "connecting": "接続中...", - "noHostSelected": "ホストが選択されていません", - "reconnecting": "再接続中... ({{attempt}}/{{max}})", - "reconnected": "再接続に成功しました", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", "tmuxSessionCreated": "tmux session created: {{name}}", "tmuxSessionAttached": "tmux session attached: {{name}}", - "tmuxUnavailable": "tmuxはリモートホストにインストールされておらず、標準シェルに戻ります", - "tmuxSessionPickerTitle": "tmuxセッション", - "tmuxSessionPickerDesc": "このホスト上に既存のtmuxセッションがあります。再アタッチまたは新しいセッションを作成する場合は1つ選択してください。", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", "tmuxWindows": "Windows", - "tmuxWindowCount": "{{count}} ウィンドウ", - "tmuxAttached": "添付されたクライアント", - "tmuxAttachedCount": "{{count}} が添付されました", - "tmuxLastActivity": "最後のアクティビティ", - "tmuxTimeJustNow": "ちょうど今", - "tmuxTimeMinutes": "{{count}}分前", - "tmuxTimeHours": "{{count}}時間前", - "tmuxTimeDays": "{{count}}日前", - "tmuxCreateNew": "新しいセッションを開始", - "tmuxCopyHint": "選択範囲を調整してEnterキーを押してクリップボードにコピー", - "tmuxDetach": "tmux セッションから切り離します", - "tmuxDetached": "tmux セッションから切り離しました", - "maxReconnectAttemptsReached": "最大再接続試行回数に達しました", - "closeTab": "閉じる", - "connectionTimeout": "接続タイムアウト", - "terminalTitle": "ターミナル - {{host}}", - "terminalWithPath": "ターミナル - {{host}}:{{path}}", - "runTitle": "実行中 {{command}} - {{host}}", - "totpRequired": "二段階認証が必要です", - "totpCodeLabel": "認証コード", - "totpVerify": "確認する", - "warpgateAuthRequired": "Warpgate認証が必要です", - "warpgateSecurityKey": "セキュリティキー", - "warpgateAuthUrl": "認証URL", - "warpgateOpenBrowser": "ブラウザで開く", - "warpgateContinue": "認証を完了しました", - "opksshAuthRequired": "OPKSSH 認証が必要です", - "opksshAuthDescription": "ブラウザで認証を完了してください。このセッションは24時間有効です。", - "opksshOpenBrowser": "ブラウザを開いて認証する", - "opksshWaitingForAuth": "ブラウザでの認証を待っています...", - "opksshAuthenticating": "認証を処理中...", - "opksshTimeout": "認証がタイムアウトしました。もう一度やり直してください。", - "opksshAuthFailed": "認証に失敗しました。資格情報を確認して、もう一度やり直してください。", - "opksshSignInWith": "{{provider}} でサインイン", - "sudoPasswordPopupTitle": "パスワードを入力してください。", - "websocketAbnormalClose": "予期せず接続が切断されました。リバースプロキシまたは SSL 設定に問題がある可能性があります。サーバログを確認してください。", - "connectionLogTitle": "接続ログ", - "connectionLogCopy": "ログをクリップボードにコピー", - "connectionLogEmpty": "接続ログはまだありません", - "connectionLogWaiting": "接続ログを待っています...", - "connectionLogCopied": "接続ログをクリップボードにコピーしました", - "connectionLogCopyFailed": "ログをクリップボードにコピーできませんでした", - "connectionRejected": "サーバーによって接続が拒否されました。認証とネットワーク設定を確認してください。", - "hostKeyRejected": "SSHホスト鍵の検証が拒否されました。接続がキャンセルされました。", - "sessionTakenOver": "セッションは別のタブで開かれました。再接続中..." + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "ホストが選択されていません", - "initializingEditor": "エディターを初期化しています...", - "file": "ファイル", - "folder": "フォルダ", - "uploadFile": "ファイルをアップロード", - "downloadFile": "ダウンロード", - "extractArchive": "アーカイブを抽出", - "extractingArchive": "{{name}} を抽出しています...", - "archiveExtractedSuccessfully": "{{name}} の抽出に成功しました", - "extractFailed": "抽出に失敗しました", - "compressFile": "ファイルを圧縮", - "compressFiles": "ファイルを圧縮", - "compressFilesDesc": "{{count}} アイテムをアーカイブに圧縮", - "archiveName": "アーカイブ名", - "enterArchiveName": "アーカイブ名を入力してください...", - "compressionFormat": "圧縮形式", - "selectedFiles": "選択したファイル", - "andMoreFiles": "さらに {{count}}...", - "compress": "圧縮", - "compressingFiles": "{{count}} アイテムを {{name}} に圧縮中...", - "filesCompressedSuccessfully": "{{name}} が正常に作成されました", - "compressFailed": "圧縮に失敗しました", - "edit": "編集", - "preview": "プレビュー", - "previous": "前", - "next": "次へ", - "pageXOfY": "ページ {{current}} / {{total}}", - "zoomOut": "ズームアウト", - "zoomIn": "拡大", - "newFile": "新規ファイル", - "newFolder": "新規フォルダ", - "rename": "名前の変更", - "uploading": "アップロード中...", - "uploadingFile": "{{name}} をアップロードしています...", - "fileName": "ファイル名", - "folderName": "フォルダ名", - "fileUploadedSuccessfully": "ファイル \"{{name}}\" は正常にアップロードされました", - "failedToUploadFile": "ファイルのアップロードに失敗しました", - "fileDownloadedSuccessfully": "ファイル \"{{name}}\" は正常にダウンロードされました", - "failedToDownloadFile": "ファイルのダウンロードに失敗しました", - "fileCreatedSuccessfully": "ファイル \"{{name}}\" が正常に作成されました", - "folderCreatedSuccessfully": "フォルダ \"{{name}}\" が正常に作成されました", - "failedToCreateItem": "アイテムの作成に失敗しました", - "operationFailed": "{{operation}} の操作に失敗しました {{name}}: {{error}}", - "failedToResolveSymlink": "シンボリックリンクの解決に失敗しました", - "itemsDeletedSuccessfully": "{{count}} アイテムが正常に削除されました", - "failedToDeleteItems": "アイテムの削除に失敗しました", - "sudoPasswordRequired": "管理者パスワードが必要です", - "enterSudoPassword": "この操作を続行するには、sudoパスワードを入力してください", - "sudoPassword": "Sudoのパスワード", - "sudoOperationFailed": "須藤操作に失敗しました", - "sudoAuthFailed": "Sudo認証に失敗しました", - "dragFilesToUpload": "アップロードするファイルをここにドロップ", - "emptyFolder": "このフォルダは空です", - "searchFiles": "ファイルを検索...", - "upload": "アップロード", - "selectHostToStart": "ファイル管理を開始するホストを選択してください", - "sshRequiredForFileManager": "ファイルマネージャーにはSSHが必要です。このホストはSSHを有効にしていません。", - "failedToConnect": "SSHに接続できませんでした", - "failedToLoadDirectory": "ディレクトリの読み込みに失敗しました", - "noSSHConnection": "SSH接続がありません", - "copy": "コピー", - "cut": "切り取り", - "paste": "貼り付け", - "copyPath": "パスをコピー", - "copyPaths": "パスをコピー", - "delete": "削除", - "properties": "プロパティー", - "refresh": "更新", - "downloadFiles": "{{count}} ファイルをブラウザにダウンロード", - "copyFiles": "{{count}} アイテムをコピー", - "cutFiles": "{{count}} アイテムを切り取り", - "deleteFiles": "{{count}} アイテムを削除", - "filesCopiedToClipboard": "{{count}} アイテムをクリップボードにコピーしました", - "filesCutToClipboard": "{{count}} アイテムがクリップボードにカットされました", - "pathCopiedToClipboard": "パスをクリップボードにコピーしました", - "pathsCopiedToClipboard": "{{count}} パスをクリップボードにコピーしました", - "failedToCopyPath": "クリップボードへのパスのコピーに失敗しました", - "movedItems": "{{count}} アイテムを移動しました", - "failedToDeleteItem": "アイテムの削除に失敗しました", - "itemRenamedSuccessfully": "{{type}} の名前が正常に変更されました", - "failedToRenameItem": "アイテムの名前を変更できませんでした", - "download": "ダウンロード", - "permissions": "アクセス許可", - "size": "サイズ", - "modified": "修正されました", - "path": "パス", - "confirmDelete": "{{name}} を削除してもよろしいですか?", - "permissionDenied": "権限がありません", - "serverError": "サーバーエラー", - "fileSavedSuccessfully": "ファイルを正常に保存しました", - "failedToSaveFile": "ファイルの保存に失敗しました", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", - "confirmDeleteMultipleItems": "{{count}} アイテムを完全に削除してもよろしいですか?", - "confirmDeleteMultipleItemsWithFolders": "{{count}} アイテムを完全に削除してもよろしいですか?フォルダとその内容が含まれています。", - "confirmDeleteFolder": "フォルダ「{{name}}」とそのすべての内容を完全に削除してもよろしいですか?", - "permanentDeleteWarning": "この操作は元に戻せません。アイテムはサーバーから完全に削除されます。", - "recent": "最近のもの", - "pinned": "ピン留めしました", - "folderShortcuts": "フォルダのショートカット", - "failedToReconnectSSH": "SSHセッションの再接続に失敗しました", - "openTerminalHere": "ここでターミナルを開く", - "run": "実行", - "openTerminalInFolder": "このフォルダでターミナルを開く", - "openTerminalInFileLocation": "ファイルの場所でターミナルを開く", - "runningFile": "実行中 - {{file}}", - "onlyRunExecutableFiles": "実行可能ファイルのみを実行できます", - "directories": "ディレクトリ", - "removedFromRecentFiles": "最近のファイルから \"{{name}}\" を削除しました", - "removeFailed": "削除に失敗しました", - "unpinnedSuccessfully": "\"{{name}}\" のピン留めを解除しました", - "unpinFailed": "ピン留めを解除できませんでした", - "removedShortcut": "ショートカット「{{name}}」を削除しました", - "removeShortcutFailed": "ショートカットの削除に失敗しました", - "clearedAllRecentFiles": "すべての最近のファイルを消去しました", - "clearFailed": "消去に失敗しました", - "removeFromRecentFiles": "最近使ったファイルから削除", - "clearAllRecentFiles": "すべての最近のファイルをクリア", - "unpinFile": "ファイルのピン留めを解除", - "removeShortcut": "ショートカットを削除", - "pinFile": "ファイルをピン留めする", - "addToShortcuts": "ショートカットに追加", - "pasteFailed": "貼り付けに失敗しました", - "noUndoableActions": "取り消せないアクションはありません", - "undoCopySuccess": "元に戻したコピー操作: 削除された {{count}} ファイルがコピーされました", - "undoCopyFailedDelete": "元に戻すに失敗しました: コピーされたファイルを削除できませんでした", - "undoCopyFailedNoInfo": "元に戻すに失敗しました: コピーされたファイル情報が見つかりませんでした", - "undoMoveSuccess": "元に戻した移動操作: {{count}} ファイルを元の場所に戻す", - "undoMoveFailedMove": "元に戻せませんでした: ファイルを元に戻すことができませんでした", - "undoMoveFailedNoInfo": "元に戻すに失敗しました: 移動したファイル情報が見つかりませんでした", - "undoDeleteNotSupported": "削除操作を元に戻すことはできません: ファイルがサーバーから完全に削除されました", - "undoTypeNotSupported": "サポートされていない操作タイプのundo", - "undoOperationFailed": "操作の取り消しに失敗しました", - "unknownError": "不明なエラー", - "confirm": "確認する", - "find": "検索...", - "replace": "置換", - "downloadInstead": "代わりにダウンロード", - "keyboardShortcuts": "キーボードショートカット", - "searchAndReplace": "検索と置換", - "editing": "編集", - "search": "検索", - "findNext": "次を検索", - "findPrevious": "前を検索", - "save": "保存", - "selectAll": "すべて選択", - "undo": "元に戻す", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", "redo": "Redo", - "moveLineUp": "行を上に移動", - "moveLineDown": "行を下に移動", - "toggleComment": "コメントの切り替え", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "画像の読み込みに失敗しました", - "startTyping": "入力を開始...", - "unknownSize": "不明なサイズ", - "fileIsEmpty": "ファイルが空です", - "largeFileWarning": "大きなファイルの警告", - "largeFileWarningDesc": "このファイルのサイズは {{size}} です。テキストとして開くとパフォーマンスの問題が発生する可能性があります。", - "fileNotFoundAndRemoved": "ファイル \"{{name}}\" が見つからず、最近/ピン留めされたファイルから削除されました", - "failedToLoadFile": "ファイルの読み込みに失敗しました: {{error}}", - "serverErrorOccurred": "サーバーエラーが発生しました。後でもう一度お試しください。", - "autoSaveFailed": "自動保存に失敗しました", - "fileAutoSaved": "ファイルを自動保存", - "moveFileFailed": "{{name}} の移動に失敗しました", - "moveOperationFailed": "移動に失敗しました", - "canOnlyCompareFiles": "2つのファイルのみ比較可能", - "comparingFiles": "ファイルの比較: {{file1}} と {{file2}}", - "dragFailed": "ドラッグ操作に失敗しました", - "filePinnedSuccessfully": "ファイル \"{{name}}\" のピン留めに成功しました", - "pinFileFailed": "ファイルのピン留めに失敗しました", - "fileUnpinnedSuccessfully": "ファイル \"{{name}}\" のピン留めが解除されました", - "unpinFileFailed": "ファイルのピン留めを解除できませんでした", - "shortcutAddedSuccessfully": "フォルダショートカット \"{{name}}\" が正常に追加されました", - "addShortcutFailed": "ショートカットの追加に失敗しました", - "operationCompletedSuccessfully": "{{operation}} {{count}} アイテムに成功しました", - "operationCompleted": "{{operation}} {{count}} アイテム", - "downloadFileSuccess": "ファイル {{name}} は正常にダウンロードされました", - "downloadFileFailed": "ダウンロードに失敗しました", - "moveTo": "{{name}} に移動", - "diffCompareWith": "{{name}} との比較の差", - "dragOutsideToDownload": "ウィンドウの外側にドラッグしてダウンロード ({{count}} ファイル)", - "newFolderDefault": "新規フォルダ", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "{{count}} アイテムを {{target}} に移動しました", - "move": "移動", - "searchInFile": "ファイル内で検索 (Ctrl+F)", - "showKeyboardShortcuts": "キーボードショートカットを表示", - "startWritingMarkdown": "マークダウンの内容を書き始めます...", - "loadingFileComparison": "ファイル比較を読み込み中...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "比較", - "sideBySide": "並べて表示", - "inline": "インライン", - "fileComparison": "ファイル比較: {{file1}} vs {{file2}}", - "fileTooLarge": "ファイルが大きすぎます: {{error}}", - "sshConnectionFailed": "SSH接続に失敗しました。 {{name}} ({{ip}}:{{port}}) への接続を確認してください。", - "loadFileFailed": "ファイルの読み込みに失敗しました: {{error}}", - "connecting": "接続中...", - "connectedSuccessfully": "接続に成功しました", - "totpVerificationFailed": "TOTP 認証に失敗しました", - "warpgateVerificationFailed": "Warpgate認証に失敗しました", - "authenticationFailed": "認証に失敗しました", - "verificationCodePrompt": "認証コード:", - "changePermissions": "権限の変更", - "currentPermissions": "現在の権限", - "owner": "所有者", - "group": "グループ", - "others": "その他", - "read": "既読にする", - "write": "書き込み", - "execute": "実行", - "permissionsChangedSuccessfully": "権限が正常に変更されました", - "failedToChangePermissions": "権限の変更に失敗しました", - "name": "名前", - "sortByName": "名前", - "sortByDate": "変更日", - "sortBySize": "サイズ", - "ascending": "昇順", - "descending": "降順", - "root": "ルート", - "new": "新規作成", - "sortBy": "並び替え", - "items": "アイテム", - "selected": "選択済み", - "editor": "エディター", - "octal": "8 進法", - "storage": "ストレージ", - "disk": "ディスク", - "used": "使用中", - "of": "/", - "toggleSidebar": "サイドバーの切り替え", - "cannotLoadPdf": "PDFを読み込めません", - "pdfLoadError": "このPDFファイルの読み込み中にエラーが発生しました。", - "loadingPdf": "PDFを読み込んでいます...", - "loadingPage": "ページを読み込んでいます..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "ホストにコピー…", - "moveToHost": "ホストに移動…", - "copyItemsToHost": "{{count}} 個のアイテムをホスト…にコピーします。", - "moveItemsToHost": "{{count}} 個のアイテムをホスト…に移動します。", - "noHostsConnected": "他に利用可能なファイルマネージャーホストはありません。", - "noHostsConnectedHint": "ホストマネージャで、ファイルマネージャが有効になっている別のSSHホストを追加します。", - "selectDestinationHost": "宛先ホストを選択してください", - "destinationPath": "目的地への経路", - "recentDestinations": "最近の旅行先", - "collapseRecentDestinations": "最近訪れた場所を折りたたむ", - "expandRecentDestinations": "最近の目的地を拡大", - "browseFolders": "保存先フォルダを参照する", - "browseDestination": "パスを参照または入力してください", - "confirmCopy": "コピー", - "confirmMove": "動く", - "transferring": "… を転送中", - "compressing": "… を圧縮中", - "extracting": "… を抽出中", - "transferringItems": "{{current}} 個の {{total}} アイテムを転送中…", - "transferSuccess": "転送完了", - "transferError": "転送に失敗しました", - "transferPartial": "転送は {{count}} 個のエラーで完了しました。", - "transferPartialHint": "転送できませんでした: {{paths}}", - "itemsSummary": "{{count}} アイテム", - "destMustBeDirectory": "宛先は複数アイテム転送用のディレクトリである必要があります。", - "selectThisFolder": "このフォルダーを選択してください", - "browsePathWillBeCreated": "このフォルダはまだ存在しません。転送が開始されると作成されます。", - "browsePathError": "宛先ホストでこのパスを開くことができませんでした。", - "goUp": "上へ", - "copyFolderToHost": "フォルダをホストにコピーします…", - "moveFolderToHost": "フォルダをホストに移動…", - "hostReady": "準備ができて", - "hostConnecting": "接続中…", - "hostDisconnected": "接続されていません", - "hostAuthRequired": "認証が必要です — まずこのホストでファイルマネージャを開いてください", - "hostConnectionFailed": "接続に失敗しました", - "metricsTitle": "転送時間", - "metricsPrepare": "宛先を準備します: {{duration}}", - "metricsCompress": "ソースで圧縮: {{duration}}", - "metricsHopSourceRead": "ソース → サーバー: {{throughput}}", - "metricsHopDestSftpWrite": "サーバー → 宛先 (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "サーバー → 宛先 (ローカル): {{throughput}}", - "metricsTransfer": "エンドツーエンド: {{throughput}} ({{duration}})", - "metricsExtract": "抽出先: {{duration}}", - "metricsSourceDelete": "ソースから削除: {{duration}}", - "metricsTotal": "合計: {{duration}}", - "progressCompressing": "ソースホスト上で圧縮中…", - "progressExtracting": "抽出先… で抽出中", - "progressTransferring": "データ転送中…", - "progressReconnecting": "再接続中…", - "parallelSegmentsLabel": "並行する乗り換えレーン", - "parallelSegmentsOption": "{{count}} 車線", - "parallelSegmentsHint": "大きなファイルは256MBずつに分割されます。複数のレーンはそれぞれ別の接続(複数の転送を開始するようなもの)を使用することで、全体の処理能力を高めています。", - "progressTotalSpeed": "{{speed}} 合計 ({{lanes}} 車線)", - "progressTransferringItems": "ファイル転送中({{current}} / {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} ファイル", - "sourceNotDeletedPartial": "ソースファイルは保持されます(部分転送)", - "jumpHostLimitation": "両方のホストはTermixサーバーからアクセス可能である必要があります。ホスト間の直接ルーティングはサポートされていません。", - "cancel": "キャンセル", - "methodLabel": "転送方法", - "methodAuto": "自動", - "methodTar": "Tarアーカイブ", - "methodItemSftp": "ファイル単位のSFTP", - "methodAutoHint": "ファイル数、サイズ、圧縮率に基づいて、tar形式またはファイル単位のSFTP形式を選択します。単一ファイルの場合は常にストリーミングSFTPを使用します。", - "methodTarHint": "ソース側で圧縮し、1つのアーカイブを転送し、宛先側で展開します。両方のUnixホストにtarコマンドが必要です。", - "methodItemSftpHint": "SFTP経由で各ファイルを個別に転送します。Windowsを含むすべてのホストで動作します。", - "methodPreviewLoading": "転送方法を計算中…", - "methodPreviewError": "転送方法をプレビューできませんでした。サーバーは起動時に転送方法を自動的に選択します。", - "methodPreviewWillUseTar": "使用する形式: Tarアーカイブ", - "methodPreviewWillUseItemSftp": "使用する方式: ファイル単位のSFTP", - "methodPreviewScanSummary": "{{fileCount}} ファイル、 {{totalSize}} 合計 (ソースホストでスキャン)。", - "methodItemSftpLimitation": "各ファイルは、単一ファイルのコピーとして同じSFTPストリームを順番に使用します。進行状況はすべてのファイルで合算されるため、大きなファイルの場合はプログレスバーの動きが遅くなります。", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "ファイル単位のSFTPを選択しました。", - "user_tar": "あなたはtarアーカイブを選択しました。", - "tar_unavailable": "片方または両方のホストでtarが利用できない場合は、代わりにファイル単位のSFTPが使用されます。", - "windows_host": "Windowsホストが関係しており、tarコマンドは使用されていません。", - "auto_multi_large": "自動: 圧縮可能なデータを含む大きなファイル ({{largestSize}}を含む複数のファイル - tar バンドルを 1 つの転送にまとめます。", - "auto_single_large_in_archive": "自動: このセットには 1 つの大きなファイル ({{largestSize}}) が含まれています — ファイルごとの SFTP。", - "auto_many_incompressible": "自動: ほとんどが非圧縮データ — ファイル単位のSFTP。", - "auto_many_files": "自動: 多数のファイル ({{fileCount}}) — tar はファイルごとのオーバーヘッドを削減します。", - "auto_default": "自動: このセットに対してファイルごとのSFTP接続を実行します。" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "キャンセル", - "progressCancelling": "… をキャンセルしています。", - "progressStalled": "停滞", - "resumedHint": "別のウィンドウで開始されたアクティブな転送に再接続しました。", - "transferCancelled": "転送がキャンセルされました", - "transferCancelledCopyHint": "宛先に一部のファイルが残る場合があります。", - "transferCancelledMoveHint": "宛先に一部ファイルが残る場合があります。元のファイルは削除されませんでした。", - "cleanupDestFiles": "清掃場所", - "cleanupDestFilesSuccess": "宛先から部分的なファイルを削除しました", - "cleanupDestFilesPartial": "一部ファイルが削除できませんでした", - "cleanupDestFilesNothing": "目的地では何も片付ける必要はありません", - "cleanupDestFilesError": "クリーンアップに失敗しました", - "retryTransfer": "リトライ", - "retryTransferError": "再試行に失敗しました", - "transferFailedRetryHint": "宛先に一部のデータが保存されました。接続が回復次第、再試行を再開します。" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "SSHトンネルがありません", - "createFirstTunnelMessage": "SSH トンネルはまだ作成されていません。ホストマネージャーでトンネル接続を設定して開始します。", - "connected": "接続しました", - "disconnected": "切断されました", - "connecting": "接続中...", - "error": "エラー", - "canceling": "キャンセル中...", - "connect": "接続する", - "disconnect": "接続を解除", - "cancel": "キャンセル", - "port": "ポート", - "localPort": "ローカルポート", - "remotePort": "リモートポート", - "currentHostPort": "現在のホスト ポート", - "endpointPort": "エンドポイントポート", - "bindIp": "ローカル IP", - "endpointSshConfig": "エンドポイントの SSH 設定", - "endpointSshHost": "エンドポイントの SSH ホスト", - "endpointSshHostPlaceholder": "設定されたホストを選択", - "endpointSshHostRequired": "各クライアント トンネルのエンドポイント SSH ホストを選択します。", - "attempt": "{{current}} の {{max}} 試行回数", - "nextRetryIn": "{{seconds}} 秒後に再度試行します。", - "clientTunnels": "クライアントトンネル", - "clientTunnel": "クライアントトンネル", - "addClientTunnel": "クライアントトンネルを追加", - "noClientTunnels": "このデスクトップにはクライアントトンネルが設定されていません。", - "tunnelName": "トンネル名", - "remoteHost": "リモートホスト", - "autoStart": "自動起動", - "clientAutoStartDesc": "このデスクトップ クライアントが開いて接続を維持したときに開始します。", - "clientManualStartDesc": "この行から開始と停止を使用します。Termixは自動的に開きません。", - "clientRemoteServerNote": "リモート転送では、エンドポイントの SSH サーバー上の AllowTcpForwarding と GatewayPorts が必要な場合があります。このデスクトップが切断されると、リモートポートが閉じます。", - "clientTunnelStarted": "クライアントトンネルを開始しました", - "clientTunnelStopped": "クライアントトンネルが停止しました", - "tunnelTestSucceeded": "トンネルテストが成功しました", - "tunnelTestFailed": "トンネルテストに失敗しました", - "localSaved": "クライアント・トンネルを保存しました", - "localSaveError": "ローカル クライアント トンネルの保存に失敗しました", - "invalidBindIp": "ローカルIPは有効なIPv4アドレスでなければなりません。", - "invalidLocalTargetIp": "ローカルターゲットIPは有効なIPv4アドレスでなければなりません。", - "invalidLocalPort": "ローカルポートは1から65535の間でなければなりません。", - "invalidRemotePort": "リモートポートは1から65535の間でなければなりません。", - "invalidLocalTargetPort": "ローカル ターゲット ポートは 1 から 65535 の範囲でなければなりません。", - "invalidEndpointPort": "エンドポイントポートは1から65535の間でなければなりません。", - "duplicateAutoStartBind": "{{bind}} を使用できるのは、自動起動クライアントトンネルが1つだけです。", - "manualControlError": "トンネル状態の更新に失敗しました。", - "active": "アクティブ", - "start": "開始", - "stop": "停止", - "test": "テスト", - "type": "トンネルタイプ", - "typeLocal": "ローカル (-L)", - "typeRemote": "リモート (-R)", - "typeDynamic": "ダイナミック (-D)", - "typeServerLocalDesc": "エンドポイントへの現在のホスト。", - "typeServerRemoteDesc": "現在のホストに戻るエンドポイント。", - "typeClientLocalDesc": "端末へのローカルコンピュータ。", - "typeClientRemoteDesc": "端末をローカルコンピュータに戻します。", - "typeClientDynamicDesc": "ローカルコンピュータ上でSOCKS。", - "typeDynamicDesc": "SOCKS5を転送 SSH経由でトラフィックを接続", - "forwardDescriptionServerLocal": "現在のホスト {{sourcePort}} → エンドポイント {{endpointPort}}。", - "forwardDescriptionServerRemote": "エンドポイント {{endpointPort}} → 現在のホスト {{sourcePort}}。", - "forwardDescriptionServerDynamic": "現在のホスト {{sourcePort}} のSOCKS。", - "forwardDescriptionClientLocal": "ローカル {{sourcePort}} → リモート {{endpointPort}}。", - "forwardDescriptionClientRemote": "リモート {{sourcePort}} → ローカル {{endpointPort}}。", - "forwardDescriptionClientDynamic": "ローカルポート {{sourcePort}}上のSOCKS。", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → {{endpoint}} 経由でSOCKS", - "autoNameClientLocal": "ローカル {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → ローカル {{localPort}}", - "autoNameClientDynamic": "{{localPort}} 経由のSOCKS {{endpoint}}", - "route": "ルート:", - "lastStarted": "最終開始", - "lastTested": "最終テスト", - "lastError": "最終エラー", - "maxRetries": "最大再試行回数", - "maxRetriesDescription": "再試行の最大回数", - "retryInterval": "再試行間隔 (秒)", - "retryIntervalDescription": "再試行まで待機する時間。", - "local": "ローカル", - "remote": "リモート", - "destination": "保存先", - "host": "ホスト", - "mode": "モード", - "noHostSelected": "ホストが選択されていません", - "working": "処理中..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "メモリ", - "disk": "ディスク", - "network": "ネットワーク", - "uptime": "稼働時間", - "processes": "プロセス", - "available": "利用可能です", - "free": "無料", - "connecting": "接続中...", - "connectionFailed": "サーバーへの接続に失敗しました", - "naCpus": "CPUなし", - "cpuCores_one": "{{count}} コア", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU 使用率", - "memoryUsage": "メモリ使用量", - "diskUsage": "ディスクの使用量", - "failedToFetchHostConfig": "ホスト設定の取得に失敗しました", - "serverOffline": "サーバーオフライン", - "cannotFetchMetrics": "オフラインサーバーからメトリックを取得できません", - "totpFailed": "TOTP 認証に失敗しました", - "noneAuthNotSupported": "サーバー統計は 'none' 認証タイプをサポートしていません。", - "load": "読み込み", - "systemInfo": "システム情報", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "オペレーティング システム", - "kernel": "カーネル", - "seconds": "秒", - "networkInterfaces": "ネットワークインターフェース", - "noInterfacesFound": "ネットワークインターフェースが見つかりません", - "noProcessesFound": "プロセスが見つかりません", - "loginStats": "SSHログインの統計", - "noRecentLoginData": "最近のログインデータがありません", - "executingQuickAction": "{{name}} を実行中...", - "quickActionSuccess": "{{name}} が正常に完了しました", - "quickActionFailed": "{{name}} に失敗しました", - "quickActionError": "{{name}} の実行に失敗しました", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "ポートを聴いています", + "title": "Listening Ports", "protocol": "Protocol", - "port": "ポート", - "address": "住所", - "process": "プロセス", - "noData": "リスニングポートのデータがありません" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "ファイアウォール", - "inactive": "非アクティブ", - "policy": "ポリシー", - "rules": "ルール", - "noData": "ファイアウォールデータがありません", - "action": "アクション", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "ポート", - "source": "ソース", - "anywhere": "どこでも" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "平均を読み込む", - "swap": "入れ替え", - "architecture": "建築", - "refresh": "更新", - "retry": "再試行する" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "セルフホスト型のSSHおよびリモートデスクトップ管理", - "loginTitle": "Termixにログイン", - "registerTitle": "アカウントを作成", - "forgotPassword": "パスワードをお忘れですか?", - "rememberMe": "30日間デバイスを記憶(TOTPを含む)", - "noAccount": "アカウントをお持ちでないですか?", - "hasAccount": "既にアカウントをお持ちですか?", - "twoFactorAuth": "2要素認証", - "enterCode": "認証コードを入力してください", - "backupCode": "またはバックアップコードを使用", - "verifyCode": "コードを確認", - "redirectingToApp": "アプリにリダイレクトしています...", - "sshAuthenticationRequired": "SSH 認証が必要です", - "sshNoKeyboardInteractive": "キーボード対話型認証が利用できません", - "sshAuthenticationFailed": "認証に失敗しました", - "sshAuthenticationTimeout": "認証のタイムアウト", - "sshNoKeyboardInteractiveDescription": "サーバーはキーボード対話認証をサポートしていません。パスワードまたはSSHキーを入力してください。", - "sshAuthFailedDescription": "指定された資格情報が正しくありません。有効な資格情報でもう一度お試しください。", - "sshTimeoutDescription": "認証試行がタイムアウトしました。もう一度やり直してください。", - "sshProvideCredentialsDescription": "このサーバーに接続するためにSSH認証情報を入力してください。", - "sshPasswordDescription": "SSH接続のパスワードを入力してください。", - "sshKeyPasswordDescription": "SSHキーが暗号化されている場合は、ここにパスフレーズを入力してください。", - "passphraseRequired": "パスフレーズが必要です", - "passphraseRequiredDescription": "SSHキーは暗号化されています。パスワードを入力してロックを解除してください。", - "back": "戻る", - "firstUser": "最初のユーザー", - "firstUserMessage": "あなたは初めてのユーザーであり、管理者になります。サイドバーのユーザードロップダウンから管理者の設定を確認できます。 これが間違いだと思われる場合は、docker logsを確認するか、GitHubのissueを作成してください。", - "external": "外部", - "loginWithExternal": "外部プロバイダーでログイン", - "loginWithExternalDesc": "設定された外部 ID プロバイダを使用してログイン", - "externalNotSupportedInElectron": "外部認証はまだ Electron アプリではサポートされていません。OIDC ログイン用のウェブバージョンを使用してください。", - "resetPasswordButton": "パスワードのリセット", - "sendResetCode": "リセットコードを送信", - "resetCodeDesc": "パスワードリセットコードを受け取るためにユーザー名を入力してください。コードはdockerコンテナログに記録されます。", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "コードを確認", - "enterResetCode": "ユーザーのdockerコンテナログから6桁のコードを入力してください:", - "newPassword": "新しいパスワード", - "confirmNewPassword": "パスワードの確認", - "enterNewPassword": "ユーザーの新しいパスワードを入力してください:", - "signUp": "新規登録", - "desktopApp": "デスクトップアプリ", - "loggingInToDesktopApp": "デスクトップアプリにログイン中", - "loadingServer": "サーバーを読み込み中...", - "dataLossWarning": "この方法でパスワードをリセットすると、保存した SSH ホスト、資格情報、およびその他の暗号化されたデータがすべて削除されます。 この操作は元に戻せません。パスワードを忘れてログインしていない場合にのみ使用してください。", - "authenticationDisabled": "認証が無効です", - "authenticationDisabledDesc": "すべての認証方法は現在無効です。管理者に問い合わせてください。", - "attemptsRemaining": "{{count}} 回の試行が残っています" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSHホストキーを確認", - "keyChangedWarning": "SSH ホストキーが変更されました", - "firstConnectionTitle": "初めてこのホストに接続しました", - "firstConnectionDescription": "このホストの信頼性を確立できません。指紋が期待されているものと一致することを確認してください。", - "keyChangedDescription": "このサーバーのホストキーは、最後の接続から変更されました。これはセキュリティ上の問題を示している可能性があります。", - "previousKey": "前のキー", - "newFingerprint": "新しいフィンガープリント", - "fingerprint": "フィンガープリント", - "verifyInstructions": "このホストを信頼している場合は、format@@0をクリックして続行し、今後の接続のためにこの指紋を保存します。", - "securityWarning": "セキュリティ警告", - "acceptAndContinue": "承認して続ける", - "acceptNewKey": "新しいキーを承認して続ける" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "データベースに接続できませんでした", - "unknownError": "不明なエラー", - "loginFailed": "ログインに失敗しました", - "failedPasswordReset": "パスワードのリセットに失敗しました", - "failedVerifyCode": "リセットコードの確認に失敗しました", - "failedCompleteReset": "パスワードのリセットに失敗しました", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "OIDCログインの開始に失敗しました", - "silentSigninOidcUnavailable": "サイレントサインインが要求されましたが、OIDCのログインはできません。", - "failedUserInfo": "ログイン後のユーザー情報の取得に失敗しました", - "oidcAuthFailed": "OIDC認証に失敗しました", - "invalidAuthUrl": "バックエンドから受信した認証URLが無効です", - "requiredField": "このフィールドは必須項目です", - "minLength": "最小の長さは {{min}}です", - "passwordMismatch": "パスワードが一致しません", - "passwordLoginDisabled": "ユーザー名/パスワードのログインは現在無効です", - "sessionExpired": "セッションの有効期限が切れました。もう一度ログインしてください", - "totpRateLimited": "レート制限: TOTP 認証の試行回数が多すぎます。後でもう一度お試しください。", - "totpRateLimitedWithTime": "レート制限: TOTP 認証の試行回数が多すぎます。もう一度試す前に {{time}} 秒お待ちください。", - "resetCodeRateLimited": "レート制限: 確認の試行回数が多すぎます。後でもう一度お試しください。", - "resetCodeRateLimitedWithTime": "レート制限: 確認の試行回数が多すぎます。 {{time}} 秒後に再試行してください。", - "authTokenSaveFailed": "認証トークンの保存に失敗しました", - "failedToLoadServer": "サーバーの読み込みに失敗しました" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "新しいアカウントの登録は現在管理者によって無効になっています。ログインするか、管理者に連絡してください。", - "userNotAllowed": "あなたのアカウントは登録する権限がありません。管理者に連絡してください。", - "databaseConnectionFailed": "データベースサーバーへの接続に失敗しました", - "resetCodeSent": "Dockerログに送信されたコードをリセット", - "codeVerified": "コードが正常に検証されました", - "passwordResetSuccess": "パスワードのリセットに成功しました", - "loginSuccess": "ログイン成功", - "registrationSuccess": "登録が完了しました" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "設定されたSSHホストを対象としたローカルデスクトップトンネル。", - "c2sTunnelPresets": "クライアントのトンネルプリセット", - "c2sTunnelPresetsDesc": "このデスクトップ クライアントのローカル トンネル リストを名前付きサーバー プリセットとして保存するか、このクライアントにプリセットをロードします。", - "c2sTunnelPresetsUnavailable": "クライアントトンネルプリセットはデスクトップクライアントでのみ使用できます。", - "c2sPresetName": "プリセット名", - "c2sPresetNamePlaceholder": "クライアントプリセット名", - "c2sPresetToLoad": "プリセットをロードする", - "c2sNoPresetSelected": "プリセットが選択されていません", - "c2sNoPresets": "プリセットは保存されていません", - "c2sLoadPreset": "読み込み", - "c2sCurrentLocalConfig": "{{count}} ローカルクライアントトンネルがこのデスクトップに設定されています。", - "c2sPresetSyncNote": "プリセットは明示的なスナップショットです。読み込むと、このデスクトップクライアントのローカルクライアントトンネルリストが置き換えられます。", - "c2sPresetSaved": "クライアントトンネルプリセットが保存されました", - "c2sPresetLoaded": "クライアントトンネルプリセットがローカルに読み込まれました", - "c2sPresetRenamed": "クライアントトンネルプリセットの名前を変更しました", - "c2sPresetDeleted": "クライアントトンネルプリセットが削除されました", - "c2sPresetLoadError": "クライアントトンネルプリセットの読み込みに失敗しました" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "言語", - "keyPassword": "キーパスワード", - "pastePrivateKey": "秘密鍵をここに貼り付けてください", - "localListenerHost": "127.0.0.1 (ローカルで聴く)", - "localTargetHost": "127.0.0.1 (このコンピュータのターゲット)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "パスワードを入力してください", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "ダッシュボード", - "loading": "ダッシュボードの読み込み中...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "サポート", + "support": "Support", "discord": "Discord", - "serverOverview": "サーバーの概要", - "version": "バージョン", - "upToDate": "日付まで", - "updateAvailable": "アップデートがあります", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "稼働時間", - "database": "データベース", - "healthy": "健康的", - "error": "エラー", - "totalHosts": "ホストの合計", - "totalTunnels": "合計トンネル", - "totalCredentials": "合計資格情報", - "recentActivity": "最近のアクティビティ", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "最近のアクティビティを読み込んでいます...", - "noRecentActivity": "最近のアクティビティはありません", - "quickActions": "クイックアクション", - "addHost": "ホストを追加", - "addCredential": "証明書の追加", - "adminSettings": "管理者設定", - "userProfile": "ユーザープロフィール", - "serverStats": "サーバーの統計", - "loadingServerStats": "サーバーの統計を読み込んでいます...", - "noServerData": "利用可能なサーバーデータがありません", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "ダッシュボードをカスタマイズ", - "dashboardSettings": "ダッシュボードの設定", - "enableDisableCards": "カードを有効/無効にする", - "resetLayout": "デフォルトにリセット", - "serverOverviewCard": "サーバーの概要", - "recentActivityCard": "最近のアクティビティ", - "networkGraphCard": "ネットワークグラフ", - "networkGraph": "ネットワークグラフ", - "quickActionsCard": "クイックアクション", - "serverStatsCard": "サーバーの統計", - "panelMain": "メイン", - "panelSide": "サイド", - "justNow": "ちょうど今" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "安定版", - "hostsOnline": "ホストオンライン", - "activeTunnels": "有効なトンネル", - "registerNewServer": "新しいサーバーを登録する", - "storeSshKeysOrPasswords": "SSH キーまたはパスワードを保存", - "manageUsersAndRoles": "ユーザーとロールの管理", - "manageYourAccount": "アカウントを管理する", - "hostStatus": "ホストステータス", - "noHostsConfigured": "ホストが設定されていません", - "online": "オンライン", - "offline": "オフライン", - "onlineLower": "オンライン", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "追加:", - "commandPalette": "コマンドパレット", - "done": "完了", - "editModeInstructions": "並び替えるためにカードをドラッグ・カラム仕切りをドラッグして列のサイズを変更・高さを変更するためにカードの下端をドラッグ・削除するためにゴミ箱を削除する", - "empty": "なし", - "clear": "クリア" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "ホストを追加", - "addGroup": "グループを追加", - "addLink": "リンクを追加", - "zoomIn": "拡大", - "zoomOut": "ズームアウト", - "resetView": "表示をリセット", - "selectHost": "ホストを選択", - "chooseHost": "ホストを選択してください...", - "parentGroup": "親グループ", - "noGroup": "グループなし", - "groupName": "グループ名", - "color": "色", - "source": "ソース", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "グループに移動", - "selectGroup": "グループを選択...", - "addConnection": "コネクションを追加", - "hostDetails": "ホストの詳細", - "removeFromGroup": "グループから削除", - "addHostHere": "ここにホストを追加", - "editGroup": "グループを編集", - "delete": "削除", - "add": "追加", - "create": "作成", - "move": "移動", - "connect": "接続する", - "createGroup": "グループを作成", - "selectSourcePlaceholder": "ソースを選択...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "無効なファイル", - "hostAlreadyExists": "ホストはすでにトポロジ内にあります", - "connectionExists": "接続は既に存在します", - "unknown": "不明", - "name": "名前", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "ステータス", - "failedToAddNode": "ノードの追加に失敗しました", - "sourceDifferentFromTarget": "ソースとターゲットが異なる必要があります", - "exportJSON": "JSONをエクスポート", - "importJSON": "JSON をインポート", - "terminal": "ターミナル", - "fileManager": "ファイルマネージャー", - "tunnel": "トンネル", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "サーバーの統計", - "noNodes": "ノードはまだありません" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Dockerはこのホストで有効になっていません", - "validating": "Dockerを検証中...", - "connecting": "接続中...", - "error": "エラー", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Dockerへの接続に失敗しました", - "containerStarted": "コンテナ {{name}} 開始", - "failedToStartContainer": "コンテナ {{name}}の開始に失敗しました", - "containerStopped": "コンテナ {{name}} が停止しました", - "failedToStopContainer": "コンテナ {{name}}の停止に失敗しました", - "containerRestarted": "コンテナ {{name}} が再起動しました", - "failedToRestartContainer": "コンテナ {{name}} の再起動に失敗しました", - "containerPaused": "コンテナ {{name}} は一時停止しました", - "containerUnpaused": "コンテナ {{name}} の一時停止が解除されました", - "failedToTogglePauseContainer": "コンテナ {{name}}の一時停止状態の切り替えに失敗しました", - "containerRemoved": "コンテナ {{name}} が削除されました", - "failedToRemoveContainer": "コンテナ {{name}}の削除に失敗しました", - "image": "画像", - "ports": "ポート", - "noPorts": "ポートがありません", - "start": "開始", - "confirmRemoveContainer": "コンテナ '{{name}}'を削除してもよろしいですか? この操作は元に戻せません。", - "runningContainerWarning": "警告: このコンテナは現在実行中です。削除するとコンテナが最初に停止します。", - "loadingContainers": "コンテナを読み込み中...", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", "manager": "Docker Manager", - "autoRefresh": "自動更新", - "timestamps": "タイムスタンプ", - "lines": "行", - "filterLogs": "ログのフィルタリング...", - "refresh": "更新", - "download": "ダウンロード", - "clear": "クリア", - "logsDownloaded": "ログのダウンロードに成功しました", - "last50": "過去 50", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", "last100": "Last 100", - "last500": "直近500", - "last1000": "過去 1000 個", - "allLogs": "すべてのログ", - "noLogsMatching": "\"{{query}}\" に一致するログはありません", - "noLogsAvailable": "利用可能なログがありません", - "noContainersFound": "コンテナが見つかりません", - "noContainersFoundHint": "このホストではDockerコンテナは利用できません", - "searchPlaceholder": "コンテナを検索...", - "allStatuses": "すべてのステータス", - "stateRunning": "実行中", - "statePaused": "一時停止", - "stateExited": "終了", - "stateRestarting": "再起動中", - "noContainersMatchFilters": "フィルターに一致するコンテナはありません", - "noContainersMatchFiltersHint": "検索条件またはフィルター条件を調整してみてください", - "failedToFetchStats": "コンテナの統計情報の取得に失敗しました", - "containerNotRunning": "コンテナが実行されていません", - "startContainerToViewStats": "統計情報を表示するためにコンテナを起動します", - "loadingStats": "統計情報を読み込み中...", - "errorLoadingStats": "統計情報の読み込みに失敗しました", - "noStatsAvailable": "利用可能な統計情報がありません", - "cpuUsage": "CPU 使用率", - "current": "現在", - "memoryUsage": "メモリ使用量", - "networkIo": "ネットワーク I/O", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "出力", - "blockIo": "ブロックI/O", - "read": "既読にする", - "write": "書き込み", - "pids": "PID", - "containerInformation": "コンテナ情報", - "name": "名前", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "都道府県:", - "containerMustBeRunning": "コンソールにアクセスするにはコンテナが実行されている必要があります", - "verificationCodePrompt": "認証コードを入力してください", - "totpVerificationFailed": "TOTP 認証に失敗しました。もう一度やり直してください。", - "warpgateVerificationFailed": "Warpgate認証に失敗しました。もう一度やり直してください。", - "connectedTo": "{{containerName}} に接続しました", - "disconnected": "切断されました", - "consoleError": "コンソールエラー", - "errorMessage": "エラー: {{message}}", - "failedToConnect": "コンテナに接続できませんでした", - "console": "コンソール", - "selectShell": "シェルを選択", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", "ash": "ash", - "connect": "接続する", - "disconnect": "接続を解除", - "notConnected": "接続していません", - "clickToConnect": "シェルセッションを開始するには接続をクリックしてください", - "connectingTo": "{{containerName}} に接続中...", - "containerNotFound": "コンテナが見つかりません", - "backToList": "リストに戻る", - "logs": "ログ", - "stats": "統計", - "consoleTab": "コンソール", - "startContainerToAccess": "コンソールにアクセスするためにコンテナを起動します" + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "全般", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "ユーザー", - "sectionSessions": "セッション", - "sectionRoles": "ロール", - "sectionDatabase": "データベース", - "sectionApiKeys": "API キー", - "allowRegistration": "ユーザー登録を許可する", - "allowRegistrationDesc": "新規ユーザーに自己登録を許可する", - "allowPasswordLogin": "パスワードログインを許可する", - "allowPasswordLoginDesc": "ユーザー名/パスワードログイン", - "oidcAutoProvision": "OIDC 自動提供", - "oidcAutoProvisionDesc": "登録が無効な場合でも、OIDCユーザーのアカウントを自動作成する", - "allowPasswordReset": "パスワードのリセットを許可", - "allowPasswordResetDesc": "Docker ログ経由でコードをリセット", - "sessionTimeout": "セッションタイムアウト", - "hours": "時間", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "モニタリングのデフォルト", - "statusCheck": "状態チェック", - "metrics": "メトリック", - "sec": "秒", - "logLevel": "ログレベル", - "enableGuacamole": "Guacamoleを有効にする", - "enableGuacamoleDesc": "RDP/VNC リモートデスクトップ", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "SSO 用の OpenID 接続を構成します。* とマークされたフィールドは必須です。", - "oidcClientId": "クライアント ID", - "oidcClientSecret": "クライアントシークレット", - "oidcAuthUrl": "認証URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", "oidcIssuerUrl": "Issuer URL", - "oidcTokenUrl": "トークンURL", - "oidcUserIdentifier": "ユーザー識別子パス", - "oidcDisplayName": "表示名パス", - "oidcScopes": "スコープ", - "oidcUserinfoUrl": "ユーザー情報 URL を上書きする", - "oidcAllowedUsers": "許可されたユーザー", - "oidcAllowedUsersDesc": "1行あたり1つのメールアドレスです。すべて許可するには空のままにしてください。", - "removeOidc": "削除", - "usersCount": "{{count}} ユーザー", - "createUser": "作成", - "newRole": "新しいロール", - "roleName": "名前", - "roleDisplayName": "表示名", - "roleDescription": "説明", - "rolesCount": "{{count}} ロール", - "createRole": "作成", - "creating": "作成中...", - "exportDatabase": "データベースをエクスポート", - "exportDatabaseDesc": "すべてのホスト、資格情報、設定のバックアップをダウンロードする", - "export": "エクスポート", - "exporting": "エクスポート中...", - "importDatabase": "データベースをインポート", - "importDatabaseDesc": ".sqliteバックアップファイルから復元", - "importDatabaseSelected": "選択済: {{name}}", - "selectFile": "ファイルを選択", - "changeFile": "変更", - "import": "インポート", - "importing": "インポート中...", - "apiKeysCount": "{{count}} キー", - "newApiKey": "新しいAPIキー", - "apiKeyCreatedWarning": "キーが作成されました - 今すぐコピーしてください。再び表示されません。", - "apiKeyName": "名前", - "apiKeyUser": "ユーザー", - "apiKeySelectUser": "ユーザーを選択...", - "apiKeyExpiresAt": "有効期限", - "createKey": "キーを作成", - "apiKeyNoExpiry": "有効期限なし", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "デュアル認証", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "ローカル", - "adminStatusAdministrator": "管理者", - "adminStatusRegularUser": "レギュラーユーザー", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "SYSの", - "customBadge": "カスタム", - "youBadge": "あなた", - "sessionsActive": "{{count}} 有効", - "sessionActive": "アクティブ: {{time}}", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "すべて", - "revokeAllSessionsSuccess": "ユーザーのすべてのセッションが取り消されました", - "revokeAllSessionsFailed": "セッションの取り消しに失敗しました", - "revokeSessionFailed": "セッションの取り消しに失敗しました", - "addRole": "役割を追加", - "noCustomRoles": "カスタムロールが定義されていません", - "removeRoleFailed": "ロールの削除に失敗しました", - "assignRoleFailed": "ロールの割り当てに失敗しました", - "deleteRoleFailed": "ロールの削除に失敗しました", - "userAdminAccess": "管理者", - "userAdminAccessDesc": "すべての管理者設定へのフルアクセス", - "userRoles": "ロール", - "revokeAllUserSessions": "すべてのセッションを取り消す", - "revokeAllUserSessionsDesc": "すべてのデバイスで強制的に再ログインする", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "この利用者を削除することは永久です。", - "deleteUser": "{{username}} を削除", - "deleting": "削除中...", - "deleteUserFailed": "ユーザーの削除に失敗しました", - "deleteUserSuccess": "ユーザー \"{{username}}\" が削除されました", - "deleteRoleSuccess": "ロール \"{{name}}\" が削除されました", - "revokeKeySuccess": "キー \"{{name}}\" が取り消されました", - "revokeKeyFailed": "キーの取り消しに失敗しました", - "copiedToClipboard": "クリップボードにコピーしました", - "done": "完了", - "createUserTitle": "ユーザーを作成", - "createUserDesc": "新しいローカルアカウントを作成します。", - "createUserUsername": "ユーザー名", - "createUserPassword": "パスワード", - "createUserPasswordHint": "最低6文字です。", - "createUserEnterUsername": "ユーザー名を入力してください", - "createUserEnterPassword": "パスワードを入力", - "createUserSubmit": "ユーザーを作成", - "editUserTitle": "ユーザーを管理: {{username}}", - "editUserDesc": "ロール、管理者ステータス、セッション、アカウント設定を編集します。", - "editUserUsername": "ユーザー名", - "editUserAuthType": "認証タイプ", - "editUserAdminStatus": "管理者ステータス", - "editUserUserId": "ユーザー ID", - "linkAccountTitle": "OIDCをパスワードアカウントにリンクする", - "linkAccountDesc": "OIDC アカウント {{username}} と既存のローカル アカウントをマージします。", - "linkAccountWarningTitle": "次の操作を行います:", - "linkAccountEffect1": "OIDC専用アカウントを削除する", - "linkAccountEffect2": "ターゲットアカウントにOIDCログインを追加", - "linkAccountEffect3": "OIDCとパスワードの両方のログインを許可する", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "リンクするローカルアカウントのユーザー名を入力してください", - "linkAccounts": "アカウントをリンク", - "linkAccountSuccess": "「{{username}}」にリンクされたOIDCアカウント", - "linkAccountFailed": "OIDCアカウントのリンクに失敗しました", - "linkAccountInProgress": "リンク中...", - "saving": "保存中...", - "updateRegistrationFailed": "登録設定の更新に失敗しました", - "updatePasswordLoginFailed": "パスワードのログイン設定を更新できませんでした", - "updateOidcAutoProvisionFailed": "OIDC 自動プロビジョニング設定を更新できませんでした。", - "updatePasswordResetFailed": "パスワードリセット設定の更新に失敗しました", - "sessionTimeoutRange2": "セッションのタイムアウトは1〜720時間の間でなければなりません", - "sessionTimeoutSaved": "セッションタイムアウトを保存しました", - "sessionTimeoutSaveFailed": "セッションのタイムアウトを保存できませんでした", - "monitoringIntervalInvalid": "間隔の値が無効です", - "monitoringSaved": "モニタリング設定が保存されました", - "monitoringSaveFailed": "監視設定の保存に失敗しました", - "guacamoleSaved": "Guacamoleの設定を保存しました", - "guacamoleSaveFailed": "Guacamole設定の保存に失敗しました", - "guacamoleUpdateFailed": "Guacamoleの設定を更新できませんでした", - "logLevelUpdateFailed": "ログレベルの更新に失敗しました", - "oidcSaved": "OIDC設定を保存しました", - "oidcSaveFailed": "OIDC設定の保存に失敗しました", - "oidcRemoved": "OIDC設定が削除されました", - "oidcRemoveFailed": "OIDC設定の削除に失敗しました", - "createUserRequired": "ユーザー名とパスワードが必要です", - "createUserPasswordTooShort": "パスワードは6文字以上でなければなりません", - "createUserSuccess": "ユーザー \"{{username}}\" が作成されました", - "createUserFailed": "ユーザーの作成に失敗しました", - "updateAdminStatusFailed": "管理者ステータスの更新に失敗しました", - "allSessionsRevoked": "すべてのセッションが取り消されました", - "revokeSessionsFailed": "セッションの取り消しに失敗しました", - "createRoleRequired": "名前と表示名が必要です", - "createRoleSuccess": "ロール \"{{name}}\" が作成されました", - "createRoleFailed": "ロールの作成に失敗しました", - "apiKeyNameRequired": "キー名が必要です", - "apiKeyUserRequired": "ユーザー ID が必要です", - "apiKeyCreatedSuccess": "API キー \"{{name}}\" が作成されました", - "apiKeyCreateFailed": "API キーの作成に失敗しました", - "exportSuccess": "データベースのエクスポートに成功しました", - "exportFailed": "データベースのエクスポートに失敗しました", - "importSelectFile": "最初にファイルを選択してください", - "importCompleted": "インポートが完了しました: {{total}} アイテムがインポートされ、 {{skipped}} がスキップされました", - "importFailed": "インポートに失敗しました: {{error}}", - "importError": "データベースのインポートに失敗しました" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "ホスト", - "hostPlaceholder": "192.168.1.1またはexample.com", - "portLabel": "ポート", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "ユーザー名", - "usernamePlaceholder": "ユーザー名", - "authLabel": "認証", - "passwordLabel": "パスワード", - "passwordPlaceholder": "パスワード", - "privateKeyLabel": "プライベートキー", - "privateKeyPlaceholder": "秘密キーを貼り付け...", - "credentialLabel": "資格情報", - "credentialPlaceholder": "保存された資格情報を選択", - "connectToTerminal": "ターミナルに接続", - "connectToFiles": "ファイルに接続" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "端末が選択されていません", - "noTerminalSelectedHint": "SSH ターミナルタブを開いてコマンド履歴を表示します", - "searchPlaceholder": "履歴を検索...", - "clearAll": "すべてクリア", - "noHistoryEntries": "履歴エントリがありません", - "trackingDisabled": "履歴の追跡は無効です", - "trackingDisabledHint": "コマンドを記録するには、プロファイル設定で有効にしてください。" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "キー録音", - "recordToTerminals": "端末に記録", - "selectAll": "すべて", - "selectNone": "なし", - "noTerminalTabsOpen": "ターミナルタブが開いていません", - "selectTerminalsAbove": "上の端末を選択", - "broadcastInputPlaceholder": "キーストロークをブロードキャストするにはここに入力してください...", - "stopRecording": "録画を停止", - "startRecording": "録画開始", - "settingsTitle": "設定", - "enableRightClickCopyPaste": "右クリックでコピー/貼り付け" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "レイアウト", - "selectLayoutAbove": "上のレイアウトを選択してください", - "selectLayoutHint": "表示するペインの数を選択", - "panesTitle": "ペイン", - "openTabsTitle": "タブを開く", - "dragTabsHint": "タブを上のペインにドラッグするか、クイック割り当てを使用してください。", - "dropHere": "ここにドロップ", - "emptyPane": "なし", - "dashboard": "ダッシュボード", - "clearSplitScreen": "分割画面をクリア", - "quickAssign": "クイック割り当て", - "alreadyAssigned": "ペイン {{index}}", - "splitTab": "分割タブ", - "addToSplit": "分割に追加", - "removeFromSplit": "分割から削除", - "assignToPane": "ペインに割り当てる" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "スニペットを作成", - "createSnippetDescription": "クイック実行のための新しいコマンドスニペットを作成します", - "nameLabel": "名前", - "namePlaceholder": "例えば、Nginx を再起動", - "descriptionLabel": "説明", - "descriptionPlaceholder": "オプションの説明", - "optional": "省略可能", - "folderLabel": "フォルダ", - "noFolder": "フォルダがありません(未分類)", - "commandLabel": "(Command)", - "commandPlaceholder": "例えば、sudo systemctl restart nginx", - "cancel": "キャンセル", - "createSnippetButton": "スニペットを作成", - "createFolderTitle": "フォルダを作成", - "createFolderDescription": "スニペットをフォルダーに整理する", - "folderNameLabel": "フォルダ名", - "folderNamePlaceholder": "例:システムコマンド、Docker スクリプト", - "folderColorLabel": "フォルダの色", - "folderIconLabel": "フォルダアイコン", - "previewLabel": "プレビュー", - "folderNameFallback": "フォルダ名", - "createFolderButton": "フォルダを作成", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "すべて", - "selectNone": "なし", - "noTerminalTabsOpen": "ターミナルタブが開いていません", - "searchPlaceholder": "スニペットを検索...", - "newSnippet": "スニペットを新規作成", - "newFolder": "新規フォルダ", - "run": "実行", - "noSnippetsInFolder": "このフォルダにスニペットがありません", - "uncategorized": "未分類", - "editSnippetTitle": "スニペットを編集", - "editSnippetDescription": "このコマンドスニペットを更新", - "saveSnippetButton": "変更を保存", - "createSuccess": "スニペットの作成に成功しました", - "createFailed": "スニペットの作成に失敗しました", - "updateSuccess": "スニペットが正常に更新されました", - "updateFailed": "スニペットの更新に失敗しました", - "deleteFailed": "スニペットの削除に失敗しました", - "folderCreateSuccess": "フォルダが正常に作成されました", - "folderCreateFailed": "フォルダの作成に失敗しました", - "editFolderTitle": "フォルダを編集", - "editFolderDescription": "このフォルダの外観を変更または名前を変更します", - "saveFolderButton": "変更を保存", - "editFolder": "フォルダを編集", - "deleteFolder": "フォルダを削除", - "folderDeleteSuccess": "フォルダ \"{{name}}\" が削除されました", - "folderDeleteFailed": "フォルダの削除に失敗しました", - "folderEditSuccess": "フォルダが正常に更新されました", - "folderEditFailed": "フォルダの更新に失敗しました", - "confirmRunMessage": "「{{name}}」を実行しますか?", - "confirmRunButton": "走る", - "runSuccess": "{{name}}ターミナルで \" {{count}} \" を実行しました", - "copySuccess": "\"{{name}}\"をクリップボードにコピーしました", - "shareTitle": "スニペットを共有", - "shareUser": "ユーザー", - "shareRole": "ロール", - "selectUser": "ユーザーを選択...", - "selectRole": "役割を選択...", - "shareSuccess": "スニペットの共有に成功しました", - "shareFailed": "スニペットを共有できませんでした", - "revokeSuccess": "アクセスが取り消されました", - "revokeFailed": "アクセスの取り消しに失敗しました", - "currentAccess": "現在のアクセス", - "shareLoadError": "共有データの読み込みに失敗しました", - "loading": "読み込み中...", - "close": "閉じる", - "reorderFailed": "スニペットの順序を保存できませんでした" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "アカウント", - "sectionAppearance": "外観", - "sectionSecurity": "セキュリティ", - "sectionApiKeys": "API キー", - "sectionC2sTunnels": "C2S トンネル", - "usernameLabel": "ユーザー名", - "roleLabel": "ロール", - "roleAdministrator": "管理者", - "authMethodLabel": "認証方法", - "authMethodLocal": "ローカル", - "twoFaLabel": "二段階認証(2FA)", - "twoFaOn": "オン", - "twoFaOff": "オフ", - "versionLabel": "バージョン", - "deleteAccount": "アカウントを削除", - "deleteAccountDescription": "アカウントを完全に削除する", - "deleteButton": "削除", - "deleteAccountPermanent": "この操作は永久的で、元に戻すことはできません。", - "deleteAccountWarning": "すべてのセッション、ホスト、資格情報、および設定は完全に削除されます。", - "confirmPasswordDeletePlaceholder": "パスワードを入力して確認してください", - "languageLabel": "言語", - "themeLabel": "テーマ", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "アクセントカラー", - "settingsTerminal": "ターミナル", - "commandAutocomplete": "コマンドオートコンプリート", - "commandAutocompleteDesc": "入力中にオートコンプリートを表示", - "historyTracking": "履歴の追跡", - "historyTrackingDesc": "端末のコマンドを追跡する", - "syntaxHighlighting": "構文のハイライト", - "syntaxHighlightingDesc": "端末の出力を強調表示する", - "commandPalette": "コマンドパレット", - "commandPaletteDesc": "キーボードショートカットを有効にする", - "reopenTabsOnLogin": "ログイン時にタブを再度開く", - "reopenTabsOnLoginDesc": "別のデバイスからログインしたりページを更新したりしても、開いているタブを復元します。", - "confirmTabClose": "タブを閉じる", - "confirmTabCloseDesc": "ターミナルタブを閉じる前に確認する", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "ホストタグの表示", - "showHostTagsDesc": "ホストリストにタグを表示する", - "hostTrayOnClick": "ホストアクションを展開するにはクリックしてください", - "hostTrayOnClickDesc": "接続ボタンを常に表示する。管理オプションを展開するには、マウスオーバーではなくクリックする。", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "左サイドバーのアプリレールを、マウスオーバー時ではなく常に展開した状態にする。", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", "settingsSnippets": "Snippets", - "foldersCollapsed": "折りたたまれたフォルダ", - "foldersCollapsedDesc": "デフォルトでフォルダを閉じる", - "confirmExecution": "実行を確認", - "confirmExecutionDesc": "スニペットを実行する前に確認", - "settingsUpdates": "更新", - "disableUpdateChecks": "更新チェックを無効にする", - "disableUpdateChecksDesc": "更新の確認を停止する", - "totpAuthenticator": "TOTP 認証システム", - "totpEnabled": "二段階認証は有効です", - "totpDisabled": "追加のログインセキュリティを追加", - "disable": "無効", - "enable": "有効にする", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "QRコードをスキャンするか、認証アプリにシークレットを入力し、6桁のコードを入力してください", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "確認する", - "changePassword": "パスワードの変更", - "currentPasswordLabel": "現在のパスワード", - "currentPasswordPlaceholder": "現在のパスワード", - "newPasswordLabel": "新しいパスワード", - "newPasswordPlaceholder": "新しいパスワード", - "confirmPasswordLabel": "新しいパスワードの確認", - "confirmPasswordPlaceholder": "新しいパスワードを確認", - "updatePassword": "パスワードの更新", - "createApiKeyTitle": "API キーを作成", - "createApiKeyDescription": "プログラムアクセス用の新しい API キーを生成します。", - "apiKeyNameLabel": "名前", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "省略可能", - "cancel": "キャンセル", - "createKey": "キーを作成", - "apiKeyCount": "{{count}} キー", - "newKey": "新しいキー", - "noApiKeys": "API キーはまだありません。", - "apiKeyActive": "アクティブ", - "apiKeyUsageHint": "にキーを含める", - "apiKeyUsageHintHeader": "ヘッダ。", - "apiKeyPermissionsHint": "キーは作成ユーザーの権限を継承します。", - "roleUser": "ユーザー", - "authMethodDual": "デュアル認証", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP セットアップの開始に失敗しました", - "totpEnter6Digits": "6桁のコードを入力してください", - "totpEnabledSuccess": "二段階認証を有効にしました", - "totpInvalidCode": "無効なコードです。もう一度やり直してください。", - "totpDisableInputRequired": "TOTPコードまたはパスワードを入力してください", - "totpDisabledSuccess": "二段階認証は無効です", - "totpDisableFailed": "2FAの無効化に失敗しました", - "totpDisableTitle": "2段階認証を無効にする", - "totpDisablePlaceholder": "TOTP コードまたはパスワードを入力", - "totpDisableConfirm": "2段階認証を無効にする", - "totpContinueVerify": "確認を続ける", - "totpVerifyTitle": "コードを確認", - "totpBackupTitle": "バックアップコード", - "totpDownloadBackup": "バックアップコードをダウンロード", - "done": "完了", - "secretCopied": "シークレットをクリップボードにコピーしました", - "apiKeyNameRequired": "キー名が必要です", - "apiKeyCreated": "API キー \"{{name}}\" が作成されました", - "apiKeyCreateFailed": "API キーの作成に失敗しました", - "apiKeyUser": "ユーザー", - "apiKeyExpires": "期限切れ", - "apiKeyRevoked": "API キー \"{{name}}\" が取り消されました", - "apiKeyRevokeFailed": "APIキーの取り消しに失敗しました", - "passwordFieldsRequired": "現在のパスワードと新しいパスワードが必要です", - "passwordMismatch": "パスワードが一致しません", - "passwordTooShort": "パスワードは6文字以上でなければなりません", - "passwordUpdated": "パスワードが正常に更新されました", - "passwordUpdateFailed": "パスワードの更新に失敗しました", - "deletePasswordRequired": "アカウントを削除するにはパスワードが必要です", - "deleteFailed": "アカウントの削除に失敗しました", - "deleting": "削除中...", - "colorPickerTooltip": "カラーピッカーを開く", - "themeSystem": "システム", - "themeLight": "ライト", - "themeDark": "ダーク", - "themeDracula": "ドラキュラ", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "1つのダーク", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ko_KR.json b/src/ui/locales/translated/ko_KR.json index ef28f958..9bc5af32 100644 --- a/src/ui/locales/translated/ko_KR.json +++ b/src/ui/locales/translated/ko_KR.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "폴더", - "folder": "폴더", - "password": "비밀번호", - "key": "키", - "sshPrivateKey": "SSH 개인 키", - "upload": "업로드", - "keyPassword": "키 비밀번호", - "sshKey": "SSH 키", - "uploadPrivateKeyFile": "개인 키 파일 업로드", - "searchCredentials": "자격 증명 검색...", - "addCredential": "자격 증명 추가", - "caCertificate": "CA 인증서(-cert.pub)", - "caCertificateDescription": "선택 사항: CA에서 서명한 인증서 파일(예: id_ed25519-cert.pub)을 업로드하거나 붙여넣으세요. SSH 서버에서 인증서 기반 인증을 사용하는 경우 필수입니다.", - "uploadCertFile": "-cert.pub 파일 업로드", - "clearCert": "분명한", - "certLoaded": "인증서가 로드되었습니다", - "certPublicKeyLabel": "캘리포니아 주 인증서", - "certTypeLabel": "인증서 유형", - "pasteOrUploadCert": "-cert.pub 인증서를 붙여넣거나 업로드하세요...", - "hasCaCert": "CA 자격증 보유", - "noCaCert": "공인회계사 자격증 없음" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "알림을 불러오는 데 실패했습니다.", - "failedToDismissAlert": "알림을 닫는 데 실패했습니다." + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "서버 설정", - "description": "백엔드 서비스에 연결할 Termix 서버 URL을 설정하세요.", - "serverUrl": "서버 URL", - "enterServerUrl": "서버 URL을 입력해 주세요", - "saveFailed": "설정을 저장하는 데 실패했습니다.", - "saveError": "설정 저장 오류", - "saving": "저장 중...", - "saveConfig": "설정 저장", - "helpText": "Termix 서버가 실행 중인 URL을 입력하세요(예: http://localhost:30001 또는 https://your-server.com).", - "changeServer": "서버 변경", - "mustIncludeProtocol": "서버 URL은 http:// 또는 https://로 시작해야 합니다.", - "allowInvalidCertificate": "유효하지 않은 인증서를 허용합니다", - "allowInvalidCertificateDesc": "자체 서명 인증서 또는 IP 주소 인증서를 사용하는 신뢰할 수 있는 자체 호스팅 서버에서만 사용하십시오.", - "useEmbedded": "로컬 서버 사용", - "embeddedDesc": "Termix를 내장된 로컬 서버로 실행하세요(원격 서버는 필요하지 않습니다).", - "embeddedConnecting": "로컬 서버에 연결 중...", - "embeddedNotReady": "로컬 서버가 아직 준비되지 않았습니다. 잠시 기다렸다가 다시 시도해 주세요.", - "localServer": "로컬 서버" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "버전 확인 오류", - "checkFailed": "업데이트 확인에 실패했습니다", - "upToDate": "앱이 최신 버전입니다", - "currentVersion": "현재 {{version}} 버전을 실행 중입니다.", - "updateAvailable": "업데이트 가능", - "newVersionAvailable": "새 버전이 있습니다! 현재 {{current}} 버전을 사용 중이며, {{latest}} 버전을 사용할 수 있습니다.", - "betaVersion": "베타 버전", - "betaVersionDesc": "현재 실행 중인 버전은 {{current}}이며, 이는 최신 안정 버전인 {{latest}}보다 최신 버전입니다.", - "releasedOn": "{{date}}에 출시됨", - "downloadUpdate": "업데이트 다운로드", - "checking": "업데이트를 확인하는 중...", - "checkUpdates": "업데이트 확인", - "checkingUpdates": "업데이트를 확인하는 중...", - "updateRequired": "업데이트 필요" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "닫기", - "minimize": "최소화", - "online": "온라인", - "offline": "오프라인", - "continue": "계속", - "maintenance": "유지보수", - "degraded": "성능 저하", - "error": "오류", - "warning": "경고", - "unsavedChanges": "저장되지 않은 변경 사항", - "dismiss": "해고하다", - "loading": "불러오는 중...", - "optional": "선택 사항", - "connect": "연결", - "copied": "복사됨", - "connecting": "연결 중...", - "updateAvailable": "업데이트 가능", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "새 탭에서 열기", - "noReleases": "릴리스 없음", - "updatesAndReleases": "업데이트 및 릴리스", - "newVersionAvailable": "새 버전({{version}})이 있습니다.", - "failedToFetchUpdateInfo": "업데이트 정보를 가져오는 데 실패했습니다.", - "preRelease": "사전 릴리스", - "noReleasesFound": "릴리스를 찾을 수 없습니다.", - "cancel": "취소", - "username": "사용자 이름", - "login": "로그인", - "register": "등록", - "password": "비밀번호", - "confirmPassword": "비밀번호 확인", - "back": "뒤로", - "save": "저장", - "saving": "저장 중...", - "delete": "삭제", - "rename": "이름 변경", - "edit": "편집", - "add": "추가", - "confirm": "확인", - "no": "아니요", - "or": "또는", - "next": "다음", - "previous": "이전", - "refresh": "새로고침", - "language": "언어", - "checking": "확인 중...", - "checkingDatabase": "데이터베이스 연결을 확인하는 중...", - "checkingAuthentication": "인증 상태를 확인하는 중...", - "backendReconnected": "서버 연결이 복구되었습니다.", - "connectionDegraded": "서버 연결이 끊어졌습니다. 복구 중…", - "reload": "새로고침", - "remove": "제거", - "create": "생성", - "update": "업데이트", - "copy": "복사", - "copyFailed": "클립보드에 복사하는 데 실패했습니다.", - "maximize": "최대화", - "restore": "복원하다", - "of": "~의" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "홈", - "terminal": "터미널", - "docker": "도커", - "tunnels": "터널", - "fileManager": "파일 관리자", - "serverStats": "서버 통계", - "admin": "관리자", - "userProfile": "사용자 프로필", - "splitScreen": "분할 화면", - "confirmClose": "이 세션을 종료하시겠습니까?", - "close": "닫다", - "cancel": "취소", - "sshManager": "SSH 관리자", - "cannotSplitTab": "이 탭을 분할할 수 없습니다.", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "비밀번호 복사", - "copySudoPassword": "Sudo 비밀번호 복사", - "passwordCopied": "비밀번호가 클립보드에 복사되었습니다.", - "noPasswordAvailable": "비밀번호를 사용할 수 없습니다.", - "failedToCopyPassword": "비밀번호 복사에 실패했습니다.", - "refreshTab": "연결 새로 고침", - "openFileManager": "파일 관리자 열기", - "dashboard": "계기반", - "networkGraph": "네트워크 그래프", - "quickConnect": "빠른 연결", - "sshTools": "SSH 도구", - "history": "역사", - "hosts": "호스트", - "snippets": "스니펫", - "hostManager": "호스트 관리자", - "credentials": "자격 증명", - "connections": "사이", - "roleAdministrator": "관리자", - "roleUser": "사용자" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "호스트", - "noHosts": "SSH 호스트 없음", - "retry": "다시 시도", - "refresh": "새로고침", - "optional": "선택 사항", - "downloadSample": "샘플 다운로드", - "failedToDeleteHost": "{{name}} 삭제에 실패했습니다.", - "importSkipExisting": "가져오기 (기존 항목 건너뛰기)", - "connectionDetails": "연결 정보", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", - "telnet": "텔넷", - "remoteDesktop": "원격 데스크톱", - "port": "포트", - "username": "사용자 이름", - "folder": "폴더", - "tags": "태그", - "pin": "핀", - "addHost": "호스트 추가", - "editHost": "호스트 편집", - "cloneHost": "호스트 복제", - "enableTerminal": "터미널 활성화", - "enableTunnel": "터널 활성화", - "enableFileManager": "파일 관리자 활성화", - "enableDocker": "Docker 활성화", - "defaultPath": "기본 경로", - "connection": "연결", - "upload": "업로드", - "authentication": "인증", - "password": "비밀번호", - "key": "키", - "credential": "자격 증명", - "none": "없음", - "sshPrivateKey": "SSH 개인 키", - "keyType": "키 유형", - "uploadFile": "파일 업로드", - "tabGeneral": "일반적인", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "터널", - "tabDocker": "도커", - "tabFiles": "파일", - "tabStats": "통계", - "tabTelnet": "텔넷", - "tabSharing": "공유하기", - "tabAuthentication": "입증", - "terminal": "터미널", - "tunnel": "터널", - "fileManager": "파일 관리자", - "serverStats": "서버 통계", - "status": "상태", - "folderRenamed": "폴더 \"{{oldName}}\"의 이름이 \"{{newName}}\"로 변경되었습니다.", - "failedToRenameFolder": "폴더 이름 변경에 실패했습니다", - "movedToFolder": "\"{{folder}} \"로 이동했습니다.", - "editHostTooltip": "호스트 편집", - "statusChecks": "상태 확인", - "metricsCollection": "메트릭 수집", - "metricsInterval": "메트릭 수집 간격", - "metricsIntervalDesc": "서버 통계 수집 빈도 (5초 ~ 1시간)", - "behavior": "동작", - "themePreview": "테마 미리보기", - "theme": "테마", - "fontFamily": "글꼴 패밀리", - "fontSize": "글꼴 크기", - "letterSpacing": "글자 간격", - "lineHeight": "줄 높이", - "cursorStyle": "커서 스타일", - "cursorBlink": "커서 깜빡임", - "scrollbackBuffer": "스크롤백 버퍼", - "bellStyle": "벨 스타일", - "rightClickSelectsWord": "오른쪽 클릭 시 단어 선택", - "fastScrollModifier": "빠른 스크롤 수정자", - "fastScrollSensitivity": "빠른 스크롤 감도", - "sshAgentForwarding": "SSH 에이전트 포워딩", - "backspaceMode": "백스페이스 모드", - "startupSnippet": "스타트업 스니펫", - "selectSnippet": "스니펫을 선택하세요", - "forceKeyboardInteractive": "키보드 상호작용 강제 적용", - "overrideCredentialUsername": "자격 증명 사용자 이름 재정의", - "overrideCredentialUsernameDesc": "자격 증명의 사용자 이름 대신 위에 지정된 사용자 이름을 사용하십시오.", - "jumpHostChain": "점프 호스트 체인", - "portKnocking": "포트 노킹", - "addKnock": "포트 추가", - "addProxyNode": "노드 추가", - "proxyNode": "프록시 노드", - "proxyType": "프록시 유형", - "quickActions": "빠른 조치", - "sudoPasswordAutoFill": "Sudo 비밀번호 자동 완성", - "sudoPassword": "Sudo 비밀번호", - "keepaliveInterval": "유지 간격(밀리초)", - "moshCommand": "MOSH 명령", - "environmentVariables": "환경 변수", - "addVariable": "변수 추가", - "docker": "도커", - "copyTerminalUrl": "터미널 URL 복사", - "copyFileManagerUrl": "파일 관리자 URL 복사", - "copyRemoteDesktopUrl": "원격 데스크톱 URL 복사", - "failedToConnect": "콘솔에 연결하지 못했습니다.", - "connect": "연결", - "disconnect": "연결 끊기", - "start": "시작", - "enableStatusCheck": "상태 확인 활성화", - "enableMetrics": "메트릭 활성화", - "bulkUpdateFailed": "일괄 업데이트 실패", - "selectAll": "모두 선택", - "deselectAll": "모두 선택 해제", - "protocols": "프로토콜", - "secureShell": "보안 셸", - "virtualNetwork": "가상 네트워크", - "unencryptedShell": "암호화되지 않은 셸", - "addressIp": "주소/IP", - "friendlyName": "친근한 이름", - "folderAndAdvanced": "폴더 및 고급", - "privateNotes": "개인 메모", - "privateNotesPlaceholder": "이 서버에 대한 자세한 정보...", - "pinToTop": "맨 위에 고정", - "pinToTopDesc": "이 호스트를 항상 목록 맨 위에 표시하세요", - "portKnockingSequence": "포트 노킹 시퀀스", - "addKnockBtn": "노크를 추가하세요", - "noPortKnocking": "포트 노킹이 구성되지 않았습니다.", - "knockPort": "노크 포트", - "protocol": "규약", - "delayAfterMs": "지연 시간(밀리초)", - "useSocks5Proxy": "SOCKS5 프록시를 사용하세요", - "useSocks5ProxyDesc": "프록시 서버를 통한 연결 라우팅", - "proxyHost": "프록시 호스트", - "proxyPort": "프록시 포트", - "proxyUsername": "프록시 사용자 이름", - "proxyPassword": "프록시 비밀번호", - "proxySingleMode": "싱글 프록시", - "proxyChainMode": "프록시 체인", - "you": "너", - "jumpHostChainLabel": "점프 호스트 체인", - "addJumpBtn": "점프 추가", - "noJumpHosts": "구성된 점프 호스트가 없습니다.", - "selectAServer": "서버를 선택하세요...", - "sshPort": "SSH 포트", - "authMethod": "인증 방식", - "storedCredential": "저장된 자격 증명", - "selectACredential": "자격증명을 선택하세요...", - "keyTypeLabel": "키 유형", - "keyTypeAuto": "자동 감지", - "keyPasteTab": "반죽", - "keyUploadTab": "업로드", - "keyFileLoaded": "키 파일이 로드되었습니다.", - "keyUploadClick": ".pem / .key / .ppk 파일을 업로드하려면 클릭하세요.", - "clearKey": "지우기 키", - "keySaved": "SSH 키가 저장되었습니다", - "keyReplaceNotice": "아래에 새 키를 붙여넣어 기존 키를 대체하세요.", - "keyPassphraseSaved": "암호가 저장되었습니다. 변경하려면 입력하세요.", - "replaceKey": "키를 교체하세요", - "forceKeyboardInteractiveLabel": "키보드 대화형 강제 적용", - "forceKeyboardInteractiveShortDesc": "열쇠가 있더라도 수동으로 암호를 입력하도록 강제합니다.", - "terminalAppearance": "터미널 외관", - "colorTheme": "색상 테마", - "fontFamilyLabel": "글꼴 패밀리", - "fontSizeLabel": "글꼴 크기", - "cursorStyleLabel": "커서 스타일", - "letterSpacingPx": "자간(픽셀)", - "lineHeightLabel": "선 높이", - "bellStyleLabel": "벨 스타일", - "backspaceModeLabel": "백스페이스 모드", - "cursorBlinking": "커서 깜빡임", - "cursorBlinkingDesc": "터미널 커서의 깜빡임 애니메이션을 활성화합니다.", - "rightClickSelectsWordLabel": "마우스 오른쪽 버튼을 클릭하고 단어를 선택합니다.", - "rightClickSelectsWordShortDesc": "마우스 오른쪽 버튼을 클릭하여 커서 아래 단어를 선택하세요.", - "behaviorAndAdvanced": "행동 및 고급", - "scrollbackBufferLabel": "스크롤백 버퍼", - "scrollbackMaxLines": "기록에 저장되는 최대 행 수", - "sshAgentForwardingLabel": "SSH 에이전트 포워딩", - "sshAgentForwardingShortDesc": "로컬 SSH 키를 이 호스트로 전달하세요.", - "enableAutoMosh": "자동 모싱 활성화", - "enableAutoMoshDesc": "가능하다면 SSH보다 Mosh를 선호합니다.", - "enableAutoTmux": "자동 Tmux 활성화", - "enableAutoTmuxDesc": "tmux 세션을 자동으로 시작하거나 연결합니다.", - "sudoPasswordAutoFillLabel": "Sudo 비밀번호 자동 완성", - "sudoPasswordAutoFillShortDesc": "sudo 암호를 입력하라는 메시지가 나타나면 자동으로 암호를 제공합니다.", - "sudoPasswordLabel": "Sudo 비밀번호", - "environmentVariablesLabel": "환경 변수", - "addVariableBtn": "변수 추가", - "noEnvVars": "환경 변수가 설정되지 않았습니다.", - "fastScrollModifierLabel": "빠른 스크롤 수정자", - "fastScrollSensitivityLabel": "빠른 스크롤 감도", - "moshCommandLabel": "모쉬 커맨드", - "startupSnippetLabel": "스타트업 스니펫", - "keepaliveIntervalLabel": "유지 간격(초)", - "maxKeepaliveMisses": "맥스 킵얼라이브 미스", - "tunnelSettings": "터널 설정", - "enableTunneling": "터널링 활성화", - "enableTunnelingDesc": "이 호스트에 대해 SSH 터널 기능을 활성화합니다.", - "serverTunnelsSection": "서버 터널", - "addTunnelBtn": "터널 추가", - "noTunnelsConfigured": "구성된 터널이 없습니다.", - "tunnelLabel": "터널 {{number}}", - "tunnelType": "터널 유형", - "tunnelModeLocalDesc": "로컬 포트를 원격 서버(또는 해당 서버에서 접근 가능한 호스트)의 포트로 포워딩합니다.", - "tunnelModeRemoteDesc": "원격 서버의 포트를 사용자 컴퓨터의 로컬 포트로 포워딩하십시오.", - "tunnelModeDynamicDesc": "동적 포트 포워딩을 위해 로컬 포트에 SOCKS5 프록시를 생성합니다.", - "sameHost": "이 호스트(직접 터널)", - "endpointHost": "엔드포인트 호스트", - "endpointPort": "엔드포인트 포트", - "bindHost": "호스트 바인딩", - "sourcePort": "소스 포트", - "maxRetries": "최대 재시도 횟수", - "retryIntervalS": "재시도 간격(초)", - "autoStartLabel": "자동 시작", - "autoStartDesc": "호스트가 로드될 때 이 터널을 자동으로 연결합니다.", - "tunnelConnecting": "터널 연결...", - "tunnelDisconnected": "터널 연결이 끊어졌습니다", - "failedToConnectTunnel": "연결에 실패했습니다", - "failedToDisconnectTunnel": "연결 해제에 실패했습니다", - "dockerIntegration": "Docker 통합", - "enableDockerMonitor": "Docker 활성화", - "enableDockerMonitorDesc": "Docker를 통해 이 호스트의 컨테이너를 모니터링하고 관리하세요.", - "enableFileManagerMonitor": "파일 관리자 활성화", - "enableFileManagerMonitorDesc": "SFTP를 통해 이 호스트의 파일을 탐색하고 관리하세요.", - "defaultPathLabel": "기본 경로", - "fileManagerPathHint": "이 호스트에서 파일 관리자가 실행될 때 열릴 디렉터리입니다.", - "statusChecksLabel": "상태 확인", - "enableStatusChecks": "상태 확인 활성화", - "enableStatusChecksDesc": "주기적으로 이 호스트에 ping을 보내 가용성을 확인하십시오.", - "useGlobalInterval": "글로벌 간격을 사용하세요", - "useGlobalIntervalDesc": "서버 전체 상태 확인 간격으로 재정의합니다.", - "checkIntervalS": "점검 간격(초)", - "checkIntervalDesc": "각 연결 핑 사이의 간격(초)", - "metricsCollectionLabel": "측정항목 수집", - "enableMetricsLabel": "메트릭 활성화", - "enableMetricsDesc": "이 호스트의 CPU, RAM, 디스크 및 네트워크 사용량을 수집합니다.", - "useGlobalMetrics": "글로벌 간격을 사용하세요", - "useGlobalMetricsDesc": "서버 전체 메트릭 간격으로 재정의합니다.", - "metricsIntervalS": "측정 간격(초)", - "metricsIntervalDesc2": "측정값 스냅샷 간 간격(초)", - "visibleWidgets": "표시되는 위젯", - "cpuUsageLabel": "CPU 사용량", - "cpuUsageDesc": "CPU 사용률, 평균 부하, 스파크라인 그래프", - "memoryLabel": "메모리 사용량", - "memoryDesc": "RAM 사용량, 스왑, 캐시", - "storageLabel": "디스크 사용량", - "storageDesc": "마운트 지점별 디스크 사용량", - "networkLabel": "네트워크 인터페이스", - "networkDesc": "인터페이스 목록 및 대역폭", - "uptimeLabel": "가동 시간", - "uptimeDesc": "시스템 가동 시간 및 부팅 시간", - "systemInfoLabel": "시스템 정보", - "systemInfoDesc": "OS, 커널, 호스트 이름, 아키텍처", - "recentLoginsLabel": "최근 로그인", - "recentLoginsDesc": "로그인 성공 및 실패 이벤트", - "topProcessesLabel": "주요 프로세스", - "topProcessesDesc": "PID, CPU%, 메모리%, 명령", - "listeningPortsLabel": "수신 포트", - "listeningPortsDesc": "프로세스 및 상태를 사용하여 포트를 엽니다.", - "firewallLabel": "방화벽", - "firewallDesc": "방화벽, AppArmor, SELinux 상태", - "quickActionsLabel": "빠른 조치", - "quickActionsToolbar": "빠른 실행 기능은 서버 통계 도구 모음에 버튼으로 표시되어 한 번의 클릭으로 명령을 실행할 수 있습니다.", - "noQuickActions": "아직 빠른 조치는 없습니다.", - "buttonLabel": "버튼 라벨", - "selectSnippetPlaceholder": "코드 조각을 선택하세요...", - "addActionBtn": "작업 추가", - "hostSharedSuccessfully": "호스트가 성공적으로 공유했습니다.", - "failedToShareHost": "호스트 공유에 실패했습니다", - "accessRevoked": "접근 권한이 취소되었습니다", - "failedToRevokeAccess": "접근 권한 취소에 실패했습니다.", - "cancelBtn": "취소", - "savingBtn": "절약...", - "addHostBtn": "호스트 추가", - "hostUpdated": "호스트가 업데이트되었습니다.", - "hostCreated": "호스트가 생성했습니다", - "failedToSave": "호스트 저장에 실패했습니다", - "credentialUpdated": "자격 증명이 업데이트되었습니다.", - "credentialCreated": "자격 증명이 생성되었습니다", - "failedToSaveCredential": "자격 증명을 저장하는 데 실패했습니다.", - "backToHosts": "호스트 목록으로 돌아가기", - "backToCredentials": "자격 증명 페이지로 돌아가기", - "pinned": "고정됨", - "noHostsFound": "호스트를 찾을 수 없습니다.", - "tryDifferentTerm": "다른 용어를 사용해 보세요.", - "addFirstHost": "시작하려면 첫 번째 호스트를 추가하세요.", - "noCredentialsFound": "자격 증명을 찾을 수 없습니다.", - "addCredentialBtn": "자격 증명 추가", - "updateCredentialBtn": "자격 증명 업데이트", - "features": "특징", - "noFolder": "(폴더 없음)", - "deleteSelected": "삭제", - "exitSelection": "선택 종료", - "importSkip": "가져오기 (기존 항목 건너뛰기)", - "importOverwrite": "가져오기(덮어쓰기)", - "collapseBtn": "무너지다", - "importExportBtn": "수입/수출", - "hostStatusesRefreshed": "호스트 상태가 새로 고쳐졌습니다.", - "failedToRefreshHosts": "호스트 새로 고침에 실패했습니다.", - "movedHostTo": "{{host}} 를 \"{{folder}} \"로 이동했습니다.", - "failedToMoveHost": "호스트 이동에 실패했습니다", - "folderRenamedTo": "폴더 이름이 \"{{name}} \"로 변경되었습니다.", - "deletedFolder": "삭제된 폴더 \"{{name}}\"", - "failedToDeleteFolder": "폴더 삭제에 실패했습니다", - "deleteAllInFolder": "\"{{name}}\"에 있는 모든 호스트를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "deletedHost": "삭제됨 {{name}}", - "copiedToClipboard": "클립보드에 복사됨", - "terminalUrlCopied": "터미널 URL이 복사되었습니다.", - "fileManagerUrlCopied": "파일 관리자 URL이 복사되었습니다.", - "tunnelUrlCopied": "터널 URL이 복사되었습니다", - "dockerUrlCopied": "Docker URL이 복사되었습니다.", - "serverStatsUrlCopied": "서버 통계 URL이 복사되었습니다", - "rdpUrlCopied": "RDP URL이 복사되었습니다.", - "vncUrlCopied": "VNC URL이 복사되었습니다.", - "telnetUrlCopied": "텔넷 URL이 복사되었습니다.", - "remoteDesktopUrlCopied": "원격 데스크톱 URL이 복사되었습니다.", - "expandActions": "확장 작업", - "collapseActions": "붕괴 조치", - "wakeOnLanAction": "LAN에서 웨이크업", - "wakeOnLanSuccess": "매직 패킷이 {{name}}로 전송되었습니다.", - "wakeOnLanError": "매직 패킷 전송에 실패했습니다.", - "cloneHostAction": "클론 호스트", - "copyAddress": "주소 복사", - "copyLink": "링크 복사", - "copyTerminalUrlAction": "터미널 URL 복사", - "copyFileManagerUrlAction": "파일 관리자 URL 복사", - "copyTunnelUrlAction": "터널 URL 복사", - "copyDockerUrlAction": "Docker URL 복사", - "copyServerStatsUrlAction": "서버 통계 URL 복사", - "copyRdpUrlAction": "RDP URL 복사", - "copyVncUrlAction": "VNC URL 복사", - "copyTelnetUrlAction": "텔넷 URL 복사", - "copyRemoteDesktopUrlAction": "원격 데스크톱 URL 복사", - "deleteCredentialConfirm": "자격 증명 \"{{name}} \"을 삭제하시겠습니까?", - "deletedCredential": "삭제됨 {{name}}", - "deploySSHKeyTitle": "SSH 키 배포", - "deployingBtn": "배포 중...", - "deployBtn": "배포", - "failedToDeployKey": "키 배포에 실패했습니다", - "deleteHostsConfirm": "{{count}} 호스트{{plural}}를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "movedToRoot": "루트로 이동됨", - "failedToMoveHosts": "호스트 이동에 실패했습니다", - "enableTerminalFeature": "터미널 활성화", - "disableTerminalFeature": "터미널 비활성화", - "enableFilesFeature": "파일 활성화", - "disableFilesFeature": "파일 비활성화", - "enableTunnelsFeature": "터널 활성화", - "disableTunnelsFeature": "터널 비활성화", - "enableDockerFeature": "Docker 활성화", - "disableDockerFeature": "Docker 비활성화", - "addTagsPlaceholder": "태그 추가...", - "authDetails": "인증 정보", - "credType": "유형", - "generateKeyPairDesc": "새로운 키 쌍을 생성하면 개인 키와 공개 키가 자동으로 채워집니다.", - "generatingKey": "생성 중...", - "generateLabel": "{{label}} 생성", - "uploadFileBtn": "파일 업로드", - "keyPassphraseOptional": "키 암호문 (선택 사항)", - "sshPublicKeyOptional": "SSH 공개 키 (선택 사항)", - "publicKeyGenerated": "공개 키가 생성되었습니다", - "failedToGeneratePublicKey": "공개 키를 생성하는 데 실패했습니다.", - "publicKeyCopied": "공개 키가 복사되었습니다.", - "keyPairGenerated": "{{label}} 키 쌍이 생성되었습니다", - "failedToGenerateKeyPair": "키 쌍 생성에 실패했습니다.", - "searchHostsPlaceholder": "호스트, 주소, 태그를 검색하세요…", - "searchCredentialsPlaceholder": "자격 증명 검색…", - "refreshBtn": "새로 고치다", - "addTag": "태그 추가...", - "deleteConfirmBtn": "삭제", - "tunnelRequirementsText": "SSH 서버는 /etc/ssh/sshd_config 파일에 GatewayPorts yes, AllowTcpForwarding yes, PermitRootLogin yes 설정이 되어 있어야 합니다.", - "deleteHostConfirm": "\"{{name}} \"을 삭제하시겠습니까?", - "enableAtLeastOneProtocol": "인증 및 연결 설정을 구성하려면 위의 프로토콜 중 하나 이상을 활성화하십시오.", - "keyPassphrase": "키 암호문", - "connectBtn": "연결하다", - "disconnectBtn": "연결을 끊으세요", - "basicInformation": "기본 정보", - "authDetailsSection": "인증 정보", - "credTypeLabel": "유형", - "hostsTab": "호스트", - "credentialsTab": "신임장", - "selectMultiple": "여러 개를 선택하세요", - "selectHosts": "호스트를 선택하세요", - "connectionLabel": "연결", - "authenticationLabel": "입증", - "generateKeyPairTitle": "키 페어 생성", - "generateKeyPairDescription": "새로운 키 쌍을 생성하면 개인 키와 공개 키가 자동으로 채워집니다.", - "generateFromPrivateKey": "개인 키에서 생성", - "refreshBtn2": "새로 고치다", - "exitSelectionTitle": "선택 종료", - "exportAll": "모두 내보내기", - "addHostBtn2": "호스트 추가", - "addCredentialBtn2": "자격 증명 추가", - "checkingHostStatuses": "호스트 상태를 확인하는 중...", - "pinnedSection": "고정됨", - "hostsExported": "호스트 내보내기가 성공적으로 완료되었습니다.", - "exportFailed": "호스트 내보내기에 실패했습니다", - "sampleDownloaded": "샘플 파일 다운로드됨", - "failedToDeleteCredential2": "자격 증명 삭제에 실패했습니다.", - "noFolderOption": "(폴더 없음)", - "nSelected": "{{count}} 선택됨", - "featuresMenu": "특징", - "moveMenu": "이동하다", - "cancelSelection": "취소", - "deployDialogDesc": "{{name}} 를 호스트의 authorized_keys에 배포합니다.", - "targetHostLabel": "대상 호스트", - "selectHostOption": "호스트를 선택하세요...", - "keyDeployedSuccess": "키가 성공적으로 배포되었습니다.", - "failedToDeployKey2": "키 배포에 실패했습니다", - "deletedCount": "삭제된 {{count}} 호스트", - "failedToDeleteCount": "{{count}} 호스트를 삭제하는 데 실패했습니다.", - "duplicatedHost": "\"{{name}} \"가 복제됨", - "failedToDuplicateHost": "호스트 복제에 실패했습니다.", - "updatedCount": "{{count}} 호스트가 업데이트되었습니다.", - "friendlyNameLabel": "친근한 이름", - "descriptionLabel": "설명", - "loadingHost": "호스트를 불러오는 중...", - "loadingHosts": "호스트를 불러오는 중...", - "loadingCredentials": "자격 증명을 불러오는 중...", - "noHostsYet": "아직 호스트가 없습니다", - "noHostsMatchSearch": "검색 조건에 맞는 호스트가 없습니다.", - "hostNotFound": "호스트를 찾을 수 없습니다", - "searchHosts": "호스트를 검색하세요...", - "sortHosts": "호스트 정렬", - "sortDefault": "기본 주문", - "sortNameAsc": "이름 (A → Z)", - "sortNameDesc": "이름 (Z → A)", - "sortIpAsc": "IP 주소(오름차순)", - "sortIpDesc": "IP 주소 (설명)", - "sortOnlineFirst": "온라인 우선", - "sortOfflineFirst": "오프라인 우선", - "sortPinnedFirst": "첫 번째 고정됨", - "filterHosts": "필터 호스트", - "filterClearAll": "필터 지우기", - "filterStatusGroup": "상태", - "filterOnline": "온라인", - "filterOffline": "오프라인", - "filterPinned": "고정됨", - "filterAuthGroup": "인증 유형", - "filterAuthPassword": "비밀번호", - "filterAuthKey": "SSH 키", - "filterAuthCredential": "신임장", - "filterAuthNone": "없음", - "filterAuthOpkssh": "오프크시", - "filterProtocolGroup": "규약", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "텔넷", - "filterFeaturesGroup": "특징", - "filterFeatureTerminal": "단말기", - "filterFeatureFileManager": "파일 관리자", - "filterFeatureTunnel": "터널", - "filterFeatureDocker": "도커", - "filterTagsGroup": "태그", - "shareHost": "호스트 공유", - "shareHostTitle": "공유: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "이 호스트에서 공유를 활성화하려면 자격 증명이 필요합니다. 먼저 호스트를 편집하고 자격 증명을 할당하십시오." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "연결", - "authentication": "입증", - "connectionSettings": "연결 설정", - "displaySettings": "디스플레이 설정", - "audioSettings": "오디오 설정", - "rdpPerformance": "RDP 성능", - "deviceRedirection": "기기 리디렉션", - "session": "세션", - "gateway": "게이트웨이", - "remoteApp": "원격 앱", - "clipboard": "클립보드", - "sessionRecording": "세션 녹화", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC 설정", - "terminalSettings": "터미널 설정", - "rdpPort": "RDP 포트", - "username": "사용자 이름", - "password": "비밀번호", - "domain": "도메인", - "securityMode": "보안 모드", - "colorDepth": "색심도", - "width": "너비", - "height": "키", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "크기 조정 방법", - "clientName": "고객 이름", - "initialProgram": "초기 프로그램", - "serverLayout": "서버 레이아웃", - "timezone": "시간대", - "gatewayHostname": "게이트웨이 호스트 이름", - "gatewayPort": "게이트웨이 포트", - "gatewayUsername": "게이트웨이 사용자 이름", - "gatewayPassword": "게이트웨이 비밀번호", - "gatewayDomain": "게이트웨이 도메인", - "remoteAppProgram": "RemoteApp 프로그램", - "workingDirectory": "업무 디렉토리", - "arguments": "논거", - "normalizeLineEndings": "줄 끝 문자 정규화", - "recordingPath": "기록 경로", - "recordingName": "녹음 이름", - "macAddress": "MAC 주소", - "broadcastAddress": "방송 주소", - "udpPort": "UDP 포트", - "waitTimeS": "대기 시간(초)", - "driveName": "드라이브 이름", - "drivePath": "차량 통행로", - "ignoreCertificate": "인증서 무시", - "ignoreCertificateDesc": "자체 서명 인증서를 사용하는 호스트에 대한 연결을 허용합니다.", - "forceLossless": "힘 손실 없음", - "forceLosslessDesc": "무손실 이미지 인코딩 강제 적용(더 높은 화질, 더 많은 대역폭)", - "disableAudio": "오디오 비활성화", - "disableAudioDesc": "원격 세션의 모든 오디오를 음소거합니다.", - "enableAudioInput": "오디오 입력(마이크)을 활성화하세요.", - "enableAudioInputDesc": "로컬 마이크를 원격 세션으로 전달합니다.", - "wallpaper": "벽지", - "wallpaperDesc": "바탕 화면 배경 표시 (해제하면 성능이 향상됩니다)", - "theming": "테마", - "themingDesc": "시각적 테마 및 스타일을 활성화합니다.", - "fontSmoothing": "글꼴 부드럽게 처리", - "fontSmoothingDesc": "ClearType 글꼴 렌더링을 활성화합니다.", - "fullWindowDrag": "전체 창 드래그", - "fullWindowDragDesc": "드래그하는 동안 창 내용을 표시합니다.", - "desktopComposition": "데스크톱 구성", - "desktopCompositionDesc": "Aero 유리 효과 활성화", - "menuAnimations": "메뉴 애니메이션", - "menuAnimationsDesc": "메뉴 페이드 및 슬라이드 애니메이션을 활성화합니다.", - "disableBitmapCaching": "비트맵 캐싱 비활성화", - "disableBitmapCachingDesc": "비트맵 캐시를 끄세요(오류 해결에 도움이 될 수 있습니다).", - "disableOffscreenCaching": "오프스크린 캐싱 비활성화", - "disableOffscreenCachingDesc": "오프스크린 캐시를 끄세요", - "disableGlyphCaching": "글리프 캐싱 비활성화", - "disableGlyphCachingDesc": "글리프 캐시를 끄세요", - "enableGfx": "GFX 활성화", - "enableGfxDesc": "RemoteFX 그래픽 파이프라인을 사용하세요.", - "enablePrinting": "인쇄 활성화", - "enablePrintingDesc": "로컬 프린터를 원격 세션으로 리디렉션합니다.", - "enableDriveRedirection": "드라이브 리디렉션 활성화", - "enableDriveRedirectionDesc": "원격 세션에서 로컬 폴더를 드라이브로 매핑합니다.", - "createDrivePath": "드라이브 경로 생성", - "createDrivePathDesc": "폴더가 존재하지 않으면 자동으로 생성합니다.", - "disableDownload": "다운로드 비활성화", - "disableDownloadDesc": "원격 세션에서 파일 다운로드를 방지합니다.", - "disableUpload": "업로드 비활성화", - "disableUploadDesc": "원격 세션에 파일 업로드를 방지합니다.", - "enableTouch": "터치 활성화", - "enableTouchDesc": "터치 입력 전달 활성화", - "consoleSession": "콘솔 세션", - "consoleSessionDesc": "새 세션을 생성하는 대신 콘솔(세션 0)에 연결하세요.", - "sendWolPacket": "WOL 패킷 전송", - "sendWolPacketDesc": "연결하기 전에 매직 패킷을 보내서 이 호스트를 깨우세요.", - "disableCopy": "복사 비활성화", - "disableCopyDesc": "원격 세션에서 텍스트 복사를 방지합니다.", - "disablePaste": "붙여넣기 비활성화", - "disablePasteDesc": "원격 세션에 텍스트 붙여넣기를 방지합니다.", - "createPathIfMissing": "경로가 없으면 생성하세요", - "createPathIfMissingDesc": "녹음 디렉토리를 자동으로 생성합니다", - "excludeOutput": "출력 제외", - "excludeOutputDesc": "화면 출력은 기록하지 마십시오 (메타데이터만 기록).", - "excludeMouse": "마우스 제외", - "excludeMouseDesc": "마우스 움직임을 기록하지 마세요.", - "includeKeystrokes": "키 입력을 포함하세요", - "includeKeystrokesDesc": "화면 출력 외에도 키 입력 내용을 그대로 기록합니다.", - "vncPort": "VNC 포트", - "vncPassword": "VNC 비밀번호", - "vncUsernameOptional": "사용자 이름 (선택 사항)", - "vncLeaveBlank": "필요하지 않은 경우 비워 두십시오.", - "cursorMode": "커서 모드", - "swapRedBlue": "빨간색/파란색 바꾸기", - "swapRedBlueDesc": "빨간색과 파란색 채널을 서로 바꿉니다(일부 색상 문제를 해결합니다).", - "readOnly": "읽기 전용", - "readOnlyDesc": "아무런 입력도 보내지 않고 원격 화면을 봅니다.", - "telnetPort": "텔넷 포트", - "terminalType": "터미널 유형", - "fontName": "글꼴 이름", - "fontSize": "글꼴 크기", - "colorScheme": "색 구성표", - "backspaceKey": "백스페이스 키", - "saveHostFirst": "먼저 호스트를 저장하세요.", - "sharingOptionsAfterSave": "호스트를 저장한 후에는 공유 옵션을 사용할 수 있습니다.", - "sharingLoadError": "공유 데이터를 불러오는 데 실패했습니다. 연결 상태를 확인하고 다시 시도하세요.", - "shareHostSection": "호스트 공유", - "shareWithUser": "사용자와 공유", - "shareWithRole": "역할과 공유하기", - "selectUser": "사용자 선택", - "selectRole": "역할 선택", - "selectUserOption": "사용자를 선택하세요...", - "selectRoleOption": "역할을 선택하세요...", - "permissionLevel": "권한 수준", - "expiresInHours": "(시간) 후에 만료됩니다.", - "noExpiryPlaceholder": "유통기한이 없으려면 빈칸으로 두십시오.", - "shareBtn": "공유하다", - "currentAccess": "현재 액세스", - "typeHeader": "유형", - "targetHeader": "목표", - "permissionHeader": "허가", - "grantedByHeader": "승인자:", - "expiresHeader": "만료됨", - "noAccessEntries": "아직 접근 기록이 없습니다.", - "expiredLabel": "만료됨", - "neverLabel": "절대", - "revokeBtn": "취소", - "cancelBtn": "취소", - "savingBtn": "절약...", - "updateHostBtn": "호스트 업데이트", - "addHostBtn": "호스트 추가" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "호스트, 명령어 또는 설정을 검색하세요...", - "quickActions": "빠른 조치", - "hostManager": "호스트 관리자", - "hostManagerDesc": "호스트를 관리, 추가 또는 편집합니다.", - "addNewHost": "새 호스트 추가", - "addNewHostDesc": "새 호스트 등록", - "adminSettings": "관리자 설정", - "adminSettingsDesc": "시스템 환경설정 및 사용자 구성", - "userProfile": "사용자 프로필", - "userProfileDesc": "계정 및 환경설정 관리", - "addCredential": "자격 증명 추가", - "addCredentialDesc": "SSH 키 또는 암호를 저장하세요", - "recentActivity": "최근 활동", - "serversAndHosts": "서버 및 호스트", - "noHostsFound": "\"{{search}} \" 와 일치하는 호스트를 찾을 수 없습니다.", - "links": "링크", - "navigate": "탐색하기", - "select": "선택하다", - "toggleWith": "토글" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "패널 {{index}} - 비어 있음", - "noTabAssigned": "탭이 지정되지 않았습니다.", - "focusedPane": "활성 창" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "연결 없음", - "noConnectionsDesc": "터미널, 파일 관리자 또는 원격 데스크톱을 열어 연결 상태를 확인하세요.", - "connectedFor": "{{duration}}에 연결되었습니다.", - "connected": "연결됨", - "disconnected": "연결이 끊어졌습니다", - "closeTab": "닫기 탭", - "closeConnection": "닫기", - "forgetTab": "잊다", - "removeBackground": "제거하다", - "reconnect": "다시 연결", - "reopenTab": "다시 열다", - "sectionOpen": "열려 있는", - "sectionBackground": "배경", - "backgroundDesc": "연결이 끊어진 후에도 세션은 30분 동안 유지되며 다시 연결할 수 있습니다.", - "persisted": "백그라운드에서 지속됨", - "expiresIn": "{{duration}} 후에 만료됩니다.", - "search": "검색 연결...", - "noSearchResults": "검색 조건과 일치하는 결과가 없습니다." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "{{type}} 세션에 연결 중...", - "connectionError": "연결 오류", - "connectionFailed": "연결 실패", - "failedToConnect": "연결 토큰을 가져오는 데 실패했습니다.", - "hostNotFound": "호스트를 찾을 수 없습니다", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "reconnect": "다시 연결", - "retry": "다시 해 보다", - "guacdUnavailable": "원격 데스크톱 서비스(guacd)를 사용할 수 없습니다. guacd가 실행 중이고 관리자 설정에서 올바르게 구성되어 있는지 확인하십시오.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (화면 잠금)", - "winKey": "윈도우 키", - "ctrl": "Ctrl 키", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "옮기다", - "win": "이기다", - "stickyActive": "{{key}} (잠금 - 클릭하여 해제)", - "stickyInactive": "{{key}} (클릭하여 잠그세요)", - "esc": "탈출하다", - "tab": "꼬리표", - "home": "집", - "end": "끝", - "pageUp": "페이지 위로", - "pageDown": "페이지 다운", - "arrowUp": "위쪽 화살표", - "arrowDown": "아래쪽 화살표", - "arrowLeft": "왼쪽 화살표", - "arrowRight": "오른쪽 화살표", - "fnToggle": "기능 키", - "reconnect": "세션 재연결", - "collapse": "툴바 접기", - "expand": "확장 도구 모음", - "dragHandle": "드래그하여 위치를 변경하세요" + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "호스트에 연결", - "clear": "지우기", - "paste": "붙여넣기", - "reconnect": "다시 연결", - "connectionLost": "연결이 끊어졌습니다.", - "connected": "연결됨", - "clipboardWriteFailed": "클립보드에 복사하는 데 실패했습니다. 페이지가 HTTPS 또는 localhost를 통해 제공되는지 확인하세요.", - "clipboardReadFailed": "클립보드에서 읽는 데 실패했습니다. 클립보드 권한이 부여되었는지 확인하세요.", - "clipboardHttpWarning": "붙여넣기에는 HTTPS가 필요합니다. Ctrl+Shift+V를 사용하거나 Termix를 HTTPS로 제공하세요.", - "unknownError": "알 수 없는 오류가 발생했습니다.", - "websocketError": "웹소켓 연결 오류", - "connecting": "연결 중...", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "reconnecting": "다시 연결 중... ({{attempt}}/{{max}})", - "reconnected": "다시 연결되었습니다.", - "tmuxSessionCreated": "tmux 세션 생성됨: {{name}}", - "tmuxSessionAttached": "tmux 세션 연결됨: {{name}}", - "tmuxUnavailable": "원격 호스트에 tmux가 설치되어 있지 않아 일반 셸로 대체합니다.", - "tmuxSessionPickerTitle": "tmux 세션", - "tmuxSessionPickerDesc": "이 호스트에서 기존 tmux 세션을 찾았습니다. 다시 연결할 세션을 선택하거나 새 세션을 생성하세요.", - "tmuxWindows": "창", - "tmuxWindowCount": "{{count}}개 창", - "tmuxAttached": "연결된 클라이언트", - "tmuxAttachedCount": "{{count}}개 연결됨", - "tmuxLastActivity": "마지막 활동", - "tmuxTimeJustNow": "방금 전", - "tmuxTimeMinutes": "{{count}}분 전", - "tmuxTimeHours": "{{count}}시간 전", - "tmuxTimeDays": "{{count}}일 전", - "tmuxCreateNew": "새 세션 시작", - "tmuxCopyHint": "선택 영역을 조정한 후 Enter를 눌러 클립보드에 복사하세요.", - "tmuxDetach": "tmux 세션에서 분리", - "tmuxDetached": "tmux 세션에서 분리됨", - "maxReconnectAttemptsReached": "최대 재연결 시도 횟수에 도달했습니다.", - "closeTab": "닫기", - "connectionTimeout": "연결 시간 초과", - "terminalTitle": "터미널 - {{host}}", - "terminalWithPath": "터미널 - {{host}}:{{path}}", - "runTitle": "{{command}} - {{host}} 실행 중", - "totpRequired": "2단계 인증이 필요합니다", - "totpCodeLabel": "인증 코드", - "totpVerify": "확인", - "warpgateAuthRequired": "워프게이트 인증 필요", - "warpgateSecurityKey": "보안 키", - "warpgateAuthUrl": "인증 URL", - "warpgateOpenBrowser": "브라우저에서 열기", - "warpgateContinue": "인증 완료", - "opksshAuthRequired": "OPKSSH 인증이 필요합니다", - "opksshAuthDescription": "계속하려면 브라우저에서 인증을 완료하세요. 이 세션은 24시간 동안 유효합니다.", - "opksshOpenBrowser": "인증하려면 브라우저를 여세요", - "opksshWaitingForAuth": "브라우저에서 인증을 기다리는 중...", - "opksshAuthenticating": "인증 처리 중...", - "opksshTimeout": "인증 시간이 초과되었습니다. 다시 시도해 주세요.", - "opksshAuthFailed": "인증에 실패했습니다. 자격 증명을 확인하고 다시 시도해 주세요.", - "opksshSignInWith": "{{provider}}로 로그인", - "sudoPasswordPopupTitle": "비밀번호를 입력할까요?", - "websocketAbnormalClose": "연결이 예기치 않게 종료되었습니다. 리버스 프록시 또는 SSL 구성 문제 때문일 수 있습니다. 서버 로그를 확인하세요.", - "connectionLogTitle": "연결 로그", - "connectionLogCopy": "로그를 클립보드에 복사", - "connectionLogEmpty": "아직 연결 로그가 없습니다.", - "connectionLogWaiting": "연결 로그를 기다리는 중...", - "connectionLogCopied": "연결 로그가 클립보드에 복사되었습니다.", - "connectionLogCopyFailed": "로그를 클립보드에 복사하는 데 실패했습니다.", - "connectionRejected": "서버에서 연결이 거부되었습니다. 인증 및 네트워크 구성을 확인하세요.", - "hostKeyRejected": "SSH 호스트 키 인증이 거부되었습니다. 연결이 취소되었습니다.", - "sessionTakenOver": "세션이 다른 탭에서 열려 있습니다. 다시 연결 중..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "호스트가 선택되지 않았습니다.", - "initializingEditor": "편집기 초기화 중...", - "file": "파일", - "folder": "폴더", - "uploadFile": "파일 업로드", - "downloadFile": "다운로드", - "extractArchive": "압축 해제", - "extractingArchive": "{{name}} 추출 중...", - "archiveExtractedSuccessfully": "{{name}} 추출 성공", - "extractFailed": "추출 실패", - "compressFile": "파일 압축", - "compressFiles": "파일 압축", - "compressFilesDesc": "{{count}}개 항목을 아카이브로 압축합니다.", - "archiveName": "아카이브 이름", - "enterArchiveName": "아카이브 이름을 입력하세요...", - "compressionFormat": "압축 형식", - "selectedFiles": "선택된 파일", - "andMoreFiles": "외 {{count}}개 더...", - "compress": "압축", - "compressingFiles": "{{count}}개 항목을 {{name}}(으)로 압축 중...", - "filesCompressedSuccessfully": "{{name}}이(가) 생성되었습니다.", - "compressFailed": "압축 실패", - "edit": "편집", - "preview": "미리보기", - "previous": "이전", - "next": "다음", - "pageXOfY": "{{current}} / {{total}} 페이지", - "zoomOut": "축소하기", - "zoomIn": "확대", - "newFile": "새 파일", - "newFolder": "새 폴더", - "rename": "이름 변경", - "uploading": "업로드 중...", - "uploadingFile": "{{name}} 업로드 중...", - "fileName": "파일 이름", - "folderName": "폴더 이름", - "fileUploadedSuccessfully": "파일 \"{{name}}\"이 업로드되었습니다.", - "failedToUploadFile": "파일 업로드에 실패했습니다", - "fileDownloadedSuccessfully": "파일 \"{{name}}\"이 다운로드되었습니다.", - "failedToDownloadFile": "파일 다운로드에 실패했습니다", - "fileCreatedSuccessfully": "파일 \"{{name}}\"이 생성되었습니다.", - "folderCreatedSuccessfully": "폴더 \"{{name}}\"이 생성되었습니다.", - "failedToCreateItem": "아이템 생성에 실패했습니다", - "operationFailed": "{{name}}의 {{operation}} 작업에 실패했습니다: {{error}}", - "failedToResolveSymlink": "심볼릭 링크를 해결할 수 없습니다.", - "itemsDeletedSuccessfully": "항목 {{count}}개가 삭제되었습니다.", - "failedToDeleteItems": "항목 삭제에 실패했습니다", - "sudoPasswordRequired": "관리자 비밀번호가 필요합니다", - "enterSudoPassword": "이 작업을 계속하려면 sudo 비밀번호를 입력하세요.", - "sudoPassword": "Sudo 비밀번호", - "sudoOperationFailed": "sudo 작업이 실패했습니다.", - "sudoAuthFailed": "Sudo 인증에 실패했습니다.", - "dragFilesToUpload": "파일을 여기에 드롭하여 업로드하세요", - "emptyFolder": "이 폴더는 비어 있습니다", - "searchFiles": "파일을 검색하세요...", - "upload": "업로드", - "selectHostToStart": "파일 관리를 시작할 호스트를 선택하세요.", - "sshRequiredForFileManager": "파일 관리자는 SSH를 필요로 합니다. 이 호스트는 SSH가 활성화되어 있지 않습니다.", - "failedToConnect": "SSH 연결에 실패했습니다.", - "failedToLoadDirectory": "디렉터리를 불러오는 데 실패했습니다.", - "noSSHConnection": "SSH 연결을 사용할 수 없습니다.", - "copy": "복사", - "cut": "잘라내기", - "paste": "붙여넣기", - "copyPath": "경로 복사", - "copyPaths": "경로 복사", - "delete": "삭제", - "properties": "속성", - "refresh": "새로고침", - "downloadFiles": "{{count}}개 파일을 브라우저로 다운로드", - "copyFiles": "{{count}}개 항목 복사", - "cutFiles": "{{count}}개 항목 잘라내기", - "deleteFiles": "{{count}}개 항목 삭제", - "filesCopiedToClipboard": "{{count}}개 항목이 클립보드에 복사되었습니다.", - "filesCutToClipboard": "{{count}}개 항목을 잘라내어 클립보드에 저장했습니다.", - "pathCopiedToClipboard": "경로가 클립보드에 복사되었습니다", - "pathsCopiedToClipboard": "경로 {{count}}개가 클립보드에 복사되었습니다.", - "failedToCopyPath": "클립보드에 경로를 복사하는 데 실패했습니다.", - "movedItems": "{{count}}개 항목을 이동했습니다.", - "failedToDeleteItem": "항목 삭제에 실패했습니다", - "itemRenamedSuccessfully": "{{type}} 이름이 변경되었습니다.", - "failedToRenameItem": "항목 이름 변경에 실패했습니다", - "download": "다운로드", - "permissions": "권한", - "size": "크기", - "modified": "수정됨", - "path": "경로", - "confirmDelete": "{{name}}을(를) 정말로 삭제하시겠습니까?", - "permissionDenied": "권한이 거부되었습니다", - "serverError": "서버 오류", - "fileSavedSuccessfully": "파일이 저장되었습니다.", - "failedToSaveFile": "파일 저장에 실패했습니다", - "confirmDeleteSingleItem": "\"{{name}}\"을 영구적으로 삭제하시겠습니까?", - "confirmDeleteMultipleItems": "{{count}}개 항목을 영구적으로 삭제하시겠습니까?", - "confirmDeleteMultipleItemsWithFolders": "{{count}}개 항목을 영구적으로 삭제하시겠습니까? 폴더와 그 내용이 모두 포함됩니다.", - "confirmDeleteFolder": "폴더 \"{{name}}\"와 그 안의 모든 내용을 영구적으로 삭제하시겠습니까?", - "permanentDeleteWarning": "이 작업은 되돌릴 수 없습니다. 해당 항목은 서버에서 영구적으로 삭제됩니다.", - "recent": "최근의", - "pinned": "고정됨", - "folderShortcuts": "폴더 바로가기", - "failedToReconnectSSH": "SSH 세션 재연결에 실패했습니다.", - "openTerminalHere": "여기에서 터미널 열기", - "run": "실행", - "openTerminalInFolder": "이 폴더에서 터미널을 엽니다.", - "openTerminalInFileLocation": "해당 파일 위치에서 터미널을 엽니다.", - "runningFile": "실행 중 - {{file}}", - "onlyRunExecutableFiles": "실행 파일만 실행할 수 있습니다.", - "directories": "디렉터리", - "removedFromRecentFiles": "최근 파일에서 \"{{name}}\"을(를) 제거했습니다.", - "removeFailed": "제거 실패", - "unpinnedSuccessfully": "\"{{name}}\" 고정이 해제되었습니다.", - "unpinFailed": "핀 해제 실패", - "removedShortcut": "바로가기 \"{{name}}\"을(를) 제거했습니다.", - "removeShortcutFailed": "바로가기 제거 실패", - "clearedAllRecentFiles": "최근 파일을 모두 삭제했습니다.", - "clearFailed": "지우기 실패", - "removeFromRecentFiles": "최근 파일에서 삭제", - "clearAllRecentFiles": "최근 파일을 모두 삭제합니다.", - "unpinFile": "파일 고정 해제", - "removeShortcut": "바로가기 제거", - "pinFile": "파일 고정", - "addToShortcuts": "바로가기에 추가", - "pasteFailed": "붙여넣기 실패", - "noUndoableActions": "되돌릴 수 없는 작업", - "undoCopySuccess": "복사 작업 실행 취소 완료: 복사된 파일 {{count}}개를 삭제했습니다.", - "undoCopyFailedDelete": "실행 취소 실패: 복사된 파일을 삭제할 수 없습니다.", - "undoCopyFailedNoInfo": "실행 취소 실패: 복사된 파일 정보를 찾을 수 없습니다.", - "undoMoveSuccess": "이동 작업 실행 취소 완료: 파일 {{count}}개를 원래 위치로 되돌렸습니다.", - "undoMoveFailedMove": "실행 취소 실패: 파일을 되돌릴 수 없습니다.", - "undoMoveFailedNoInfo": "실행 취소 실패: 이동된 파일 정보를 찾을 수 없습니다.", - "undoDeleteNotSupported": "삭제 작업은 되돌릴 수 없습니다. 파일이 서버에서 영구적으로 삭제되었습니다.", - "undoTypeNotSupported": "지원되지 않는 실행 취소 작업 유형", - "undoOperationFailed": "실행 취소 작업이 실패했습니다.", - "unknownError": "알 수 없는 오류", - "confirm": "확인", - "find": "찾기...", - "replace": "바꾸기", - "downloadInstead": "대신 다운로드", - "keyboardShortcuts": "키보드 단축키", - "searchAndReplace": "검색 및 바꾸기", - "editing": "편집", - "search": "검색", - "findNext": "다음 찾기", - "findPrevious": "이전 찾기", - "save": "저장", - "selectAll": "모두 선택", - "undo": "실행 취소", - "redo": "다시 실행", - "moveLineUp": "줄을 위로 이동", - "moveLineDown": "줄을 아래로 이동", - "toggleComment": "주석 토글", - "autoComplete": "자동 완성", - "imageLoadError": "이미지를 불러오는 데 실패했습니다.", - "startTyping": "입력을 시작하세요...", - "unknownSize": "크기 미상", - "fileIsEmpty": "파일이 비어 있습니다", - "largeFileWarning": "대용량 파일 경고", - "largeFileWarningDesc": "이 파일의 크기는 {{size}}이므로 텍스트 모드로 열면 성능 문제가 발생할 수 있습니다.", - "fileNotFoundAndRemoved": "파일 \"{{name}}\"을(를) 찾을 수 없어 최근/고정된 파일 목록에서 제거되었습니다.", - "failedToLoadFile": "파일을 불러오지 못했습니다: {{error}}", - "serverErrorOccurred": "서버 오류가 발생했습니다. 나중에 다시 시도해 주세요.", - "autoSaveFailed": "자동 저장 실패", - "fileAutoSaved": "파일이 자동으로 저장되었습니다.", - "moveFileFailed": "{{name}}을(를) 이동하는 데 실패했습니다.", - "moveOperationFailed": "이동 작업이 실패했습니다.", - "canOnlyCompareFiles": "두 파일만 비교할 수 있습니다.", - "comparingFiles": "파일 비교: {{file1}} 및 {{file2}}", - "dragFailed": "드래그 작업이 실패했습니다.", - "filePinnedSuccessfully": "파일 \"{{name}}\"이 고정되었습니다.", - "pinFileFailed": "파일 고정 실패", - "fileUnpinnedSuccessfully": "파일 \"{{name}}\" 고정이 해제되었습니다.", - "unpinFileFailed": "파일 고정 해제에 실패했습니다.", - "shortcutAddedSuccessfully": "폴더 바로가기 \"{{name}}\"가 추가되었습니다.", - "addShortcutFailed": "바로가기 추가에 실패했습니다", - "operationCompletedSuccessfully": "{{operation}} 항목 {{count}}개 완료", - "operationCompleted": "{{operation}} {{count}}개 항목", - "downloadFileSuccess": "파일 {{name}}이 다운로드되었습니다.", - "downloadFileFailed": "다운로드 실패", - "moveTo": "{{name}}로 이동", - "diffCompareWith": "{{name}}와(과) 차이점을 비교합니다.", - "dragOutsideToDownload": "다운로드하려면 창 바깥쪽으로 드래그하세요({{count}}개 파일).", - "newFolderDefault": "새 폴더", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "항목 {{count}}개를 {{target}}(으)로 이동했습니다.", - "move": "이동", - "searchInFile": "파일에서 검색(Ctrl+F)", - "showKeyboardShortcuts": "키보드 단축키 표시", - "startWritingMarkdown": "마크다운 콘텐츠 작성을 시작하세요...", - "loadingFileComparison": "파일 비교 불러오는 중...", - "reload": "다시 불러오기", - "compare": "비교", - "sideBySide": "나란히", - "inline": "인라인", - "fileComparison": "파일 비교: {{file1}} vs {{file2}}", - "fileTooLarge": "파일 크기가 너무 큽니다: {{error}}", - "sshConnectionFailed": "SSH 연결에 실패했습니다. {{name}}({{ip}}:{{port}}) 연결을 확인하세요.", - "loadFileFailed": "파일을 불러오지 못했습니다: {{error}}", - "connecting": "연결 중...", - "connectedSuccessfully": "연결되었습니다", - "totpVerificationFailed": "TOTP 인증 실패", - "warpgateVerificationFailed": "Warpgate 인증 실패", - "authenticationFailed": "인증 실패", - "verificationCodePrompt": "인증 코드:", - "changePermissions": "권한 변경", - "currentPermissions": "현재 권한", - "owner": "소유자", - "group": "그룹", - "others": "기타", - "read": "읽기", - "write": "쓰기", - "execute": "실행", - "permissionsChangedSuccessfully": "권한이 변경되었습니다.", - "failedToChangePermissions": "권한 변경에 실패했습니다", - "name": "이름", - "sortByName": "이름", - "sortByDate": "수정일", - "sortBySize": "크기", - "ascending": "오름차순", - "descending": "내림차순", - "root": "뿌리", - "new": "새로운", - "sortBy": "정렬 기준", - "items": "품목", - "selected": "선택된", - "editor": "편집자", - "octal": "8진수", - "storage": "저장", - "disk": "디스크", - "used": "사용된", - "of": "~의", - "toggleSidebar": "사이드바 토글", - "cannotLoadPdf": "PDF 파일을 불러올 수 없습니다.", - "pdfLoadError": "PDF 파일을 불러오는 중 오류가 발생했습니다.", - "loadingPdf": "PDF 파일을 불러오는 중...", - "loadingPage": "페이지를 불러오는 중..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "호스트에 복사…", - "moveToHost": "호스트…로 이동", - "copyItemsToHost": "{{count}} 항목을… 호스트로 복사합니다.", - "moveItemsToHost": "{{count}} 항목을 호스트…로 이동합니다.", - "noHostsConnected": "다른 파일 관리자 호스트를 사용할 수 없습니다.", - "noHostsConnectedHint": "호스트 관리자에서 파일 관리자가 활성화된 SSH 호스트를 하나 더 추가하세요.", - "selectDestinationHost": "대상 호스트를 선택하세요", - "destinationPath": "목적지 경로", - "recentDestinations": "최근 여행지", - "collapseRecentDestinations": "최근 방문지 접기", - "expandRecentDestinations": "최근 방문지 펼치기", - "browseFolders": "대상 폴더를 찾아보세요", - "browseDestination": "경로를 찾아보거나 입력하세요.", - "confirmCopy": "복사", - "confirmMove": "이동하다", - "transferring": "… 전송 중", - "compressing": "… 압축 중", - "extracting": "… 추출 중", - "transferringItems": "{{current}} 개의 {{total}} 개 항목을…로 전송 중", - "transferSuccess": "이체 완료", - "transferError": "전송 실패", - "transferPartial": "전송이 완료되었지만 {{count}} 개의 오류가 발생했습니다.", - "transferPartialHint": "전송할 수 없습니다: {{paths}}", - "itemsSummary": "{{count}} 항목", - "destMustBeDirectory": "대상은 여러 항목을 전송하기 위한 디렉토리여야 합니다.", - "selectThisFolder": "이 폴더를 선택하세요", - "browsePathWillBeCreated": "이 폴더는 아직 존재하지 않습니다. 전송이 시작되면 생성됩니다.", - "browsePathError": "대상 호스트에서 이 경로를 열 수 없습니다.", - "goUp": "위로 올라가세요", - "copyFolderToHost": "폴더를 호스트로 복사하세요…", - "moveFolderToHost": "폴더를 호스트로 이동…", - "hostReady": "준비가 된", - "hostConnecting": "… 연결 중", - "hostDisconnected": "연결되지 않음", - "hostAuthRequired": "인증이 필요합니다. 먼저 이 호스트에서 파일 관리자를 여세요.", - "hostConnectionFailed": "연결 실패", - "metricsTitle": "이체 시간", - "metricsPrepare": "목적지 준비: {{duration}}", - "metricsCompress": "원본에서 압축: {{duration}}", - "metricsHopSourceRead": "출처 → 서버: {{throughput}}", - "metricsHopDestSftpWrite": "서버 → 대상(SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "서버 → 대상(로컬): {{throughput}}", - "metricsTransfer": "종단 간: {{throughput}} ({{duration}})", - "metricsExtract": "목적지에서 추출: {{duration}}", - "metricsSourceDelete": "원본에서 제거: {{duration}}", - "metricsTotal": "총계: {{duration}}", - "progressCompressing": "소스 호스트…에서 압축 중", - "progressExtracting": "대상…에서 추출 중", - "progressTransferring": "데이터 전송 중…", - "progressReconnecting": "… 다시 연결 중", - "parallelSegmentsLabel": "평행 환승 차선", - "parallelSegmentsOption": "{{count}} 차선", - "parallelSegmentsHint": "대용량 파일은 256MB 단위로 분할됩니다. 여러 레인을 사용하는 경우 (여러 전송을 동시에 시작하는 것처럼) 별도의 연결을 사용하여 전체 처리량을 높입니다.", - "progressTotalSpeed": "{{speed}} 총 ({{lanes}} 차선)", - "progressTransferringItems": "파일 전송 중 ({{current}} of {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} 파일", - "sourceNotDeletedPartial": "원본 파일은 보존되었습니다 (부분 전송).", - "jumpHostLimitation": "두 호스트 모두 Termix 서버에서 접근 가능해야 합니다. 호스트 간 직접 라우팅은 지원되지 않습니다.", - "cancel": "취소", - "methodLabel": "전송 방법", - "methodAuto": "자동", - "methodTar": "타르 아카이브", - "methodItemSftp": "파일별 SFTP", - "methodAutoHint": "파일 개수, 크기 및 압축률에 따라 tar 또는 파일별 SFTP 전송 방식을 선택합니다. 단일 파일의 경우 항상 스트리밍 SFTP를 사용합니다.", - "methodTarHint": "원본에서 압축하고, 하나의 아카이브로 전송한 후, 대상에서 압축을 해제합니다. 양쪽 Unix 호스트 모두에 tar 명령어가 필요합니다.", - "methodItemSftpHint": "SFTP를 통해 각 파일을 개별적으로 전송합니다. Windows를 포함한 모든 호스트에서 작동합니다.", - "methodPreviewLoading": "전송 방법 계산 중…", - "methodPreviewError": "전송 방법을 미리 볼 수 없습니다. 서버에서 전송을 시작하면 자동으로 방법을 선택합니다.", - "methodPreviewWillUseTar": "사용 대상: Tar 아카이브", - "methodPreviewWillUseItemSftp": "사용 방식: 파일별 SFTP", - "methodPreviewScanSummary": "{{fileCount}} 개 파일, {{totalSize}} 개 총계(소스 호스트에서 스캔됨).", - "methodItemSftpLimitation": "각 파일은 동일한 SFTP 스트림을 사용하여 단일 파일 복사 방식으로 순차적으로 처리됩니다. 모든 파일의 진행 상황이 합산되므로 파일 크기가 클수록 진행률 표시줄이 느리게 움직입니다.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "파일별 SFTP를 선택하셨습니다.", - "user_tar": "tar 압축 파일을 선택하셨습니다.", - "tar_unavailable": "두 호스트 중 하나 또는 둘 모두에서 Tar를 사용할 수 없습니다. 대신 파일별 SFTP가 사용됩니다.", - "windows_host": "Windows 호스트가 관련되어 있으며, tar는 사용되지 않습니다.", - "auto_multi_large": "자동: 압축 가능한 데이터가 포함된 대용량 파일({{largestSize}})을 비롯한 여러 파일 - tar 번들을 하나의 전송으로 묶습니다.", - "auto_single_large_in_archive": "자동: 이 세트에 하나의 큰 파일({{largestSize}})이 있습니다. 파일별 SFTP입니다.", - "auto_many_incompressible": "자동: 대부분 압축 불가능한 데이터 — 파일별 SFTP.", - "auto_many_files": "자동: 많은 파일({{fileCount}}) — tar는 파일당 오버헤드를 줄입니다.", - "auto_default": "자동: 이 세트에 대해 파일별 SFTP를 설정합니다." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "취소", - "progressCancelling": "… 취소 중", - "progressStalled": "정체됨", - "resumedHint": "다른 창에서 시작된 활성 전송에 다시 연결되었습니다.", - "transferCancelled": "이적 취소됨", - "transferCancelledCopyHint": "일부 파일이 대상 위치에 남아 있을 수 있습니다.", - "transferCancelledMoveHint": "대상 위치에 일부 파일이 남아 있을 수 있습니다. 원본 파일은 삭제되지 않았습니다.", - "cleanupDestFiles": "청소 목적지", - "cleanupDestFilesSuccess": "대상 위치에서 부분 파일을 제거했습니다.", - "cleanupDestFilesPartial": "일부 불완전한 파일이 삭제되지 않았습니다.", - "cleanupDestFilesNothing": "목적지에는 청소할 것이 아무것도 없습니다.", - "cleanupDestFilesError": "정리 실패", - "retryTransfer": "다시 해 보다", - "retryTransferError": "재시도 실패", - "transferFailedRetryHint": "대상에 일부 데이터가 남아 있습니다. 연결이 복구되면 재시도가 재개됩니다." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "SSH 터널 없음", - "createFirstTunnelMessage": "아직 SSH 터널을 생성하지 않았습니다. 시작하려면 호스트 관리자에서 터널 연결을 구성하세요.", - "connected": "연결됨", - "disconnected": "연결이 끊어졌습니다", - "connecting": "연결 중...", - "error": "오류", - "canceling": "취소 중...", - "connect": "연결", - "disconnect": "연결을 끊으세요", - "cancel": "취소", - "port": "포트", - "localPort": "로컬 포트", - "remotePort": "원격 포트", - "currentHostPort": "현재 호스트 포트", - "endpointPort": "엔드포인트 포트", - "bindIp": "로컬 IP", - "endpointSshConfig": "엔드포인트 SSH 구성", - "endpointSshHost": "엔드포인트 SSH 호스트", - "endpointSshHostPlaceholder": "구성된 호스트를 선택하세요", - "endpointSshHostRequired": "각 클라이언트 터널에 대해 엔드포인트 SSH 호스트를 선택하십시오.", - "attempt": "{{max}}회 중 {{current}}번째 시도", - "nextRetryIn": "{{seconds}}초 후 다시 시도", - "clientTunnels": "클라이언트 터널", - "clientTunnel": "클라이언트 터널", - "addClientTunnel": "클라이언트 터널 추가", - "noClientTunnels": "이 데스크톱에는 클라이언트 터널이 구성되어 있지 않습니다.", - "tunnelName": "터널 이름", - "remoteHost": "원격 호스트", - "autoStart": "자동 시작", - "clientAutoStartDesc": "이 데스크톱 클라이언트가 실행될 때 시작되어 연결 상태를 유지합니다.", - "clientManualStartDesc": "이 행의 시작 및 정지 버튼을 사용하십시오. Termix는 자동으로 열지 않습니다.", - "clientRemoteServerNote": "원격 포트 포워딩을 사용하려면 엔드포인트 SSH 서버에서 AllowTcpForwarding 및 GatewayPorts 설정이 필요할 수 있습니다. 이 데스크톱 연결이 끊어지면 원격 포트가 닫힙니다.", - "clientTunnelStarted": "클라이언트 터널이 시작되었습니다", - "clientTunnelStopped": "클라이언트 터널이 중지되었습니다.", - "tunnelTestSucceeded": "터널 시험 성공", - "tunnelTestFailed": "터널 테스트 실패", - "localSaved": "클라이언트 터널이 저장되었습니다", - "localSaveError": "로컬 클라이언트 터널을 저장하는 데 실패했습니다.", - "invalidBindIp": "로컬 IP는 유효한 IPv4 주소여야 합니다.", - "invalidLocalTargetIp": "로컬 대상 IP 주소는 유효한 IPv4 주소여야 합니다.", - "invalidLocalPort": "로컬 포트는 1에서 65535 사이여야 합니다.", - "invalidRemotePort": "원격 포트는 1에서 65535 사이여야 합니다.", - "invalidLocalTargetPort": "로컬 대상 포트는 1에서 65535 사이여야 합니다.", - "invalidEndpointPort": "엔드포인트 포트는 1에서 65535 사이여야 합니다.", - "duplicateAutoStartBind": "{{bind}}를 사용할 수 있는 자동 시작 클라이언트 터널은 하나뿐입니다.", - "manualControlError": "터널 상태 업데이트에 실패했습니다.", - "active": "활성", - "start": "시작", - "stop": "중지", - "test": "시험", - "type": "터널형", - "typeLocal": "로컬(-L)", - "typeRemote": "원격(-R)", - "typeDynamic": "동적(-D)", - "typeServerLocalDesc": "현재 호스트에서 엔드포인트까지.", - "typeServerRemoteDesc": "엔드포인트에서 현재 호스트로 돌아갑니다.", - "typeClientLocalDesc": "로컬 컴퓨터에서 엔드포인트까지.", - "typeClientRemoteDesc": "엔드포인트에서 로컬 컴퓨터로의 복귀.", - "typeClientDynamicDesc": "로컬 컴퓨터에 있는 양말.", - "typeDynamicDesc": "SOCKS5 CONNECT 트래픽을 SSH를 통해 전달합니다.", - "forwardDescriptionServerLocal": "현재 호스트 {{sourcePort}} → 엔드포인트 {{endpointPort}}.", - "forwardDescriptionServerRemote": "엔드포인트 {{endpointPort}} → 현재 호스트 {{sourcePort}}.", - "forwardDescriptionServerDynamic": "현재 호스트 {{sourcePort}}의 SOCKS.", - "forwardDescriptionClientLocal": "로컬 {{sourcePort}} → 원격 {{endpointPort}}.", - "forwardDescriptionClientRemote": "원격 {{sourcePort}} → 로컬 {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS는 로컬 포트 {{sourcePort}}에 있습니다.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → 양말 (SOCKS) via {{endpoint}}", - "autoNameClientLocal": "로컬 {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → 로컬 {{localPort}}", - "autoNameClientDynamic": "양말 {{localPort}} 을 통해 {{endpoint}}", - "route": "노선:", - "lastStarted": "마지막으로 시작함", - "lastTested": "최근 테스트 완료", - "lastError": "마지막 오류", - "maxRetries": "최대 재시도 횟수", - "maxRetriesDescription": "최대 재시도 횟수.", - "retryInterval": "재시도 간격(초)", - "retryIntervalDescription": "재시도 시도 간 대기 시간.", - "local": "로컬", - "remote": "원격", - "destination": "목적지", - "host": "주인", - "mode": "방법", - "noHostSelected": "호스트가 선택되지 않았습니다.", - "working": "일하고 있는..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "메모리", - "disk": "디스크", - "network": "네트워크", - "uptime": "가동 시간", - "processes": "프로세스", - "available": "사용 가능", - "free": "여유", - "connecting": "연결 중...", - "connectionFailed": "서버 연결에 실패했습니다.", - "naCpus": "CPU 해당 없음", - "cpuCores_one": "{{count}} 코어", - "cpuCores_other": "{{count}} 코어", - "cpuUsage": "CPU 사용량", - "memoryUsage": "메모리 사용량", - "diskUsage": "디스크 사용량", - "failedToFetchHostConfig": "호스트 구성을 가져오는 데 실패했습니다.", - "serverOffline": "서버 오프라인", - "cannotFetchMetrics": "오프라인 서버에서 메트릭을 가져올 수 없습니다.", - "totpFailed": "TOTP 인증 실패", - "noneAuthNotSupported": "서버 통계는 '없음' 인증 유형을 지원하지 않습니다.", - "load": "부하", - "systemInfo": "시스템 정보", - "hostname": "호스트 이름", - "operatingSystem": "운영 체제", - "kernel": "커널", - "seconds": "초", - "networkInterfaces": "네트워크 인터페이스", - "noInterfacesFound": "네트워크 인터페이스를 찾을 수 없습니다.", - "noProcessesFound": "프로세스를 찾을 수 없습니다.", - "loginStats": "SSH 로그인 통계", - "noRecentLoginData": "최근 로그인 데이터가 없습니다.", - "executingQuickAction": "{{name}} 실행 중...", - "quickActionSuccess": "{{name}}이(가) 완료되었습니다.", - "quickActionFailed": "{{name}} 실패", - "quickActionError": "{{name}} 실행에 실패했습니다.", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "수신 포트", - "protocol": "프로토콜", - "port": "포트", - "address": "주소", - "process": "프로세스", - "noData": "수신 포트 데이터 없음" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "방화벽", - "inactive": "비활성화됨", - "policy": "정책", - "rules": "규칙", - "noData": "방화벽 데이터가 없습니다.", - "action": "동작", - "protocol": "프로토콜", - "port": "포트", - "source": "소스", - "anywhere": "모든 위치" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "평균 부하", - "swap": "교환", - "architecture": "건축학", - "refresh": "새로 고치다", - "retry": "다시 해 보다" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "자체 호스팅 SSH 및 원격 데스크톱 관리", - "loginTitle": "Termix에 로그인하세요", - "registerTitle": "계정 생성", - "forgotPassword": "비밀번호를 잊으셨나요?", - "rememberMe": "30일 동안 기기 정보를 기억해 두세요 (TOTP 포함).", - "noAccount": "계정이 없으신가요?", - "hasAccount": "이미 계정이 있으신가요?", - "twoFactorAuth": "2단계 인증", - "enterCode": "인증 코드를 입력하세요", - "backupCode": "또는 백업 코드를 사용하세요.", - "verifyCode": "코드 확인", - "redirectingToApp": "앱으로 리디렉션 중...", - "sshAuthenticationRequired": "SSH 인증이 필요합니다", - "sshNoKeyboardInteractive": "키보드 기반 인증을 사용할 수 없습니다.", - "sshAuthenticationFailed": "인증 실패", - "sshAuthenticationTimeout": "인증 시간 초과", - "sshNoKeyboardInteractiveDescription": "서버는 키보드 입력 방식의 인증을 지원하지 않습니다. 비밀번호 또는 SSH 키를 입력해 주세요.", - "sshAuthFailedDescription": "입력하신 정보가 올바르지 않습니다. 유효한 정보를 입력하여 다시 시도해 주세요.", - "sshTimeoutDescription": "인증 시도 시간이 초과되었습니다. 다시 시도해 주세요.", - "sshProvideCredentialsDescription": "이 서버에 연결하려면 SSH 자격 증명을 입력하세요.", - "sshPasswordDescription": "이 SSH 연결에 사용할 비밀번호를 입력하세요.", - "sshKeyPasswordDescription": "SSH 키가 암호화되어 있으면 여기에 암호를 입력하세요.", - "passphraseRequired": "암호문이 필요합니다", - "passphraseRequiredDescription": "SSH 키가 암호화되어 있습니다. 키를 해제하려면 암호를 입력하십시오.", - "back": "뒤로", - "firstUser": "첫 번째 사용자", - "firstUserMessage": "첫 번째 사용자이므로 관리자 권한이 부여됩니다. 관리자 설정은 사이드바의 사용자 드롭다운 메뉴에서 확인할 수 있습니다. 오류라고 생각되면 Docker 로그를 확인하거나 GitHub 이슈를 생성해 주세요.", - "external": "외부", - "loginWithExternal": "외부 제공업체로 로그인", - "loginWithExternalDesc": "구성된 외부 ID 공급자를 사용하여 로그인하세요.", - "externalNotSupportedInElectron": "Electron 앱에서는 아직 외부 인증이 지원되지 않습니다. OIDC 로그인을 위해서는 웹 버전을 이용해 주세요.", - "resetPasswordButton": "비밀번호 재설정", - "sendResetCode": "재설정 코드 전송", - "resetCodeDesc": "사용자 이름을 입력하여 비밀번호 재설정 코드를 받으세요. 코드는 Docker 컨테이너 로그에 기록됩니다.", - "resetCode": "재설정 코드", - "verifyCodeButton": "코드 확인", - "enterResetCode": "사용자의 Docker 컨테이너 로그에 있는 6자리 코드를 입력하세요:", - "newPassword": "새 비밀번호", - "confirmNewPassword": "비밀번호 확인", - "enterNewPassword": "사용자 계정의 새 비밀번호를 입력하세요:", - "signUp": "회원가입", - "desktopApp": "데스크톱 앱", - "loggingInToDesktopApp": "데스크톱 앱에 로그인하기", - "loadingServer": "서버 불러오는 중...", - "dataLossWarning": "이 방식으로 비밀번호를 재설정하면 저장된 모든 SSH 호스트, 자격 증명 및 기타 암호화된 데이터가 삭제됩니다. 이 작업은 되돌릴 수 없습니다. 비밀번호를 잊어버렸고 로그인되어 있지 않은 경우에만 사용하세요.", - "authenticationDisabled": "인증 비활성화됨", - "authenticationDisabledDesc": "현재 모든 인증 방식이 비활성화되어 있습니다. 관리자에게 문의하세요.", - "attemptsRemaining": "남은 시도 횟수 {{count}} 회" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH 호스트 키 확인", - "keyChangedWarning": "SSH 호스트 키가 변경되었습니다.", - "firstConnectionTitle": "이 호스트에 처음 연결합니다.", - "firstConnectionDescription": "이 호스트의 신뢰성을 확인할 수 없습니다. 지문이 예상한 값과 일치하는지 확인하세요.", - "keyChangedDescription": "이 서버의 호스트 키가 마지막 접속 이후 변경되었습니다. 이는 보안 문제를 나타낼 수 있습니다.", - "previousKey": "이전 키", - "newFingerprint": "새 지문", - "fingerprint": "지문", - "verifyInstructions": "이 호스트를 신뢰한다면 '수락'을 클릭하여 계속 진행하고 향후 연결을 위해 이 지문을 저장하세요.", - "securityWarning": "보안 경고", - "acceptAndContinue": "수락 및 계속", - "acceptNewKey": "새 키를 수락하고 계속 진행하세요" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "데이터베이스에 연결할 수 없습니다.", - "unknownError": "알 수 없는 오류", - "loginFailed": "로그인 실패", - "failedPasswordReset": "비밀번호 재설정을 시작하는 데 실패했습니다.", - "failedVerifyCode": "재설정 코드 확인에 실패했습니다.", - "failedCompleteReset": "비밀번호 재설정을 완료하지 못했습니다.", - "invalidTotpCode": "잘못된 TOTP 코드", - "failedOidcLogin": "OIDC 로그인 시작에 실패했습니다.", - "silentSigninOidcUnavailable": "자동 로그인 요청이 있었지만 OIDC 로그인이 불가능합니다.", - "failedUserInfo": "로그인 후 사용자 정보를 가져오는 데 실패했습니다.", - "oidcAuthFailed": "OIDC 인증에 실패했습니다.", - "invalidAuthUrl": "백엔드에서 잘못된 인증 URL을 수신했습니다.", - "requiredField": "이 항목은 필수 입력 사항입니다.", - "minLength": "최소 길이는 {{min}}입니다.", - "passwordMismatch": "비밀번호가 일치하지 않습니다", - "passwordLoginDisabled": "현재 사용자 이름/비밀번호 로그인 기능이 비활성화되어 있습니다.", - "sessionExpired": "세션이 만료되었습니다. 다시 로그인해 주세요.", - "totpRateLimited": "시도 횟수 제한: TOTP 인증 시도 횟수가 너무 많습니다. 나중에 다시 시도해 주세요.", - "totpRateLimitedWithTime": "시도 횟수 제한: TOTP 인증 시도 횟수가 너무 많습니다. 다시 시도하기 전에 {{time}} 초 동안 기다려 주세요.", - "resetCodeRateLimited": "시도 횟수 제한: 인증 시도 횟수가 너무 많습니다. 나중에 다시 시도해 주세요.", - "resetCodeRateLimitedWithTime": "시도 횟수 제한: 인증 시도 횟수가 너무 많습니다. 다시 시도하기 전에 {{time}} 초 동안 기다려 주세요.", - "authTokenSaveFailed": "인증 토큰 저장에 실패했습니다.", - "failedToLoadServer": "서버 로드 실패" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "관리자에 의해 신규 계정 등록이 현재 비활성화되었습니다. 로그인하시거나 관리자에게 문의해 주세요.", - "userNotAllowed": "이 계정은 등록 권한이 없습니다. 관리자에게 문의하세요.", - "databaseConnectionFailed": "데이터베이스 서버에 연결하지 못했습니다.", - "resetCodeSent": "Docker 로그에 전송된 재설정 코드", - "codeVerified": "코드 검증이 완료되었습니다.", - "passwordResetSuccess": "비밀번호 재설정 성공", - "loginSuccess": "로그인 성공", - "registrationSuccess": "등록 완료" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "구성된 SSH 호스트를 대상으로 하는 로컬 데스크톱 터널.", - "c2sTunnelPresets": "클라이언트 터널 사전 설정", - "c2sTunnelPresetsDesc": "이 데스크톱 클라이언트의 로컬 터널 목록을 명명된 서버 사전 설정으로 저장하거나, 사전 설정을 이 클라이언트로 다시 불러옵니다.", - "c2sTunnelPresetsUnavailable": "클라이언트 터널 사전 설정은 데스크톱 클라이언트에서만 사용할 수 있습니다.", - "c2sPresetName": "사전 설정 이름", - "c2sPresetNamePlaceholder": "클라이언트 사전 설정 이름", - "c2sPresetToLoad": "미리 설정하여 로드", - "c2sNoPresetSelected": "선택된 사전 설정이 없습니다.", - "c2sNoPresets": "저장된 프리셋이 없습니다.", - "c2sLoadPreset": "짐", - "c2sCurrentLocalConfig": "이 데스크톱에 {{count}} 로컬 클라이언트 터널이 구성되었습니다.", - "c2sPresetSyncNote": "사전 설정은 명시적인 스냅샷입니다. 사전 설정을 로드하면 이 데스크톱 클라이언트의 로컬 클라이언트 터널 목록이 대체됩니다.", - "c2sPresetSaved": "클라이언트 터널 사전 설정이 저장되었습니다.", - "c2sPresetLoaded": "클라이언트 터널 사전 설정이 로컬에 로드됨", - "c2sPresetRenamed": "클라이언트 터널 사전 설정 이름 변경됨", - "c2sPresetDeleted": "클라이언트 터널 사전 설정이 삭제되었습니다.", - "c2sPresetLoadError": "클라이언트 터널 사전 설정 불러오기에 실패했습니다." + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "언어", - "keyPassword": "키 비밀번호", - "pastePrivateKey": "여기에 개인 키를 붙여넣으세요...", - "localListenerHost": "127.0.0.1 (현지에서 청취)", - "localTargetHost": "127.0.0.1 (이 컴퓨터의 대상 주소)", - "socksListenerHost": "127.0.0.1 (SOCKS 리스너)", - "enterPassword": "비밀번호를 입력하세요", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "대시보드", - "loading": "대시보드를 불러오는 중...", - "github": "깃허브", - "support": "문의하기", - "discord": "디스코드", - "serverOverview": "서버 개요", - "version": "버전", - "upToDate": "최신 상태", - "updateAvailable": "업데이트 가능", - "beta": "베타", - "uptime": "가동 시간", - "database": "데이터베이스", - "healthy": "정상", - "error": "오류", - "totalHosts": "전체 호스트", - "totalTunnels": "전체 터널", - "totalCredentials": "총 자격 증명", - "recentActivity": "최근 활동", - "reset": "초기화", - "loadingRecentActivity": "최근 활동 불러오는 중...", - "noRecentActivity": "최근 활동 없음", - "quickActions": "빠른 실행", - "addHost": "호스트 추가", - "addCredential": "자격 증명 추가", - "adminSettings": "관리자 설정", - "userProfile": "사용자 프로필", - "serverStats": "서버 통계", - "loadingServerStats": "서버 통계 불러오는 중...", - "noServerData": "서버 데이터가 없습니다.", + "title": "Dashboard", + "loading": "Loading dashboard...", + "github": "GitHub", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "대시보드 맞춤 설정", - "dashboardSettings": "대시보드 설정", - "enableDisableCards": "카드 활성화/비활성화", - "resetLayout": "기본값으로 재설정", - "serverOverviewCard": "서버 개요", - "recentActivityCard": "최근 활동", - "networkGraphCard": "네트워크 그래프", - "networkGraph": "네트워크 그래프", - "quickActionsCard": "빠른 조치", - "serverStatsCard": "서버 통계", - "panelMain": "기본", - "panelSide": "옆", - "justNow": "방금" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "안정적인", - "hostsOnline": "호스트 온라인", - "activeTunnels": "활성 터널", - "registerNewServer": "새 서버를 등록하세요", - "storeSshKeysOrPasswords": "SSH 키 또는 암호를 저장하세요", - "manageUsersAndRoles": "사용자 및 역할 관리", - "manageYourAccount": "계정 관리하기", - "hostStatus": "호스트 상태", - "noHostsConfigured": "구성된 호스트가 없습니다.", - "online": "온라인", - "offline": "오프라인", - "onlineLower": "온라인", - "nodes": "{{count}} 노드", - "add": "추가하다:", - "commandPalette": "커맨드 팔레트", - "done": "완료", - "editModeInstructions": "카드를 드래그하여 순서를 변경하세요. 열 구분선을 드래그하여 열 크기를 조정하세요. 카드의 아래쪽 가장자리를 드래그하여 높이를 조정하세요. 휴지통을 클릭하여 삭제하세요.", - "empty": "비어 있는", - "clear": "분명한" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "호스트 추가", - "addGroup": "그룹 추가", - "addLink": "링크 추가", - "zoomIn": "확대", - "zoomOut": "축소", - "resetView": "보기 재설정", - "selectHost": "호스트를 선택하세요", - "chooseHost": "호스트를 선택하세요...", - "parentGroup": "상위 그룹", - "noGroup": "그룹 없음", - "groupName": "그룹 이름", - "color": "색상", - "source": "소스", - "target": "대상", - "moveToGroup": "그룹으로 이동", - "selectGroup": "그룹을 선택하세요...", - "addConnection": "연결 추가", - "hostDetails": "호스트 정보", - "removeFromGroup": "그룹에서 제거", - "addHostHere": "여기에 호스트를 추가하세요", - "editGroup": "그룹 편집", - "delete": "삭제", - "add": "추가", - "create": "생성", - "move": "이동", - "connect": "연결", - "createGroup": "그룹 생성", - "selectSourcePlaceholder": "소스 선택...", - "selectTargetPlaceholder": "대상을 선택하세요...", - "invalidFile": "잘못된 파일입니다", - "hostAlreadyExists": "호스트가 이미 토폴로지에 있습니다.", - "connectionExists": "이미 연결이 존재합니다", - "unknown": "알 수 없음", - "name": "이름", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "상태", - "failedToAddNode": "노드를 추가하는 데 실패했습니다.", - "sourceDifferentFromTarget": "소스와 대상은 서로 달라야 합니다.", - "exportJSON": "JSON 내보내기", - "importJSON": "JSON 가져오기", - "terminal": "터미널", - "fileManager": "파일 관리자", - "tunnel": "터널", - "docker": "도커", - "serverStats": "서버 통계", - "noNodes": "아직 노드가 없습니다." + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "이 호스트에서는 Docker가 활성화되어 있지 않습니다.", - "validating": "Docker 유효성 검사 중...", - "connecting": "연결 중...", - "error": "오류", - "version": "도커 {{version}}", - "connectionFailed": "Docker에 연결하지 못했습니다.", - "containerStarted": "컨테이너 {{name}}이(가) 시작되었습니다.", - "failedToStartContainer": "컨테이너 {{name}} 시작에 실패했습니다.", - "containerStopped": "{{name}} 컨테이너가 중지되었습니다.", - "failedToStopContainer": "컨테이너 {{name}}을(를) 중지하는 데 실패했습니다.", - "containerRestarted": "{{name}} 컨테이너가 재시작되었습니다.", - "failedToRestartContainer": "컨테이너 {{name}} 재시작에 실패했습니다.", - "containerPaused": "{{name}} 컨테이너가 일시 중지되었습니다.", - "containerUnpaused": "{{name}} 컨테이너의 일시 중지가 해제되었습니다.", - "failedToTogglePauseContainer": "컨테이너 {{name}}의 일시 정지 상태를 전환하는 데 실패했습니다.", - "containerRemoved": "{{name}} 컨테이너가 제거되었습니다.", - "failedToRemoveContainer": "컨테이너 {{name}}을(를) 제거하는 데 실패했습니다.", - "image": "이미지", - "ports": "포트", - "noPorts": "포트 없음", - "start": "시작", - "confirmRemoveContainer": "컨테이너 \"{{name}}\"을(를) 정말로 제거하시겠습니까? 이 작업은 되돌릴 수 없습니다.", - "runningContainerWarning": "경고: 이 컨테이너는 현재 실행 중입니다. 제거하려면 먼저 컨테이너가 중지되어야 합니다.", - "loadingContainers": "컨테이너 불러오는 중...", - "manager": "도커 관리자", - "autoRefresh": "자동 새로 고침", - "timestamps": "타임스탬프", - "lines": "윤곽", - "filterLogs": "로그 필터링...", - "refresh": "새로 고치다", - "download": "다운로드", - "clear": "분명한", - "logsDownloaded": "로그 파일이 성공적으로 다운로드되었습니다.", - "last50": "마지막 50개", - "last100": "마지막 100개", - "last500": "마지막 500개", - "last1000": "마지막 1000개", - "allLogs": "모든 로그", - "noLogsMatching": "\"{{query}} \" 와 일치하는 로그가 없습니다.", - "noLogsAvailable": "로그 파일이 없습니다.", - "noContainersFound": "컨테이너를 찾을 수 없습니다.", - "noContainersFoundHint": "이 호스트에는 사용 가능한 Docker 컨테이너가 없습니다.", - "searchPlaceholder": "컨테이너 검색...", - "allStatuses": "모든 상태", - "stateRunning": "달리기", - "statePaused": "일시 중지됨", - "stateExited": "나갔습니다", - "stateRestarting": "재시작 중", - "noContainersMatchFilters": "필터 조건에 맞는 컨테이너가 없습니다.", - "noContainersMatchFiltersHint": "검색어 또는 필터 조건을 조정해 보세요.", - "failedToFetchStats": "컨테이너 통계를 가져오는 데 실패했습니다.", - "containerNotRunning": "컨테이너가 실행 중이 아닙니다", - "startContainerToViewStats": "컨테이너를 시작하여 통계를 확인하세요.", - "loadingStats": "통계 불러오는 중...", - "errorLoadingStats": "통계 불러오기 오류", - "noStatsAvailable": "이용 가능한 통계가 없습니다.", - "cpuUsage": "CPU 사용량", - "current": "현재", - "memoryUsage": "메모리 사용량", - "networkIo": "네트워크 I/O", - "input": "입력", - "output": "출력", - "blockIo": "블록 I/O", - "read": "읽기", - "write": "쓰기", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "컨테이너 정보", - "name": "이름", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "상태", - "containerMustBeRunning": "콘솔에 접속하려면 컨테이너가 실행 중이어야 합니다.", - "verificationCodePrompt": "인증 코드를 입력하세요", - "totpVerificationFailed": "TOTP 인증에 실패했습니다. 다시 시도해 주세요.", - "warpgateVerificationFailed": "워프게이트 인증에 실패했습니다. 다시 시도해 주세요.", - "connectedTo": "{{containerName}}에 연결됨", - "disconnected": "연결이 끊어졌습니다", - "consoleError": "콘솔 오류", - "errorMessage": "오류: {{message}}", - "failedToConnect": "컨테이너에 연결하지 못했습니다.", - "console": "콘솔", - "selectShell": "셸 선택", - "bash": "세게 때리다", - "sh": "쉿", - "ash": "금연 건강 증진 협회", - "connect": "연결", - "disconnect": "연결 끊기", - "notConnected": "연결되지 않음", - "clickToConnect": "연결을 클릭하여 셸 세션을 시작하세요.", - "connectingTo": "{{containerName}}에 연결 중...", - "containerNotFound": "컨테이너를 찾을 수 없습니다", - "backToList": "목록으로 돌아가기", - "logs": "로그", - "stats": "통계", - "consoleTab": "콘솔", - "startContainerToAccess": "콘솔에 접속하려면 컨테이너를 시작하세요." + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "일반적인", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "사용자", - "sectionSessions": "세션", - "sectionRoles": "역할", - "sectionDatabase": "데이터 베이스", - "sectionApiKeys": "API 키", - "allowRegistration": "사용자 등록 허용", - "allowRegistrationDesc": "신규 사용자가 직접 등록할 수 있도록 하세요", - "allowPasswordLogin": "비밀번호 로그인 허용", - "allowPasswordLoginDesc": "사용자 이름/비밀번호로 로그인", - "oidcAutoProvision": "OIDC 자동 프로비저닝", - "oidcAutoProvisionDesc": "회원가입이 비활성화된 경우에도 OIDC 사용자를 위한 계정을 자동으로 생성합니다.", - "allowPasswordReset": "비밀번호 재설정 허용", - "allowPasswordResetDesc": "Docker 로그를 통해 재설정 코드를 확인하세요.", - "sessionTimeout": "세션 시간 제한", - "hours": "시간", - "sessionTimeoutRange": "최소 1시간 · 최대 720시간", - "monitoringDefaults": "모니터링 기본 설정", - "statusCheck": "상태 확인", - "metrics": "측정 기준", - "sec": "비서", - "logLevel": "로그 수준", - "enableGuacamole": "과카몰리 활성화", - "enableGuacamoleDesc": "RDP/VNC 원격 데스크톱", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "SSO를 위해 OpenID Connect를 구성하십시오. * 표시된 필드는 필수 입력 사항입니다.", - "oidcClientId": "클라이언트 ID", - "oidcClientSecret": "고객 비밀", - "oidcAuthUrl": "인증 URL", - "oidcIssuerUrl": "발급자 URL", - "oidcTokenUrl": "토큰 URL", - "oidcUserIdentifier": "사용자 식별자 경로", - "oidcDisplayName": "표시 이름 경로", - "oidcScopes": "스코프", - "oidcUserinfoUrl": "사용자 정보 URL 재정의", - "oidcAllowedUsers": "허용된 사용자", - "oidcAllowedUsersDesc": "한 줄에 이메일 하나씩 보내주세요. 모든 이메일을 수신하려면 비워 두세요.", - "removeOidc": "제거하다", - "usersCount": "{{count}} 사용자", - "createUser": "만들다", - "newRole": "새로운 역할", - "roleName": "이름", - "roleDisplayName": "표시 이름", - "roleDescription": "설명", - "rolesCount": "{{count}} 역할", - "createRole": "만들다", - "creating": "생성 중...", - "exportDatabase": "내보내기 데이터베이스", - "exportDatabaseDesc": "모든 호스트, 자격 증명 및 설정의 백업을 다운로드하세요.", - "export": "내보내기", - "exporting": "내보내는 중...", - "importDatabase": "데이터베이스 가져오기", - "importDatabaseDesc": ".sqlite 백업 파일에서 복원", - "importDatabaseSelected": "선택됨: {{name}}", - "selectFile": "파일을 선택하세요", - "changeFile": "변화", - "import": "가져오기", - "importing": "가져오는 중...", - "apiKeysCount": "{{count}} 키", - "newApiKey": "새 API 키", - "apiKeyCreatedWarning": "키가 생성되었습니다. 지금 복사하세요. 다시 표시되지 않습니다.", - "apiKeyName": "이름", - "apiKeyUser": "사용자", - "apiKeySelectUser": "사용자를 선택하세요...", - "apiKeyExpiresAt": "만료일", - "createKey": "키를 생성합니다", - "apiKeyNoExpiry": "유효기간 없음", - "revokedBadge": "취소됨", - "authTypeDual": "이중 인증", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "현지의", - "adminStatusAdministrator": "관리자", - "adminStatusRegularUser": "일반 사용자", - "adminBadge": "관리자", - "systemBadge": "시스템", - "customBadge": "관습", - "youBadge": "너", - "sessionsActive": "{{count}} 활성", - "sessionActive": "활성: {{time}}", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "모두", - "revokeAllSessionsSuccess": "해당 사용자의 모든 세션이 취소되었습니다.", - "revokeAllSessionsFailed": "세션 취소에 실패했습니다", - "revokeSessionFailed": "세션 취소에 실패했습니다.", - "addRole": "역할 추가", - "noCustomRoles": "사용자 지정 역할이 정의되지 않았습니다.", - "removeRoleFailed": "역할 제거에 실패했습니다", - "assignRoleFailed": "역할 할당에 실패했습니다", - "deleteRoleFailed": "역할 삭제에 실패했습니다", - "userAdminAccess": "관리자", - "userAdminAccessDesc": "모든 관리자 설정에 대한 전체 액세스 권한", - "userRoles": "역할", - "revokeAllUserSessions": "모든 세션 취소", - "revokeAllUserSessionsDesc": "모든 기기에서 강제로 다시 로그인합니다", - "revoke": "취소", - "deleteUserWarning": "이 사용자를 삭제하면 영구적으로 삭제됩니다.", - "deleteUser": "삭제 {{username}}", - "deleting": "삭제 중...", - "deleteUserFailed": "사용자 삭제에 실패했습니다", - "deleteUserSuccess": "사용자 \"{{username}}\" 삭제됨", - "deleteRoleSuccess": "역할 \"{{name}}\"이 삭제되었습니다", - "revokeKeySuccess": "키 \"{{name}}\" 취소됨", - "revokeKeyFailed": "키 취소에 실패했습니다.", - "copiedToClipboard": "클립보드에 복사됨", - "done": "완료", - "createUserTitle": "사용자 생성", - "createUserDesc": "새로운 로컬 계정을 만드세요.", - "createUserUsername": "사용자 이름", - "createUserPassword": "비밀번호", - "createUserPasswordHint": "최소 6자 이상.", - "createUserEnterUsername": "사용자 이름을 입력하세요", - "createUserEnterPassword": "비밀번호를 입력하세요", - "createUserSubmit": "사용자 생성", - "editUserTitle": "사용자 관리: {{username}}", - "editUserDesc": "역할, 관리자 상태, 세션 및 계정 설정을 편집합니다.", - "editUserUsername": "사용자 이름", - "editUserAuthType": "인증 유형", - "editUserAdminStatus": "관리자 상태", - "editUserUserId": "사용자 ID", - "linkAccountTitle": "OIDC를 비밀번호 계정에 연결", - "linkAccountDesc": "OIDC 계정 {{username}} 을 기존 로컬 계정과 병합합니다.", - "linkAccountWarningTitle": "이것은 다음과 같을 것입니다:", - "linkAccountEffect1": "OIDC 전용 계정을 삭제하세요", - "linkAccountEffect2": "대상 계정에 OIDC 로그인을 추가합니다.", - "linkAccountEffect3": "OIDC 로그인과 비밀번호 로그인 모두 허용", - "linkAccountTargetUsername": "대상 사용자 이름", - "linkAccountTargetPlaceholder": "연결할 로컬 계정 사용자 이름을 입력하세요.", - "linkAccounts": "계정 연동", - "linkAccountSuccess": "\"{{username}} \"에 연결된 OIDC 계정", - "linkAccountFailed": "OIDC 계정 연결에 실패했습니다.", - "linkAccountInProgress": "연결 중...", - "saving": "저장 중...", - "updateRegistrationFailed": "등록 설정 업데이트에 실패했습니다.", - "updatePasswordLoginFailed": "비밀번호 로그인 설정 업데이트에 실패했습니다.", - "updateOidcAutoProvisionFailed": "OIDC 자동 프로비저닝 설정 업데이트에 실패했습니다.", - "updatePasswordResetFailed": "비밀번호 재설정 설정 업데이트에 실패했습니다.", - "sessionTimeoutRange2": "세션 시간 초과는 1시간에서 720시간 사이여야 합니다.", - "sessionTimeoutSaved": "세션 시간 제한이 저장되었습니다.", - "sessionTimeoutSaveFailed": "세션 시간 초과 저장 실패", - "monitoringIntervalInvalid": "잘못된 간격 값", - "monitoringSaved": "모니터링 설정이 저장되었습니다.", - "monitoringSaveFailed": "모니터링 설정을 저장하는 데 실패했습니다.", - "guacamoleSaved": "과카몰리 설정이 저장되었습니다.", - "guacamoleSaveFailed": "과카몰리 설정 저장에 실패했습니다.", - "guacamoleUpdateFailed": "과카몰리 설정 업데이트에 실패했습니다.", - "logLevelUpdateFailed": "로그 레벨 업데이트에 실패했습니다.", - "oidcSaved": "OIDC 구성이 저장되었습니다.", - "oidcSaveFailed": "OIDC 설정을 저장하는 데 실패했습니다.", - "oidcRemoved": "OIDC 구성이 제거되었습니다.", - "oidcRemoveFailed": "OIDC 설정을 제거하는 데 실패했습니다.", - "createUserRequired": "사용자 이름과 비밀번호가 필요합니다.", - "createUserPasswordTooShort": "비밀번호는 최소 6자 이상이어야 합니다.", - "createUserSuccess": "사용자 \"{{username}}\"이 생성했습니다", - "createUserFailed": "사용자 생성에 실패했습니다", - "updateAdminStatusFailed": "관리자 상태 업데이트에 실패했습니다.", - "allSessionsRevoked": "모든 세션이 취소되었습니다", - "revokeSessionsFailed": "세션 취소에 실패했습니다", - "createRoleRequired": "이름과 표시 이름은 필수 입력 사항입니다.", - "createRoleSuccess": "역할 \"{{name}}\"이 생성되었습니다", - "createRoleFailed": "역할 생성에 실패했습니다", - "apiKeyNameRequired": "키 이름은 필수 입력 사항입니다.", - "apiKeyUserRequired": "사용자 ID가 필요합니다.", - "apiKeyCreatedSuccess": "API 키 \"{{name}}\"가 생성되었습니다", - "apiKeyCreateFailed": "API 키 생성에 실패했습니다.", - "exportSuccess": "데이터베이스 내보내기가 성공적으로 완료되었습니다.", - "exportFailed": "데이터베이스 내보내기 실패", - "importSelectFile": "먼저 파일을 선택하세요.", - "importCompleted": "가져오기 완료: {{total}} 개 항목 가져오기 완료, {{skipped}} 개 항목 건너뛰기", - "importFailed": "가져오기 실패: {{error}}", - "importError": "데이터베이스 가져오기 실패" + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "주인", - "hostPlaceholder": "192.168.1.1 또는 example.com", - "portLabel": "포트", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "사용자 이름", - "usernamePlaceholder": "사용자 이름", - "authLabel": "권한", - "passwordLabel": "비밀번호", - "passwordPlaceholder": "비밀번호", - "privateKeyLabel": "개인 키", - "privateKeyPlaceholder": "개인 키를 붙여넣으세요...", - "credentialLabel": "신임장", - "credentialPlaceholder": "저장된 자격 증명을 선택하세요", - "connectToTerminal": "터미널에 연결", - "connectToFiles": "파일에 연결" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "터미널이 선택되지 않았습니다.", - "noTerminalSelectedHint": "SSH 터미널 탭을 열어 명령 기록을 확인하세요.", - "searchPlaceholder": "검색 기록...", - "clearAll": "모두 지우기", - "noHistoryEntries": "기록 없음", - "trackingDisabled": "이용 내역 추적 기능이 비활성화되었습니다.", - "trackingDisabledHint": "명령어를 기록하려면 프로필 설정에서 해당 기능을 활성화하세요." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "주요 녹음", - "recordToTerminals": "터미널에 녹음", - "selectAll": "모두", - "selectNone": "없음", - "noTerminalTabsOpen": "터미널 탭이 열려 있지 않습니다.", - "selectTerminalsAbove": "위의 터미널을 선택하세요", - "broadcastInputPlaceholder": "여기에 입력하여 키 입력을 전송하세요...", - "stopRecording": "녹화 중지", - "startRecording": "녹화 시작", - "settingsTitle": "설정", - "enableRightClickCopyPaste": "마우스 오른쪽 버튼을 클릭하여 복사/붙여넣기를 활성화합니다." + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "공들여 나열한 것", - "selectLayoutAbove": "위의 레이아웃을 선택하세요", - "selectLayoutHint": "표시할 창의 개수를 선택하세요", - "panesTitle": "판", - "openTabsTitle": "열려있는 탭", - "dragTabsHint": "탭을 위쪽 창으로 드래그하거나 빠른 할당 기능을 사용하세요.", - "dropHere": "여기에 내려놓으세요", - "emptyPane": "비어 있는", - "dashboard": "계기반", - "clearSplitScreen": "선명한 분할 화면", - "quickAssign": "빠른 배정", - "alreadyAssigned": "패널 {{index}}", - "splitTab": "분할 탭", - "addToSplit": "분할에 추가", - "removeFromSplit": "분할에서 제거", - "assignToPane": "창에 할당" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "코드 조각 생성", - "createSnippetDescription": "빠른 실행을 위해 새로운 명령 스니펫을 만드세요.", - "nameLabel": "이름", - "namePlaceholder": "예: Nginx 재시작", - "descriptionLabel": "설명", - "descriptionPlaceholder": "선택적 설명", - "optional": "선택 과목", - "folderLabel": "접는 사람", - "noFolder": "폴더 없음 (미분류)", - "commandLabel": "명령", - "commandPlaceholder": "예: sudo systemctl restart nginx", - "cancel": "취소", - "createSnippetButton": "코드 조각 생성", - "createFolderTitle": "폴더 생성", - "createFolderDescription": "메모들을 폴더별로 정리하세요", - "folderNameLabel": "폴더 이름", - "folderNamePlaceholder": "예: 시스템 명령, Docker 스크립트", - "folderColorLabel": "폴더 색상", - "folderIconLabel": "폴더 아이콘", - "previewLabel": "시사", - "folderNameFallback": "폴더 이름", - "createFolderButton": "폴더 생성", - "targetTerminals": "대상 터미널", - "selectAll": "모두", - "selectNone": "없음", - "noTerminalTabsOpen": "터미널 탭이 열려 있지 않습니다.", - "searchPlaceholder": "검색 스니펫...", - "newSnippet": "새로운 스니펫", - "newFolder": "새 폴더", - "run": "달리다", - "noSnippetsInFolder": "이 폴더에는 스니펫이 없습니다.", - "uncategorized": "분류되지 않음", - "editSnippetTitle": "코드 조각 편집", - "editSnippetDescription": "이 명령 조각을 업데이트하세요", - "saveSnippetButton": "변경 사항 저장", - "createSuccess": "코드 조각이 성공적으로 생성되었습니다.", - "createFailed": "코드 조각을 생성하는 데 실패했습니다.", - "updateSuccess": "코드 조각이 성공적으로 업데이트되었습니다.", - "updateFailed": "코드 조각 업데이트에 실패했습니다.", - "deleteFailed": "코드 조각을 삭제하는 데 실패했습니다.", - "folderCreateSuccess": "폴더가 성공적으로 생성되었습니다.", - "folderCreateFailed": "폴더 생성에 실패했습니다", - "editFolderTitle": "폴더 편집", - "editFolderDescription": "이 폴더의 이름을 바꾸거나 모양을 변경하세요.", - "saveFolderButton": "변경 사항 저장", - "editFolder": "편집 폴더", - "deleteFolder": "폴더 삭제", - "folderDeleteSuccess": "폴더 \"{{name}}\"이 삭제되었습니다", - "folderDeleteFailed": "폴더 삭제에 실패했습니다", - "folderEditSuccess": "폴더가 성공적으로 업데이트되었습니다.", - "folderEditFailed": "폴더 업데이트에 실패했습니다", - "confirmRunMessage": "\"{{name}} \"를 실행하시겠습니까?", - "confirmRunButton": "달리다", - "runSuccess": "{{name}}\"을 {{count}} 터미널에서 실행했습니다.", - "copySuccess": "\"{{name}}\"를 클립보드에 복사했습니다.", - "shareTitle": "스니펫 공유", - "shareUser": "사용자", - "shareRole": "역할", - "selectUser": "사용자를 선택하세요...", - "selectRole": "역할을 선택하세요...", - "shareSuccess": "스니펫이 성공적으로 공유되었습니다.", - "shareFailed": "코드 조각 공유에 실패했습니다.", - "revokeSuccess": "접근 권한이 취소되었습니다", - "revokeFailed": "접근 권한 취소에 실패했습니다.", - "currentAccess": "현재 액세스", - "shareLoadError": "공유 데이터 불러오기에 실패했습니다", - "loading": "로딩 중...", - "close": "닫다", - "reorderFailed": "스니펫 순서를 저장하는 데 실패했습니다." + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "계정", - "sectionAppearance": "모습", - "sectionSecurity": "보안", - "sectionApiKeys": "API 키", - "sectionC2sTunnels": "C2S 터널", - "usernameLabel": "사용자 이름", - "roleLabel": "역할", - "roleAdministrator": "관리자", - "authMethodLabel": "인증 방식", - "authMethodLocal": "현지의", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "~에", - "twoFaOff": "끄다", - "versionLabel": "버전", - "deleteAccount": "계정 삭제", - "deleteAccountDescription": "계정을 영구적으로 삭제하세요", - "deleteButton": "삭제", - "deleteAccountPermanent": "이 조치는 영구적이며 되돌릴 수 없습니다.", - "deleteAccountWarning": "모든 세션, 호스트, 자격 증명 및 설정이 영구적으로 삭제됩니다.", - "confirmPasswordDeletePlaceholder": "비밀번호를 입력하여 확인하세요.", - "languageLabel": "언어", - "themeLabel": "주제", - "fontSizeLabel": "글꼴 크기", - "accentColorLabel": "포인트 색상", - "settingsTerminal": "단말기", - "commandAutocomplete": "명령어 자동 완성", - "commandAutocompleteDesc": "입력하는 동안 자동 완성 기능을 표시합니다.", - "historyTracking": "이력 추적", - "historyTrackingDesc": "추적 터미널 명령", - "syntaxHighlighting": "구문 강조 표시", - "syntaxHighlightingDesc": "터미널 출력 강조 표시", - "commandPalette": "커맨드 팔레트", - "commandPaletteDesc": "키보드 단축키 활성화", - "reopenTabsOnLogin": "로그인 시 탭 다시 열기", - "reopenTabsOnLoginDesc": "로그인하거나 페이지를 새로 고칠 때, 다른 기기에서 접속하더라도 열려 있던 탭을 복원할 수 있습니다.", - "confirmTabClose": "확인 탭 닫기", - "confirmTabCloseDesc": "터미널 탭을 닫기 전에 먼저 확인하세요.", - "settingsSidebar": "사이드바", - "showHostTags": "호스트 태그 표시", - "showHostTagsDesc": "호스트 목록에 태그를 표시합니다.", - "hostTrayOnClick": "호스트 작업 확장하려면 클릭하세요", - "hostTrayOnClickDesc": "연결 버튼을 항상 표시하고, 마우스 오버 대신 클릭하여 관리 옵션을 확장합니다.", - "pinAppRail": "핀 앱 레일", - "pinAppRailDesc": "왼쪽 사이드바 앱 레일을 마우스 오버 시 확장되는 대신 항상 확장된 상태로 유지합니다.", - "settingsSnippets": "짧은 발췌", - "foldersCollapsed": "폴더 접힘", - "foldersCollapsedDesc": "기본적으로 폴더를 접습니다.", - "confirmExecution": "실행 확인", - "confirmExecutionDesc": "코드 조각을 실행하기 전에 확인하십시오.", - "settingsUpdates": "업데이트", - "disableUpdateChecks": "업데이트 확인 비활성화", - "disableUpdateChecksDesc": "업데이트 확인을 중지하세요", - "totpAuthenticator": "TOTP 인증기", - "totpEnabled": "2FA가 활성화되었습니다", - "totpDisabled": "로그인 보안을 강화하세요", - "disable": "장애를 입히다", - "enable": "할 수 있게 하다", - "setupTotp": "TOTP 설정", - "qrCode": "QR 코드", - "totpInstructions": "QR 코드를 스캔하거나 인증 앱에 비밀번호를 입력한 후 6자리 코드를 입력하세요.", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "확인하다", - "changePassword": "비밀번호 변경", - "currentPasswordLabel": "현재 비밀번호", - "currentPasswordPlaceholder": "현재 비밀번호", - "newPasswordLabel": "새 비밀번호", - "newPasswordPlaceholder": "새 비밀번호", - "confirmPasswordLabel": "새 비밀번호를 확인하세요", - "confirmPasswordPlaceholder": "새 비밀번호를 확인하세요", - "updatePassword": "비밀번호 업데이트", - "createApiKeyTitle": "API 키 생성", - "createApiKeyDescription": "프로그래밍 방식으로 접근하기 위한 새 API 키를 생성합니다.", - "apiKeyNameLabel": "이름", - "apiKeyNamePlaceholder": "예: CI 파이프라인", - "expiryDateLabel": "만료일", - "optional": "선택 과목", - "cancel": "취소", - "createKey": "키를 생성합니다", - "apiKeyCount": "{{count}} 키", - "newKey": "새 키", - "noApiKeys": "아직 API 키가 없습니다.", - "apiKeyActive": "활동적인", - "apiKeyUsageHint": "키를 함께 넣어주세요", - "apiKeyUsageHintHeader": "헤더.", - "apiKeyPermissionsHint": "키는 생성 사용자의 권한을 상속합니다.", - "roleUser": "사용자", - "authMethodDual": "이중 인증", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP 설정 시작에 실패했습니다.", - "totpEnter6Digits": "6자리 코드를 입력하세요", - "totpEnabledSuccess": "2단계 인증이 활성화되었습니다.", - "totpInvalidCode": "잘못된 코드입니다. 다시 시도해 주세요.", - "totpDisableInputRequired": "TOTP 코드 또는 비밀번호를 입력하세요.", - "totpDisabledSuccess": "2단계 인증이 비활성화되었습니다.", - "totpDisableFailed": "2단계 인증 비활성화에 실패했습니다.", - "totpDisableTitle": "2단계 인증 비활성화", - "totpDisablePlaceholder": "TOTP 코드 또는 비밀번호를 입력하세요", - "totpDisableConfirm": "2단계 인증 비활성화", - "totpContinueVerify": "계속 확인", - "totpVerifyTitle": "확인 코드", - "totpBackupTitle": "백업 코드", - "totpDownloadBackup": "다운로드 백업 코드", - "done": "완료", - "secretCopied": "비밀 정보가 클립보드에 복사되었습니다.", - "apiKeyNameRequired": "키 이름은 필수 입력 사항입니다.", - "apiKeyCreated": "API 키 \"{{name}}\"가 생성되었습니다", - "apiKeyCreateFailed": "API 키 생성에 실패했습니다.", - "apiKeyUser": "사용자", - "apiKeyExpires": "만료됨", - "apiKeyRevoked": "API 키 \"{{name}}\"가 취소되었습니다.", - "apiKeyRevokeFailed": "API 키 해지에 실패했습니다.", - "passwordFieldsRequired": "현재 비밀번호와 새 비밀번호가 필요합니다.", - "passwordMismatch": "비밀번호가 일치하지 않습니다", - "passwordTooShort": "비밀번호는 최소 6자 이상이어야 합니다.", - "passwordUpdated": "비밀번호가 성공적으로 업데이트되었습니다.", - "passwordUpdateFailed": "비밀번호 업데이트에 실패했습니다", - "deletePasswordRequired": "계정을 삭제하려면 비밀번호가 필요합니다.", - "deleteFailed": "계정 삭제에 실패했습니다", - "deleting": "삭제 중...", - "colorPickerTooltip": "색상 선택기 열기", - "themeSystem": "체계", - "themeLight": "빛", - "themeDark": "어두운", - "themeDracula": "드라큘라", - "themeCatppuccin": "캣푸친", - "themeNord": "노르드", - "themeSolarized": "태양열 처리", - "themeTokyoNight": "도쿄의 밤", - "themeOneDark": "원 다크", - "themeGruvbox": "그루브박스" + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/nl_NL.json b/src/ui/locales/translated/nl_NL.json index f434ce56..9bc5af32 100644 --- a/src/ui/locales/translated/nl_NL.json +++ b/src/ui/locales/translated/nl_NL.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Mappen", - "folder": "Map", - "password": "Wachtwoord", - "key": "Sleutel", - "sshPrivateKey": "SSH persoonlijke sleutel", - "upload": "Uploaden", - "keyPassword": "Sleutel wachtwoord", - "sshKey": "SSH sleutel", - "uploadPrivateKeyFile": "Upload een privésleutel bestand", - "searchCredentials": "Zoek inloggegevens...", - "addCredential": "Toegangsgegevens toevoegen", - "caCertificate": "CA Certificaat (-cert.pub)", - "caCertificateDescription": "Optioneel: Upload of plak het CA-ondertekende certificaatbestand (bijv. id_ed25519-cert.pub). Vereist wanneer uw SSH server gebruik maakt van autorisatie op certificaat.", - "uploadCertFile": "Upload -cert.pub bestand", - "clearCert": "Verwijderen", - "certLoaded": "Certificaat geladen", - "certPublicKeyLabel": "CA-certificaat", - "certTypeLabel": "Type certificaat", - "pasteOrUploadCert": "Plak of upload een -cert.pub certificaat...", - "hasCaCert": "Heeft CA-certificaat", - "noCaCert": "Geen CA-certificaat" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Laden van waarschuwingen mislukt", - "failedToDismissAlert": "Waarschuwing sluiten mislukt" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Server Configuratie", - "description": "Configureer de Termix-server-URL om verbinding te maken met uw backend-services", + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", "serverUrl": "Server URL", - "enterServerUrl": "Voer een server-URL in", - "saveFailed": "Configuratie opslaan mislukt", - "saveError": "Fout bij opslaan configuratie", - "saving": "Opslaan...", - "saveConfig": "Configuratie opslaan", - "helpText": "Voer de URL in waar uw Termix server draait (bijv. http://localhost:30001 of https://your-server.com)", - "changeServer": "Server wijzigen", - "mustIncludeProtocol": "De server-URL moet beginnen met http:// of https://", - "allowInvalidCertificate": "Ongeldig certificaat toestaan", - "allowInvalidCertificateDesc": "Uitsluitend te gebruiken voor vertrouwde, zelfgehoste servers met zelfondertekende certificaten of IP-adrescertificaten.", - "useEmbedded": "Lokale server gebruiken", - "embeddedDesc": "Voer Termix uit met de ingebouwde lokale server (geen externe server nodig)", - "embeddedConnecting": "Verbinden met lokale server...", - "embeddedNotReady": "Lokale server is nog niet klaar. Wacht even en probeer het opnieuw.", - "localServer": "Lokale Server" + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Versie controle fout", - "checkFailed": "Controleren op updates mislukt", - "upToDate": "App is up to date", - "currentVersion": "Je gebruikt versie {{version}}", - "updateAvailable": "Update beschikbaar", - "newVersionAvailable": "Er is een nieuwe versie beschikbaar! U voert {{current}}uit, maar {{latest}} is beschikbaar.", - "betaVersion": "Beta versie", - "betaVersionDesc": "Je gebruikt {{current}}, die nieuwer is dan de laatste stabiele versie {{latest}}.", - "releasedOn": "Uitgebracht op {{date}}", - "downloadUpdate": "Update downloaden", - "checking": "Controleren op updates...", - "checkUpdates": "Controleren op updates", - "checkingUpdates": "Controleren op updates...", - "updateRequired": "Update vereist" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Afsluiten", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Doorgaan", - "maintenance": "Onderhoud", - "degraded": "Verminderde", - "error": "Foutmelding", - "warning": "Waarschuwing", - "unsavedChanges": "Niet-opgeslagen wijzigingen", - "dismiss": "Uitschakelen", - "loading": "Laden...", - "optional": "Optioneel", - "connect": "Verbinden", - "copied": "Gekoppeld", - "connecting": "Verbinden...", - "updateAvailable": "Update beschikbaar", - "appName": "Termixen", - "openInNewTab": "Openen in nieuw tabblad", - "noReleases": "Geen releases", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", "updatesAndReleases": "Updates & Releases", - "newVersionAvailable": "Er is een nieuwe versie ({{version}}) beschikbaar.", - "failedToFetchUpdateInfo": "Bijgewerkte informatie ophalen mislukt", - "preRelease": "Voor-versie", - "noReleasesFound": "Geen releases gevonden.", - "cancel": "annuleren", - "username": "Gebruikersnaam", - "login": "Aanmelden", - "register": "Registreren", - "password": "Wachtwoord", - "confirmPassword": "Bevestig wachtwoord", - "back": "Achterzijde", - "save": "Opslaan", - "saving": "Opslaan...", - "delete": "Verwijderen", - "rename": "Hernoem", - "edit": "Bewerken", - "add": "Toevoegen", - "confirm": "Bevestigen", - "no": "Neen", - "or": "of", - "next": "Volgende", - "previous": "named@@0", - "refresh": "Vernieuwen", - "language": "Taal", - "checking": "Controleren...", - "checkingDatabase": "Database-verbinding controleren...", - "checkingAuthentication": "Authenticatie controleren...", - "backendReconnected": "Serververbinding hersteld", - "connectionDegraded": "Serververbinding verbroken, herstel bezig…", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Verwijderen", - "create": "Aanmaken", - "update": "Vernieuwen", - "copy": "Kopiëren", - "copyFailed": "Kon niet naar klembord kopiëren", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Herstellen", - "of": "van" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Startpagina", + "home": "Home", "terminal": "Terminal", "docker": "Docker", "tunnels": "Tunnels", - "fileManager": "Bestands Beheer", - "serverStats": "Server Statistieken", - "admin": "Beheerder", - "userProfile": "Gebruikers Profiel", - "splitScreen": "Scherm splitsen", - "confirmClose": "Deze actieve sessie sluiten?", - "close": "Afsluiten", - "cancel": "annuleren", - "sshManager": "SSH manager", - "cannotSplitTab": "Kan dit tabblad niet splitsen", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Wachtwoord kopiëren", - "copySudoPassword": "Sudo wachtwoord kopiëren", - "passwordCopied": "Wachtwoord gekopieerd naar klembord", - "noPasswordAvailable": "Geen wachtwoord beschikbaar", - "failedToCopyPassword": "Wachtwoord kopiëren mislukt", - "refreshTab": "Connectie vernieuwen", - "openFileManager": "Bestandsbeheer openen", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", "dashboard": "Dashboard", - "networkGraph": "Netwerk Grafiek", - "quickConnect": "Snel verbinden", - "sshTools": "SSH gereedschap", - "history": "Geschiedenis", - "hosts": "Verantwoordelijken", - "snippets": "Tekstbouwstenen", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", "hostManager": "Host Manager", - "credentials": "Aanmeldgegevens", - "connections": "Verbindingen", - "roleAdministrator": "Beheerder", - "roleUser": "Gebruiker" + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Verantwoordelijken", - "noHosts": "Geen SSH hosts", - "retry": "Opnieuw", - "refresh": "Vernieuwen", - "optional": "Optioneel", - "downloadSample": "Download voorbeeld", - "failedToDeleteHost": "Verwijderen {{name}} mislukt", - "importSkipExisting": "Import (bestaande overslaan)", - "connectionDetails": "Connectie Details", - "ssh": "ZZ", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", + "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Extern bureaublad", - "port": "Poort", - "username": "Gebruikersnaam", - "folder": "Map", - "tags": "Labels", - "pin": "Vastzetten", - "addHost": "Host toevoegen", - "editHost": "Host bewerken", - "cloneHost": "Host klonen", - "enableTerminal": "Terminal inschakelen", - "enableTunnel": "Tunnel inschakelen", - "enableFileManager": "Bestandsbeheer inschakelen", - "enableDocker": "Docker inschakelen", - "defaultPath": "Standaard pad", - "connection": "Verbindingsinstellingen", - "upload": "Uploaden", - "authentication": "Authenticatie", - "password": "Wachtwoord", - "key": "Sleutel", - "credential": "Toegangsgegevens", - "none": "geen", - "sshPrivateKey": "SSH persoonlijke sleutel", - "keyType": "Type sleutel", - "uploadFile": "Bestand uploaden", - "tabGeneral": "Algemeen", - "tabSsh": "ZZ", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", + "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Bestanden", - "tabStats": "Statistieken", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Delen", - "tabAuthentication": "Authenticatie", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Bestands Beheer", - "serverStats": "Server Statistieken", - "status": "status", - "folderRenamed": "Map \"{{oldName}}\" hernoemd naar \"{{newName}}\" succesvol", - "failedToRenameFolder": "Kan map niet hernoemen", - "movedToFolder": "Verplaatst naar \"{{folder}}\"", - "editHostTooltip": "Host bewerken", - "statusChecks": "Status controles", - "metricsCollection": "Collectie Statistieken", - "metricsInterval": "Metrics Collectie Interval", - "metricsIntervalDesc": "Hoe vaak serverstatistieken worden verzameld (5s - 1h)", - "behavior": "Gedrag", - "themePreview": "Thema voorbeeld", - "theme": "Thema", - "fontFamily": "Lettertype Familie", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Letter afstand", - "lineHeight": "Lijn hoogte", - "cursorStyle": "Cursor Stijl", - "cursorBlink": "Cursor Knipperen", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", "scrollbackBuffer": "Scrollback Buffer", - "bellStyle": "Bell stijl", - "rightClickSelectsWord": "Klik met de rechtermuisknop selecteren", - "fastScrollModifier": "Snel Scrollen Aanpasser", - "fastScrollSensitivity": "Snelle Boekgevoeligheid", - "sshAgentForwarding": "SSH Agent doorsturen", - "backspaceMode": "Modus voor backspace", - "startupSnippet": "Opstarten snippet", - "selectSnippet": "Tekstfragment selecteren", - "forceKeyboardInteractive": "Forceer toetsenbord-interactief", - "overrideCredentialUsername": "Aanmeldgebruikersnaam overschrijven", - "overrideCredentialUsernameDesc": "Gebruik de hierboven opgegeven gebruikersnaam in plaats van de inlognaam", - "jumpHostChain": "Sprong Host ketting", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", "portKnocking": "Port Knocking", - "addKnock": "Poort toevoegen", - "addProxyNode": "Voeg node toe", - "proxyNode": "Proxy Knoop", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", "proxyType": "Proxy Type", - "quickActions": "Snelle acties", - "sudoPasswordAutoFill": "Sudo Wachtwoord automatisch invullen", - "sudoPassword": "Sudo Wachtwoord", - "keepaliveInterval": "Keepalive interval (ms)", - "moshCommand": "MEER commando", - "environmentVariables": "Omgeving variabelen", - "addVariable": "Variabele toevoegen", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Terminal URL kopiëren", - "copyFileManagerUrl": "Kopieer URL Bestand Manager", - "copyRemoteDesktopUrl": "Kopieer URL van extern bureaublad", - "failedToConnect": "Kan geen verbinding maken met console", - "connect": "Verbinden", - "disconnect": "Verbreek", - "start": "Beginnen", - "enableStatusCheck": "Status controle inschakelen", - "enableMetrics": "Metriek inschakelen", - "bulkUpdateFailed": "Bulk update mislukt", - "selectAll": "Alles selecteren", - "deselectAll": "Deselecteer alles", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Veilige Granaat", - "virtualNetwork": "Virtueel netwerk", - "unencryptedShell": "Onversleutelde shell", - "addressIp": "Adres / IP", - "friendlyName": "Vriendelijke naam", - "folderAndAdvanced": "Map & Geavanceerd", - "privateNotes": "Privé notities", - "privateNotesPlaceholder": "Details over deze server...", - "pinToTop": "Bovenaan vastzetten", - "pinToTopDesc": "Toon altijd deze host bovenaan de lijst", - "portKnockingSequence": "Poort Knocking Volgorde", - "addKnockBtn": "Knock toevoegen", - "noPortKnocking": "Er is geen port knocking geconfigureerd.", - "knockPort": "Knock Poort", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Vertraging na (ms)", - "useSocks5Proxy": "Gebruik SOCKS5 Proxy", - "useSocks5ProxyDesc": "Route verbinding via een proxyserver", - "proxyHost": "Proxy host", - "proxyPort": "Proxy Poort", - "proxyUsername": "Proxy Gebruikersnaam", - "proxyPassword": "Proxy wachtwoord", - "proxySingleMode": "Enkele Proxy", - "proxyChainMode": "Proxyketen keten", - "you": "Jij", - "jumpHostChainLabel": "Sprong Host ketting", - "addJumpBtn": "Voeg sprong toe", - "noJumpHosts": "Geen springhosts geconfigureerd.", - "selectAServer": "Selecteer een server...", - "sshPort": "SSH poort", - "authMethod": "Auth Methode", - "storedCredential": "Opgeslagen referenties", - "selectACredential": "Selecteer een aanmeldingsgegevens...", - "keyTypeLabel": "Type sleutel", - "keyTypeAuto": "Automatisch detecteren", - "keyPasteTab": "Plakken", - "keyUploadTab": "Uploaden", - "keyFileLoaded": "Sleutelbestand geladen", - "keyUploadClick": "Klik om .pem / .key / .ppk te uploaden", - "clearKey": "Sleutel wissen", - "keySaved": "SSH sleutel opgeslagen", - "keyReplaceNotice": "plak hieronder een nieuwe sleutel om deze te vervangen", - "keyPassphraseSaved": "Wachtwoordzin opgeslagen, type om te wijzigen", - "replaceKey": "Vervang sleutel", - "forceKeyboardInteractiveLabel": "Forceer toetsenbord Interactief", - "forceKeyboardInteractiveShortDesc": "Handmatig invoeren van wachtwoord afdwingen ook als er sleutels aanwezig zijn", - "terminalAppearance": "Terminal uiterlijk", - "colorTheme": "Thema kleur", - "fontFamilyLabel": "Lettertype Familie", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Cursor Stijl", - "letterSpacingPx": "Letter afstand (px)", - "lineHeightLabel": "Lijn hoogte", - "bellStyleLabel": "Bell stijl", - "backspaceModeLabel": "Modus voor backspace", - "cursorBlinking": "Cursor knipperen", - "cursorBlinkingDesc": "Schakel knipperende animatie in voor de terminale cursor", - "rightClickSelectsWordLabel": "Rechtsklik Selectie Woord", - "rightClickSelectsWordShortDesc": "Selecteer het woord onder de cursor bij rechtermuisklik", - "behaviorAndAdvanced": "Gedrag en geavanceerd", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", "scrollbackBufferLabel": "Scrollback Buffer", - "scrollbackMaxLines": "Maximum aantal lijnen bewaard in geschiedenis", - "sshAgentForwardingLabel": "SSH Agent doorsturen", - "sshAgentForwardingShortDesc": "Geef uw lokale SSH sleutels door aan deze host", - "enableAutoMosh": "Auto-moh inschakelen", - "enableAutoMoshDesc": "Liever Mosh over SSH indien beschikbaar", - "enableAutoTmux": "Auto-Tmux inschakelen", - "enableAutoTmuxDesc": "Start automatisch of voeg toe aan tmux sessie", - "sudoPasswordAutoFillLabel": "Sudo Wachtwoord automatisch invullen", - "sudoPasswordAutoFillShortDesc": "Automatisch sudo wachtwoord opgeven wanneer hierom gevraagd wordt", - "sudoPasswordLabel": "Sudo Wachtwoord", - "environmentVariablesLabel": "Omgeving variabelen", - "addVariableBtn": "Variabele toevoegen", - "noEnvVars": "Geen omgevingsvariabelen geconfigureerd.", - "fastScrollModifierLabel": "Snel Scrollen Aanpasser", - "fastScrollSensitivityLabel": "Snelle Boekgevoeligheid", - "moshCommandLabel": "Mosh commando", - "startupSnippetLabel": "Opstarten snippet", - "keepaliveIntervalLabel": "Keepalive-interval (seconden)", - "maxKeepaliveMisses": "Max Keepalive Gemist", - "tunnelSettings": "Tunnel instellingen", - "enableTunneling": "Tunneling inschakelen", - "enableTunnelingDesc": "SSH tunnel inschakelen voor deze host", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", "serverTunnelsSection": "Server Tunnels", - "addTunnelBtn": "Tunnel toevoegen", - "noTunnelsConfigured": "Geen tunnels geconfigureerd.", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Tunnel type", - "tunnelModeLocalDesc": "Stuur een lokale poort door naar een poort op de externe server (of een host bereikbaar).", - "tunnelModeRemoteDesc": "Stuur een poort terug naar een lokale poort op je machine.", - "tunnelModeDynamicDesc": "Creëer een SOCKS5 proxy op een lokale poort voor dynamische poort forwarding.", - "sameHost": "Deze host (directe tunnel)", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", "endpointHost": "Endpoint Host", - "endpointPort": "Eindpunt Poort", - "bindHost": "Binden host", - "sourcePort": "Bron poort", - "maxRetries": "Maximaal aantal pogingen", - "retryIntervalS": "Interval (s) opnieuw proberen", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", "autoStartLabel": "Auto-start", - "autoStartDesc": "Sluit deze tunnel automatisch aan wanneer de host wordt geladen", - "tunnelConnecting": "Tunnel verbinden...", - "tunnelDisconnected": "Tunnel verbroken", - "failedToConnectTunnel": "Kan geen verbinding maken", - "failedToDisconnectTunnel": "Verbinding verbreken mislukt", - "dockerIntegration": "Docker integratie", - "enableDockerMonitor": "Docker inschakelen", - "enableDockerMonitorDesc": "Controleer en beheer containers van deze host via Docker", - "enableFileManagerMonitor": "Bestandsbeheer inschakelen", - "enableFileManagerMonitorDesc": "Blader en beheer bestanden op deze host via SFTP", - "defaultPathLabel": "Standaard pad", - "fileManagerPathHint": "De map die wordt geopend wanneer de bestandsbeheerder start voor deze host.", - "statusChecksLabel": "Status controles", - "enableStatusChecks": "Status controles inschakelen", - "enableStatusChecksDesc": "Periodiek ping van deze host om beschikbaarheid te controleren", - "useGlobalInterval": "Algemene Interval Gebruiken", - "useGlobalIntervalDesc": "Overschrijven met de server-brede statuscontrole interval", - "checkIntervalS": "Interval (s) controleren", - "checkIntervalDesc": "Seconden tussen elke verbinding ping", - "metricsCollectionLabel": "Collectie Statistieken", - "enableMetricsLabel": "Metriek inschakelen", - "enableMetricsDesc": "Verzamel CPU, RAM, schijf en netwerkgebruik van deze host", - "useGlobalMetrics": "Algemene Interval Gebruiken", - "useGlobalMetricsDesc": "Overschrijven met het server-brede metrieke interval", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", "metricsIntervalS": "Metrics Interval (s)", - "metricsIntervalDesc2": "Tussen metrische momentopnamen", - "visibleWidgets": "Zichtbare Widgets", - "cpuUsageLabel": "CPU gebruik", - "cpuUsageDesc": "CPU percentage, laad gemiddelden, sparkline grafiek", - "memoryLabel": "Geheugen gebruik", - "memoryDesc": "RAM gebruik, swap, gecached", - "storageLabel": "Schijf gebruik", - "storageDesc": "Schijfgebruik per mount point", - "networkLabel": "Netwerk Interfaces", - "networkDesc": "Interfacelijst en bandbreedte", - "uptimeLabel": "Actief", - "uptimeDesc": "Systeem uptime en opstarttijd", - "systemInfoLabel": "Systeem info", - "systemInfoDesc": "OS, kernel, hostnaam, architectuur", - "recentLoginsLabel": "Recente aanmeldingen", - "recentLoginsDesc": "Succesvolle en mislukte login gebeurtenissen", - "topProcessesLabel": "Top processen", - "topProcessesDesc": "PID, CPU%, MEM%, opdracht", - "listeningPortsLabel": "Luisterende Poorten", - "listeningPortsDesc": "Open poorten met proces en staat", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", "firewallDesc": "Firewall, AppArmor, SELinux status", - "quickActionsLabel": "Snelle acties", - "quickActionsToolbar": "Snelle acties worden weergegeven als knoppen in de Server Stats werkbalk voor het uitvoeren van één klik opdracht.", - "noQuickActions": "Nog geen snelle acties.", - "buttonLabel": "Knop label", - "selectSnippetPlaceholder": "Tekstfragment selecteren...", - "addActionBtn": "Actie toevoegen", - "hostSharedSuccessfully": "Host met succes gedeeld", - "failedToShareHost": "Kan host niet delen", - "accessRevoked": "Toegang ingetrokken", - "failedToRevokeAccess": "Toegang intrekken mislukt", - "cancelBtn": "annuleren", - "savingBtn": "Opslaan...", - "addHostBtn": "Host toevoegen", - "hostUpdated": "Host bijgewerkt", - "hostCreated": "Host aangemaakt", - "failedToSave": "Host opslaan mislukt", - "credentialUpdated": "Toegangsgegevens bijgewerkt", - "credentialCreated": "Toegangssleutel aangemaakt", - "failedToSaveCredential": "Kan referenties niet opslaan", - "backToHosts": "Terug naar Hosts", - "backToCredentials": "Terug naar inloggegevens", - "pinned": "Vastgezet", - "noHostsFound": "Geen hosts gevonden", - "tryDifferentTerm": "Probeer een andere term", - "addFirstHost": "Voeg je eerste host toe om te beginnen", - "noCredentialsFound": "Geen inloggegevens gevonden", - "addCredentialBtn": "Toegangsgegevens toevoegen", - "updateCredentialBtn": "Certificaatgegevens bijwerken", - "features": "Eigenschappen", - "noFolder": "(Geen map)", - "deleteSelected": "Verwijderen", - "exitSelection": "Sluit selectie af", - "importSkip": "Import (bestaande overslaan)", - "importOverwrite": "Import (overschrijven)", - "collapseBtn": "Samenvouwen", - "importExportBtn": "Importeren / exporteren", - "hostStatusesRefreshed": "Hoststatus vernieuwd", - "failedToRefreshHosts": "Vernieuwen van hosts mislukt", - "movedHostTo": "{{host}} verplaatst naar \"{{folder}}\"", - "failedToMoveHost": "Verplaatsen host mislukt", - "folderRenamedTo": "Map hernoemd naar \"{{name}}\"", - "deletedFolder": "Map \"{{name}} \" verwijderd", - "failedToDeleteFolder": "Map verwijderen mislukt", - "deleteAllInFolder": "Verwijder alle hosts in \"{{name}}\"? Dit kan niet ongedaan worden gemaakt.", - "deletedHost": "Verwijderd {{name}}", - "copiedToClipboard": "Gekopieerd naar klembord", - "terminalUrlCopied": "Terminal URL gekopieerd", - "fileManagerUrlCopied": "URL Bestandsbeheer gekopieerd", - "tunnelUrlCopied": "Tunnel URL gekopieerd", - "dockerUrlCopied": "Docker URL gekopieerd", - "serverStatsUrlCopied": "Server Stats URL gekopieerd", - "rdpUrlCopied": "RDP URL gekopieerd", - "vncUrlCopied": "VNC URL gekopieerd", - "telnetUrlCopied": "Telnet URL gekopieerd", - "remoteDesktopUrlCopied": "URL op extern bureaublad gekopieerd", - "expandActions": "Acties uitbreiden", - "collapseActions": "Inklapacties", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Magisch pakket verzonden naar {{name}}", - "wakeOnLanError": "Het verzenden van het magic packet is mislukt.", - "cloneHostAction": "Host klonen", - "copyAddress": "Adres kopiëren", - "copyLink": "Koppeling kopiëren", - "copyTerminalUrlAction": "Terminal URL kopiëren", - "copyFileManagerUrlAction": "Kopieer URL Bestand Manager", - "copyTunnelUrlAction": "Kopieer Tunnel URL", - "copyDockerUrlAction": "Docker URL kopiëren", - "copyServerStatsUrlAction": "Kopieer server statistieken URL", - "copyRdpUrlAction": "Kopieer RDP URL", - "copyVncUrlAction": "VNC URL kopiëren", - "copyTelnetUrlAction": "Telnet URL kopiëren", - "copyRemoteDesktopUrlAction": "Kopieer URL van extern bureaublad", - "deleteCredentialConfirm": "Toegangsgegevens verwijderen \"{{name}}\"?", - "deletedCredential": "Verwijderd {{name}}", - "deploySSHKeyTitle": "SSH sleutel toepassen", - "deployingBtn": "Implementeren...", - "deployBtn": "Implementeren", - "failedToDeployKey": "Kan implementatiesleutel niet implementeren", - "deleteHostsConfirm": "{{count}} host{{plural}}verwijderen? Dit kan niet ongedaan worden gemaakt.", - "movedToRoot": "Verplaatst naar root", - "failedToMoveHosts": "Hosts verplaatsen mislukt", - "enableTerminalFeature": "Terminal inschakelen", - "disableTerminalFeature": "Terminal uitschakelen", - "enableFilesFeature": "Bestanden inschakelen", - "disableFilesFeature": "Bestanden uitschakelen", - "enableTunnelsFeature": "Tunnels inschakelen", - "disableTunnelsFeature": "Tunnels uitschakelen", - "enableDockerFeature": "Docker inschakelen", - "disableDockerFeature": "Docker uitschakelen", - "addTagsPlaceholder": "Labels toevoegen...", - "authDetails": "Authenticatie details", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", "credType": "Type", - "generateKeyPairDesc": "Genereer een nieuw sleutelpaar en beide private en publieke sleutels worden automatisch gevuld.", - "generatingKey": "Genereren...", - "generateLabel": "Genereer {{label}}", - "uploadFileBtn": "Bestand uploaden", - "keyPassphraseOptional": "Sleutel wachtwoord (optioneel)", - "sshPublicKeyOptional": "SSH Publieke Sleutel (optioneel)", - "publicKeyGenerated": "Publieke sleutel gegenereerd", - "failedToGeneratePublicKey": "Openbare sleutel kon niet worden afgeleid", - "publicKeyCopied": "Publieke sleutel gekopieerd", - "keyPairGenerated": "{{label}} sleutel paar gegenereerd", - "failedToGenerateKeyPair": "Genereren van sleutelpaar mislukt", - "searchHostsPlaceholder": "Zoek hosts, adressen, tags…", - "searchCredentialsPlaceholder": "Zoek inloggegevens…", - "refreshBtn": "Vernieuwen", - "addTag": "Labels toevoegen...", - "deleteConfirmBtn": "Verwijderen", - "tunnelRequirementsText": "De SSH server moet GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", - "deleteHostConfirm": "Verwijder \"{{name}}\"?", - "enableAtLeastOneProtocol": "Schakel ten minste één protocol hierboven in om authenticatie en verbindingsinstellingen te configureren.", - "keyPassphrase": "Sleutel wachtwoord", - "connectBtn": "Verbinden", - "disconnectBtn": "Verbreek", - "basicInformation": "Basis Informatie", - "authDetailsSection": "Authenticatie details", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", "credTypeLabel": "Type", - "hostsTab": "Verantwoordelijken", - "credentialsTab": "Aanmeldgegevens", - "selectMultiple": "Selecteer meerdere", - "selectHosts": "Selecteer hosts", - "connectionLabel": "Verbindingsinstellingen", - "authenticationLabel": "Authenticatie", - "generateKeyPairTitle": "Sleutelkoppeling genereren", - "generateKeyPairDescription": "Genereer een nieuw sleutelpaar en beide private en publieke sleutels worden automatisch gevuld.", - "generateFromPrivateKey": "Genereer van Private Key", - "refreshBtn2": "Vernieuwen", - "exitSelectionTitle": "Sluit selectie af", - "exportAll": "Alles exporteren", - "addHostBtn2": "Host toevoegen", - "addCredentialBtn2": "Toegangsgegevens toevoegen", - "checkingHostStatuses": "Hoststatussen controleren...", - "pinnedSection": "Vastgezet", - "hostsExported": "Hosts succesvol geëxporteerd", - "exportFailed": "Exporteren van hosts is mislukt", - "sampleDownloaded": "Voorbeeld bestand gedownload", - "failedToDeleteCredential2": "Kan aanmeldgegevens niet verwijderen", - "noFolderOption": "(Geen map)", - "nSelected": "{{count}} geselecteerd", - "featuresMenu": "Eigenschappen", - "moveMenu": "Verplaatsen", - "cancelSelection": "annuleren", - "deployDialogDesc": "Gebruik {{name}} voor een host's authorized_keys.", - "targetHostLabel": "Doel host", - "selectHostOption": "Selecteer een host...", - "keyDeployedSuccess": "Sleutel succesvol geïmplementeerd", - "failedToDeployKey2": "Kan implementatiesleutel niet implementeren", - "deletedCount": "Verwijderde {{count}} hosts", - "failedToDeleteCount": "Verwijderen van {{count}} hosts mislukt", - "duplicatedHost": "Gedupliceerd \"{{name}}\"", - "failedToDuplicateHost": "Mislukt om host te dupliceren", - "updatedCount": "Bijgewerkt {{count}} hosts", - "friendlyNameLabel": "Vriendelijke naam", - "descriptionLabel": "Beschrijving", - "loadingHost": "Host laden...", - "loadingHosts": "Hosts worden geladen...", - "loadingCredentials": "Inloggegevens worden geladen...", - "noHostsYet": "Nog geen hosts", - "noHostsMatchSearch": "Er zijn geen hosts die voldoen aan jouw zoekopdracht", - "hostNotFound": "Host niet gevonden", - "searchHosts": "Zoek hosts...", - "sortHosts": "Sorteer hosts", - "sortDefault": "Standaardbestelling", - "sortNameAsc": "Naam (A → Z)", - "sortNameDesc": "Naam (Z → A)", - "sortIpAsc": "IP-adres (oplopend)", - "sortIpDesc": "IP-adres (Beschrijving)", - "sortOnlineFirst": "Eerst online", - "sortOfflineFirst": "Eerst offline", - "sortPinnedFirst": "Eerst vastgepind", - "filterHosts": "Filter hosts", - "filterClearAll": "Filters wissen", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Vastgepind", - "filterAuthGroup": "Auth-type", - "filterAuthPassword": "Wachtwoord", - "filterAuthKey": "SSH-sleutel", - "filterAuthCredential": "Referentie", - "filterAuthNone": "Geen", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Functies", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Bestandsbeheerder", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", "filterTagsGroup": "Tags", - "shareHost": "Deel host", - "shareHostTitle": "Delen: {{name}}", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Deze host moet een certificaat gebruiken om het delen in te schakelen. Bewerk de host en wijs eerst een certificaat toe." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Verbindingsinstellingen", - "authentication": "Authenticatie", - "connectionSettings": "Verbindingsinstellingen", - "displaySettings": "Weergave instellingen", - "audioSettings": "Audio instellingen", - "rdpPerformance": "RDP prestaties", - "deviceRedirection": "Apparaat omleiding", - "session": "Sessie", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Klembord", - "sessionRecording": "Sessie opname", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC instellingen", - "terminalSettings": "Terminal Instellingen", - "rdpPort": "RDP poort", - "username": "Gebruikersnaam", - "password": "Wachtwoord", - "domain": "Domein", - "securityMode": "Beveiliging modus", - "colorDepth": "Kleur diepte", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Højde", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Methode aanpassen", - "clientName": "Naam klant", - "initialProgram": "Eerste programma", - "serverLayout": "Server lay-out", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Gateway poort", - "gatewayUsername": "Gateway Gebruikersnaam", - "gatewayPassword": "Gateway Wachtwoord", - "gatewayDomain": "Gateway Domein", - "remoteAppProgram": "RemoteApp Programma", - "workingDirectory": "Werken Map", - "arguments": "Argumenten", - "normalizeLineEndings": "Normaliseer Eindingen van lijn", - "recordingPath": "Opname pad", - "recordingName": "Naam van opname", - "macAddress": "MAC Adres", - "broadcastAddress": "Adres uitzenden", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Wacht op tijd (s)", - "driveName": "Drive Naam", - "drivePath": "Drive pad", - "ignoreCertificate": "Certificaat negeren", - "ignoreCertificateDesc": "Verbindingen met hosts toestaan met zelfondertekende certificaten", - "forceLossless": "Verlies afdwingen", - "forceLosslessDesc": "Forceer lossless afbeeldingscodering (hogere kwaliteit, meer bandbreedte)", - "disableAudio": "Audio uitschakelen", - "disableAudioDesc": "Alle audio van de externe sessie dempen", - "enableAudioInput": "Audio-invoer (microfoon) inschakelen", - "enableAudioInputDesc": "Lokale microfoon doorsturen naar de externe sessie", - "wallpaper": "Achtergrond", - "wallpaperDesc": "Bureaublad achtergrond tonen (prestaties uitschakelen)", - "theming": "Thematisering", - "themingDesc": "Visuele thema's en stijlen inschakelen", - "fontSmoothing": "Lettertype vloeiend maken", - "fontSmoothingDesc": "Activeer ClearType lettertype weergave", - "fullWindowDrag": "Slepen volledig venster", - "fullWindowDragDesc": "Laat inhoud van het venster zien tijdens slepen", - "desktopComposition": "Compositie bureaublad", - "desktopCompositionDesc": "Inschakelen van Aero glazen effecten", - "menuAnimations": "Menu animaties", - "menuAnimationsDesc": "Menu vervagen en dia animaties inschakelen", - "disableBitmapCaching": "Bitmap caching uitschakelen", - "disableBitmapCachingDesc": "Bitmap cache uitschakelen (kan helpen met glitches)", - "disableOffscreenCaching": "Offscreen caching uitschakelen", - "disableOffscreenCachingDesc": "Schakel de scherm cache uit", - "disableGlyphCaching": "Glief caching uitschakelen", - "disableGlyphCachingDesc": "Zet glyph cache uit", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Gebruik de RemoteFX grafische pipeline", - "enablePrinting": "Afdrukken inschakelen", - "enablePrintingDesc": "Lokale printers omleiden naar de externe sessie", - "enableDriveRedirection": "Schijf omleiding inschakelen", - "enableDriveRedirectionDesc": "Kaart van een lokale map als schijf in de externe sessie", - "createDrivePath": "Maak Drive Pad", - "createDrivePathDesc": "Maak de map automatisch aan als deze niet bestaat", - "disableDownload": "Download uitschakelen", - "disableDownloadDesc": "Voorkom het downloaden van bestanden van de externe sessie", - "disableUpload": "Uploaden uitschakelen", - "disableUploadDesc": "Voorkom het uploaden van bestanden naar de externe sessie", - "enableTouch": "Aanraking inschakelen", - "enableTouchDesc": "Inschakelen doorsturen van aanrakingen", - "consoleSession": "Console sessie", - "consoleSessionDesc": "Maak verbinding met de console (sessie 0) in plaats van een nieuwe sessie", - "sendWolPacket": "Verstuur WOL-pakket", - "sendWolPacketDesc": "Stuur een magische pakket om deze host te ontwaken voordat u verbinding maakt", - "disableCopy": "Kopiëren uitschakelen", - "disableCopyDesc": "Voorkom kopiëren van tekst van de externe sessie", - "disablePaste": "Plakken uitschakelen", - "disablePasteDesc": "Voorkom tekst plakken in de externe sessie", - "createPathIfMissing": "Pad aanmaken als het ontbreekt", - "createPathIfMissingDesc": "Automatisch de opnamemap aanmaken", - "excludeOutput": "Uitvoer uitsluiten", - "excludeOutputDesc": "Schermuitvoer niet opnemen (alleen metadata )", - "excludeMouse": "Uitsluiten Muis", - "excludeMouseDesc": "Muisbewegingen niet opnemen", - "includeKeystrokes": "Toetsaanslagen opnemen", - "includeKeystrokesDesc": "Neem ruwe toetsaanslagen op naast schermuitvoer", - "vncPort": "VNC Poort", - "vncPassword": "VNC wachtwoord", - "vncUsernameOptional": "Gebruikersnaam (optioneel)", - "vncLeaveBlank": "Laat leeg indien niet vereist", - "cursorMode": "Cursor Modus", - "swapRedBlue": "Rood/Blauw omwisselen", - "swapRedBlueDesc": "Wissel de rode en blauwe kleurkanalen (lost enkele kleurproblemen op)", - "readOnly": "Alleen-lezen", - "readOnlyDesc": "Bekijk het externe scherm zonder invoer te verzenden", - "telnetPort": "Telnet poort", - "terminalType": "Terminal type", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Kleurenschema", - "backspaceKey": "Backspace Sleutel", - "saveHostFirst": "Sla host eerst op.", - "sharingOptionsAfterSave": "Opties voor delen zijn beschikbaar nadat de host is opgeslagen.", - "sharingLoadError": "Laden van gedeelde gegevens mislukt. Controleer je verbinding en probeer het opnieuw.", - "shareHostSection": "Deel host", - "shareWithUser": "Delen met gebruiker", - "shareWithRole": "Delen met rol", - "selectUser": "Selecteer gebruiker", - "selectRole": "Selecteer lidmaatschap", - "selectUserOption": "Selecteer een gebruiker...", - "selectRoleOption": "Selecteer een rol...", - "permissionLevel": "Permissie niveau", - "expiresInHours": "Verloopt in (uren)", - "noExpiryPlaceholder": "Laat leeg voor geen vervaldatum", - "shareBtn": "Delen", - "currentAccess": "Huidige Toegang", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Bevoegdheden", - "grantedByHeader": "Toegekend door", - "expiresHeader": "Verloopt", - "noAccessEntries": "Nog geen toegangsinsignes.", - "expiredLabel": "Verlopen", - "neverLabel": "Nooit", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "annuleren", - "savingBtn": "Opslaan...", - "updateHostBtn": "Host bijwerken", - "addHostBtn": "Host toevoegen" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Zoek hosts, commando's of instellingen...", - "quickActions": "Snelle acties", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", "hostManager": "Host Manager", - "hostManagerDesc": "Hosts beheren of bewerken", - "addNewHost": "Nieuwe host toevoegen", - "addNewHostDesc": "Registreer een nieuwe host", - "adminSettings": "Beheerder Instellingen", - "adminSettingsDesc": "Systeemvoorkeuren en gebruikers configureren", - "userProfile": "Gebruikers Profiel", - "userProfileDesc": "Beheer uw account en voorkeuren", - "addCredential": "Toegangsgegevens toevoegen", - "addCredentialDesc": "SSH sleutels of wachtwoorden opslaan", - "recentActivity": "Recente Activiteiten", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", "serversAndHosts": "Servers & Hosts", - "noHostsFound": "Geen hosts gevonden die overeenkomen met \"{{search}}\"", - "links": "Koppelingen", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Selecteren", - "toggleWith": "Schakelen met" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Geen tabblad toegewezen", - "focusedPane": "Actief venster" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Geen verbindingen", - "noConnectionsDesc": "Open een terminal, bestandsbeheerder of extern bureaublad om de verbindingen hier te bekijken.", - "connectedFor": "Verbonden voor {{duration}}", - "connected": "Aangesloten", - "disconnected": "Verbinding verbroken", - "closeTab": "Tab sluiten", - "closeConnection": "Nauwe verbinding", - "forgetTab": "Vergeten", - "removeBackground": "Verwijderen", - "reconnect": "Opnieuw verbinding maken", - "reopenTab": "Heropenen", + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", "sectionOpen": "Open", - "sectionBackground": "Achtergrond", - "backgroundDesc": "Sessies blijven 30 minuten na de onderbreking actief en kunnen daarna opnieuw worden verbonden.", - "persisted": "Bleef op de achtergrond aanwezig", - "expiresIn": "Verloopt over {{duration}}", - "search": "Zoek naar verbanden...", - "noSearchResults": "Er zijn geen resultaten gevonden die overeenkomen met uw zoekopdracht." + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Verbinden met {{type}} sessie...", - "connectionError": "Verbindingsfout", - "connectionFailed": "Verbinding mislukt", - "failedToConnect": "Verbinding token ophalen mislukt", - "hostNotFound": "Host niet gevonden", - "noHostSelected": "Geen host geselecteerd", - "reconnect": "Opnieuw verbinden", - "retry": "Opnieuw", - "guacdUnavailable": "Desktop service (guacd) is niet beschikbaar. Zorg ervoor dat guacd wordt uitgevoerd en toegankelijk en goed geconfigureerd in de admin-instellingen.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Vergrendelscherm)", - "winKey": "Windows Sleutel", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Verschuiving", - "win": "Gewonnen", - "stickyActive": "{{key}} (vergrendelde - klik om te lozen)", - "stickyInactive": "{{key}} (klik om te vergrendelen)", - "esc": "Ontsnap", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Startpagina", - "end": "Beëindigen", - "pageUp": "Pagina omhoog", - "pageDown": "Pagina omlaag", - "arrowUp": "Pijl omhoog", - "arrowDown": "Pijl omlaag", - "arrowLeft": "Pijl links", - "arrowRight": "Pijl rechts", - "fnToggle": "Functie toetsen", - "reconnect": "Sessie opnieuw verbinden", - "collapse": "Werkbalk samenvouwen", - "expand": "Werkbalk uitvouwen", - "dragHandle": "Sleep om opnieuw te plaatsen" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Verbinding maken met host", - "clear": "Verwijderen", - "paste": "Plakken", - "reconnect": "Opnieuw verbinden", - "connectionLost": "Verbinding verbroken", - "connected": "Verbonden", - "clipboardWriteFailed": "Kopiëren naar klembord mislukt. Zorg ervoor dat de pagina via HTTPS of localhost wordt geserveerd.", - "clipboardReadFailed": "Lezen van klembord mislukt. Zorg ervoor dat machtigingen voor klembord zijn verleend.", - "clipboardHttpWarning": "Plakken vereist HTTPS. Gebruik Ctrl+Shift+V of gebruik Termix over HTTPS.", - "unknownError": "Onbekende fout opgetreden", - "websocketError": "WebSocket verbindingsfout", - "connecting": "Verbinden...", - "noHostSelected": "Geen host geselecteerd", - "reconnecting": "Opnieuw verbinden... ({{attempt}}/{{max}})", - "reconnected": "Succesvol opnieuw verbonden", - "tmuxSessionCreated": "tmux sessie gemaakt: {{name}}", - "tmuxSessionAttached": "tmux sessie gekoppeld: {{name}}", - "tmuxUnavailable": "tmux is niet geïnstalleerd op de externe host, terug vallen op standaard shell", - "tmuxSessionPickerTitle": "tmux sessies", - "tmuxSessionPickerDesc": "Bestaande tmux-sessies gevonden in deze host. Selecteer er een om opnieuw toe te voegen of maak een nieuwe sessie aan.", - "tmuxWindows": "Vensters", - "tmuxWindowCount": "{{count}} venster", - "tmuxAttached": "Bijgevoegde klanten", - "tmuxAttachedCount": "{{count}} bijgevoegd", - "tmuxLastActivity": "Laatste activiteit", - "tmuxTimeJustNow": "op dit moment", - "tmuxTimeMinutes": "{{count}}m geleden", - "tmuxTimeHours": "{{count}}uur geleden", - "tmuxTimeDays": "{{count}}dagen geleden", - "tmuxCreateNew": "Nieuwe sessie starten", - "tmuxCopyHint": "Pas de selectie aan en druk op Enter om naar het klembord te kopiëren", - "tmuxDetach": "Loskoppelen van tmux sessie", - "tmuxDetached": "Losgekoppeld van tmux sessie", - "maxReconnectAttemptsReached": "Maximum aantal herverbindingpogingen bereikt", - "closeTab": "Afsluiten", - "connectionTimeout": "Connectie time-out", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Lopende {{command}} - {{host}}", - "totpRequired": "Tweestapsverificatie vereist", - "totpCodeLabel": "Verificatie Code", - "totpVerify": "Verifiëren", - "warpgateAuthRequired": "Warpgate verificatie vereist", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", "warpgateSecurityKey": "Security Key", - "warpgateAuthUrl": "URL authenticatie", - "warpgateOpenBrowser": "Open in browser", - "warpgateContinue": "Ik heb authenticatie voltooid", - "opksshAuthRequired": "OPKSSH authenticatie vereist", - "opksshAuthDescription": "Voltooi de authenticatie in je browser om door te gaan. Deze sessie blijft 24 uur geldig.", - "opksshOpenBrowser": "Browser openen om te verifiëren", - "opksshWaitingForAuth": "Wachten op authenticatie in browser...", - "opksshAuthenticating": "Authenticatie verwerken...", - "opksshTimeout": "Authenticatie is verlopen. Probeer het opnieuw.", - "opksshAuthFailed": "Authenticatie mislukt. Controleer uw inloggegevens en probeer het opnieuw.", - "opksshSignInWith": "Log in met {{provider}}", - "sudoPasswordPopupTitle": "Wachtwoord invoeren?", - "websocketAbnormalClose": "De verbinding is onverwacht afgesloten. Dit kan veroorzaakt zijn door een reverse proxy of SSL configuratie probleem. Controleer de server logs.", - "connectionLogTitle": "Connectie Log", - "connectionLogCopy": "Logboeken naar klembord kopiëren", - "connectionLogEmpty": "Nog geen verbindingslogs", - "connectionLogWaiting": "Wachten op verbindingslogboeken...", - "connectionLogCopied": "Verbindingslogboeken gekopieerd naar het klembord", - "connectionLogCopyFailed": "Kan de logs niet kopiëren naar het klembord", - "connectionRejected": "Verbinding geweigerd door de server. Controleer uw authenticatie en netwerkconfiguratie.", - "hostKeyRejected": "SSH host sleutel verificatie afgewezen. Verbinding geannuleerd.", - "sessionTakenOver": "Sessie is geopend in een ander tabblad. Opnieuw verbinden..." + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Geen host geselecteerd", - "initializingEditor": "Editor initialiseren...", - "file": "Bestand", - "folder": "Map", - "uploadFile": "Bestand uploaden", - "downloadFile": "downloaden", - "extractArchive": "Archief uitpakken", - "extractingArchive": "Uitpakken {{name}}...", - "archiveExtractedSuccessfully": "{{name}} succesvol uitgepakt", - "extractFailed": "Uitpakken mislukt", - "compressFile": "Comprimeren bestand", - "compressFiles": "Comprimeren van bestanden", - "compressFilesDesc": "Comprimeer {{count}} items in een archief", - "archiveName": "Archief Naam", - "enterArchiveName": "Archiefnaam invoeren char@@0", - "compressionFormat": "Compressie formaat", - "selectedFiles": "Geselecteerde bestanden", - "andMoreFiles": "en {{count}} meer...", - "compress": "Comprimeren", - "compressingFiles": "{{count}} items comprimeren naar {{name}}...", - "filesCompressedSuccessfully": "{{name}} succesvol aangemaakt", - "compressFailed": "Compressie mislukt", - "edit": "Bewerken", - "preview": "Voorvertoning", - "previous": "named@@0", - "next": "Volgende", - "pageXOfY": "Pagina {{current}} van {{total}}", - "zoomOut": "Zoom uit", - "zoomIn": "Zoom in", - "newFile": "Nieuw bestand", - "newFolder": "Folder toevoegen", - "rename": "Hernoem", - "uploading": "Uploaden...", - "uploadingFile": "Uploaden {{name}}...", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", "fileName": "File Name", - "folderName": "Map Naam", - "fileUploadedSuccessfully": "Bestand \"{{name}}\" succesvol geüpload", - "failedToUploadFile": "Kan bestand niet uploaden", - "fileDownloadedSuccessfully": "{{name}}\" Bestand succesvol gedownload", - "failedToDownloadFile": "Downloaden van bestand mislukt", - "fileCreatedSuccessfully": "Bestand \"{{name}}\" succesvol aangemaakt", - "folderCreatedSuccessfully": "Map \"{{name}}\" succesvol aangemaakt", - "failedToCreateItem": "Item maken mislukt", - "operationFailed": "{{operation}} bewerking mislukt voor {{name}}: {{error}}", - "failedToResolveSymlink": "Symlink vinden mislukt", - "itemsDeletedSuccessfully": "{{count}} items succesvol verwijderd", - "failedToDeleteItems": "Kan items niet verwijderen", - "sudoPasswordRequired": "Beheerder wachtwoord vereist", - "enterSudoPassword": "Voer sudo wachtwoord in om door te gaan met deze bewerking", - "sudoPassword": "Sudo wachtwoord", - "sudoOperationFailed": "Sudo bewerking mislukt", - "sudoAuthFailed": "Sudo authenticatie mislukt", - "dragFilesToUpload": "Sleep bestanden hier om te uploaden", - "emptyFolder": "Deze map is leeg", - "searchFiles": "Bestanden zoeken...", - "upload": "Uploaden", - "selectHostToStart": "Selecteer een host om bestandsbeheer te starten", - "sshRequiredForFileManager": "Bestandsbeheer vereist SSH. Deze host heeft geen SSH ingeschakeld.", - "failedToConnect": "Kan geen verbinding maken met SSH", - "failedToLoadDirectory": "Map laden mislukt", - "noSSHConnection": "Geen SSH verbinding beschikbaar", - "copy": "Kopiëren", - "cut": "Knippen", - "paste": "Plakken", - "copyPath": "Pad kopiëren", - "copyPaths": "Paden kopiëren", - "delete": "Verwijderen", - "properties": "Eigenschappen", - "refresh": "Vernieuwen", - "downloadFiles": "Download {{count}} bestanden naar Browser", - "copyFiles": "{{count}} items kopiëren", - "cutFiles": "Knip {{count}} items", - "deleteFiles": "{{count}} items verwijderen", - "filesCopiedToClipboard": "{{count}} items gekopieerd naar klembord", - "filesCutToClipboard": "{{count}} items geknipt naar het klembord", - "pathCopiedToClipboard": "Pad gekopieerd naar klembord", - "pathsCopiedToClipboard": "{{count}} paden gekopieerd naar klembord", - "failedToCopyPath": "Kopiëren van pad naar klembord is mislukt", - "movedItems": "{{count}} items verplaatst", - "failedToDeleteItem": "Kan item niet verwijderen", - "itemRenamedSuccessfully": "{{type}} succesvol hernoemd", - "failedToRenameItem": "Kan item niet hernoemen", - "download": "downloaden", - "permissions": "Machtigingen", - "size": "Grootte", - "modified": "Gewijzigd", - "path": "Pad", - "confirmDelete": "Weet u zeker dat u {{name}} wilt verwijderen?", - "permissionDenied": "Toestemming geweigerd", - "serverError": "Server fout", - "fileSavedSuccessfully": "Bestand succesvol opgeslagen", - "failedToSaveFile": "Bestand opslaan mislukt", - "confirmDeleteSingleItem": "Weet u zeker dat u wilt verwijderen \"{{name}}\"?", - "confirmDeleteMultipleItems": "Weet u zeker dat u {{count}} items permanent wilt verwijderen?", - "confirmDeleteMultipleItemsWithFolders": "Weet u zeker dat u {{count}} items permanent wilt verwijderen? Dit omvat mappen en hun inhoud.", - "confirmDeleteFolder": "Weet u zeker dat u de map \"{{name}}\" en alle bijbehorende inhoud permanent wilt verwijderen?", - "permanentDeleteWarning": "Deze actie kan niet ongedaan worden gemaakt. De item(s) zullen permanent worden verwijderd van de server.", - "recent": "Recentelijk", - "pinned": "Vastgezet", - "folderShortcuts": "Map snelkoppelingen", - "failedToReconnectSSH": "Kon SSH sessie niet opnieuw verbinden", - "openTerminalHere": "Open terminal hier", - "run": "Uitvoeren", - "openTerminalInFolder": "Open terminal in deze map", - "openTerminalInFileLocation": "Open terminal op bestandslocatie", - "runningFile": "Lopend - {{file}}", - "onlyRunExecutableFiles": "Uitvoerbare bestanden kunnen alleen worden uitgevoerd", - "directories": "Mappen", - "removedFromRecentFiles": "\"{{name}}\" verwijderd van recente bestanden", - "removeFailed": "Verwijderen mislukt", - "unpinnedSuccessfully": "Losgemaakt \"{{name}}\" met succes", - "unpinFailed": "Ontpinnen mislukt", - "removedShortcut": "Snelkoppeling \"{{name}} \" verwijderd", - "removeShortcutFailed": "Snelkoppeling verwijderen mislukt", - "clearedAllRecentFiles": "Alle recente bestanden gewist", - "clearFailed": "Opschonen mislukt", - "removeFromRecentFiles": "Verwijderen uit recente bestanden", - "clearAllRecentFiles": "Alle recente bestanden wissen", - "unpinFile": "Bestand losmaken", - "removeShortcut": "Snelkoppeling verwijderen", - "pinFile": "Pin bestand", - "addToShortcuts": "Toevoegen aan snelkoppelingen", - "pasteFailed": "Plakken mislukt", - "noUndoableActions": "Geen onuitvoerbare acties", - "undoCopySuccess": "Ongedaan kopiëren bewerking: verwijderde {{count}} gekopieerde bestanden", - "undoCopyFailedDelete": "Herstel mislukt: gekopieerde bestanden verwijderen is mislukt", - "undoCopyFailedNoInfo": "Herstel mislukt: gekopieerde bestandsinformatie kon niet gevonden worden", - "undoMoveSuccess": "Ongedaan verplaatsen bewerking: {{count}} bestanden teruggebracht naar de oorspronkelijke locatie", - "undoMoveFailedMove": "Ongedaan maken mislukt: Kon geen bestanden terugplaatsen", - "undoMoveFailedNoInfo": "Ongedaan maken mislukt: Kon verplaatste bestandsinformatie niet vinden", - "undoDeleteNotSupported": "Verwijderen actie kan niet ongedaan worden gemaakt: bestanden zijn permanent verwijderd van de server", - "undoTypeNotSupported": "Niet-ondersteunde actie type ongedaan maken", - "undoOperationFailed": "Annuleren actie mislukt", - "unknownError": "Onbekende fout.", - "confirm": "Bevestigen", - "find": "Zoeken...", - "replace": "Vervangen", - "downloadInstead": "Download in plaats daarvan", - "keyboardShortcuts": "Toetsenbord snelkoppelingen", - "searchAndReplace": "Zoeken & vervangen", - "editing": "Bewerken", - "search": "Zoeken", - "findNext": "Volgende zoeken", - "findPrevious": "Vorige zoeken", - "save": "Opslaan", - "selectAll": "Alles selecteren", - "undo": "Herstel", - "redo": "Opnieuw", - "moveLineUp": "Lijn omhoog verplaatsen", - "moveLineDown": "Lijn omlaag verplaatsen", - "toggleComment": "Opmerking in-/uitschakelen", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Fout bij laden van afbeelding", - "startTyping": "Begin met typen...", - "unknownSize": "Onbekende grootte", - "fileIsEmpty": "Bestand is leeg", - "largeFileWarning": "Waarschuwing groot bestand", - "largeFileWarningDesc": "Dit bestand is {{size}} in grootte, dit kan prestatieproblemen veroorzaken bij het openen van de tekst.", - "fileNotFoundAndRemoved": "Bestand \"{{name}}\" niet gevonden en is verwijderd van recente / vastgezette bestanden", - "failedToLoadFile": "Bestand laden mislukt: {{error}}", - "serverErrorOccurred": "Serverfout opgetreden. Probeer het later opnieuw.", - "autoSaveFailed": "Automatisch opslaan mislukt", - "fileAutoSaved": "Bestand automatisch opgeslagen", - "moveFileFailed": "{{name}} kon niet verplaatst worden", - "moveOperationFailed": "Verplaatsen mislukt", - "canOnlyCompareFiles": "Kan slechts twee bestanden vergelijken", - "comparingFiles": "Bestanden vergelijken: {{file1}} en {{file2}}", - "dragFailed": "Sleep bewerking mislukt", - "filePinnedSuccessfully": "Bestand \"{{name}}\" met succes vastgezet", - "pinFileFailed": "Kan bestand niet vastmaken", - "fileUnpinnedSuccessfully": "Bestand \"{{name}}\" is succesvol losgemaakt", - "unpinFileFailed": "Kan bestand niet losmaken", - "shortcutAddedSuccessfully": "Snelkoppeling \"{{name}}\" succesvol toegevoegd", - "addShortcutFailed": "Snelkoppeling toevoegen mislukt", - "operationCompletedSuccessfully": "{{operation}} {{count}} items succesvol", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", "operationCompleted": "{{operation}} {{count}} items", - "downloadFileSuccess": "{{name}} bestand succesvol gedownload", - "downloadFileFailed": "Downloaden mislukt", - "moveTo": "Verplaatsen naar {{name}}", - "diffCompareWith": "Verschil met {{name}}", - "dragOutsideToDownload": "Sleep buiten het venster om te downloaden ({{count}} bestanden)", - "newFolderDefault": "Nieuwe map", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Succesvol {{count}} items verplaatst naar {{target}}", - "move": "Verplaatsen", - "searchInFile": "In bestand zoeken (Ctrl+F)", - "showKeyboardShortcuts": "Toon sneltoetsen", - "startWritingMarkdown": "Begin met het schrijven van de markdown inhoud...", - "loadingFileComparison": "Bestandsvergelijking laden...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Vergelijk", - "sideBySide": "Zijde door Zijde", + "compare": "Compare", + "sideBySide": "Side by Side", "inline": "Inline", - "fileComparison": "Bestand Vergelijking: {{file1}} vs {{file2}}", - "fileTooLarge": "Bestand te groot: {{error}}", - "sshConnectionFailed": "SSH verbinding mislukt. Controleer uw verbinding met {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Bestand laden mislukt: {{error}}", - "connecting": "Verbinden...", - "connectedSuccessfully": "Succesvol verbonden", - "totpVerificationFailed": "TOTP-verificatie mislukt", - "warpgateVerificationFailed": "Warpgate authenticatie mislukt", - "authenticationFailed": "Authenticatie mislukt", - "verificationCodePrompt": "Verificatiecode:", - "changePermissions": "Rechten wijzigen", - "currentPermissions": "Huidige permissies", - "owner": "Eigenaar", - "group": "Groeperen", - "others": "Anderen", - "read": "Lezen", - "write": "Schrijven", - "execute": "Uitvoeren", - "permissionsChangedSuccessfully": "Machtigingen met succes gewijzigd", - "failedToChangePermissions": "Permissies wijzigen mislukt", - "name": "naam", - "sortByName": "naam", - "sortByDate": "Datum gewijzigd", - "sortBySize": "Grootte", - "ascending": "Oplopend", - "descending": "Aflopend", - "root": "Hoofd", - "new": "Nieuw", - "sortBy": "Sorteren op", - "items": "Artikelen", - "selected": "Geselecteerd", - "editor": "Tekstverwerker", - "octal": "Octaal", - "storage": "Opslagruimte", - "disk": "Schijf", - "used": "Gebruikt", - "of": "van", - "toggleSidebar": "Zijbalk in-/uitschakelen", - "cannotLoadPdf": "Kan PDF niet laden", - "pdfLoadError": "Er is een fout opgetreden bij het laden van dit PDF-bestand.", - "loadingPdf": "PDF laden...", - "loadingPage": "Pagina laden..." + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopiëren naar host…", - "moveToHost": "Verplaats naar host…", - "copyItemsToHost": "Kopieer {{count}} items naar host…", - "moveItemsToHost": "Verplaats {{count}} items naar host…", - "noHostsConnected": "Er zijn geen andere bestandsbeheerhosts beschikbaar.", - "noHostsConnectedHint": "Voeg een andere SSH-host toe met Bestandsbeheer ingeschakeld in Hostbeheer.", - "selectDestinationHost": "Selecteer de bestemmingshost", - "destinationPath": "Bestemmingspad", - "recentDestinations": "Recente bestemmingen", - "collapseRecentDestinations": "Recente bestemmingen inklappen", - "expandRecentDestinations": "Recente bestemmingen uitbreiden", - "browseFolders": "Blader door bestemmingsmappen", - "browseDestination": "Bladeren of pad invoeren", - "confirmCopy": "Kopiëren", - "confirmMove": "Beweging", - "transferring": "Overdracht…", - "compressing": "Comprimeren…", - "extracting": "Extractie van…", - "transferringItems": "Overdracht van {{current}} van {{total}} items…", - "transferSuccess": "Overdracht voltooid", - "transferError": "Overdracht mislukt", - "transferPartial": "Overdracht voltooid met {{count}} fouten", - "transferPartialHint": "Overdracht mislukt: {{paths}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", "itemsSummary": "{{count}} items", - "destMustBeDirectory": "De bestemming moet een adresboek zijn voor overdrachten van meerdere items.", - "selectThisFolder": "Selecteer deze map", - "browsePathWillBeCreated": "Deze map bestaat nog niet. Hij wordt aangemaakt zodra de overdracht start.", - "browsePathError": "Dit pad kon niet worden geopend op de doelhost.", - "goUp": "Ga omhoog", - "copyFolderToHost": "Kopieer de map naar de host…", - "moveFolderToHost": "Map verplaatsen naar host…", - "hostReady": "Klaar", - "hostConnecting": "Verbinding maken…", - "hostDisconnected": "Niet verbonden", - "hostAuthRequired": "Authenticatie vereist — open eerst Bestandsbeheer op deze host.", - "hostConnectionFailed": "Verbinding mislukt", - "metricsTitle": "Transfertijden", - "metricsPrepare": "Bestemming voorbereiden: {{duration}}", - "metricsCompress": "Comprimeren op bron: {{duration}}", - "metricsHopSourceRead": "Bron → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → bestemming (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → bestemming (lokaal): {{throughput}}", - "metricsTransfer": "Einde tot einde: {{throughput}} ({{duration}})", - "metricsExtract": "Uitpakken op bestemming: {{duration}}", - "metricsSourceDelete": "Verwijderen uit de bron: {{duration}}", - "metricsTotal": "Totaal: {{duration}}", - "progressCompressing": "Comprimeren op de bronhost…", - "progressExtracting": "Uitpakken op bestemming…", - "progressTransferring": "Gegevens overdragen…", - "progressReconnecting": "Opnieuw verbinding maken…", - "parallelSegmentsLabel": "Parallelle transferstroken", - "parallelSegmentsOption": "{{count}} rijstroken", - "parallelSegmentsHint": "Grote bestanden worden opgesplitst in blokken van 256 MB. Meerdere lanes gebruiken aparte verbindingen (alsof er meerdere overdrachten worden gestart) voor een hogere totale doorvoer.", - "progressTotalSpeed": "{{speed}} totaal ({{lanes}} rijstroken)", - "progressTransferringItems": "Bestanden overdragen ({{current}} van {{total}})…", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} bestanden", - "sourceNotDeletedPartial": "Bronbestanden behouden (gedeeltelijke overdracht)", - "jumpHostLimitation": "Beide hosts moeten bereikbaar zijn vanaf de Termix-server. Directe routering tussen hosts wordt niet ondersteund.", - "cancel": "Annuleren", - "methodLabel": "Overdrachtsmethode", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Tar-archief", - "methodItemSftp": "SFTP per bestand", - "methodAutoHint": "Selecteert tar of SFTP per bestand op basis van het aantal bestanden, de grootte en de comprimeerbaarheid. Voor afzonderlijke bestanden wordt altijd streaming SFTP gebruikt.", - "methodTarHint": "Comprimeer op de bron, verstuur één archiefbestand en pak het uit op de bestemming. Vereist tar op beide Unix-hosts.", - "methodItemSftpHint": "Verstuur elk bestand afzonderlijk via SFTP. Werkt op alle besturingssystemen, inclusief Windows.", - "methodPreviewLoading": "Berekeningsmethode voor overdracht…", - "methodPreviewError": "De overdrachtsmethode kon niet worden bekeken. De server kiest nog steeds een methode wanneer u start.", - "methodPreviewWillUseTar": "Zal gebruiken: Tar-archief", - "methodPreviewWillUseItemSftp": "Zal gebruikmaken van: SFTP per bestand", - "methodPreviewScanSummary": "{{fileCount}} bestanden, {{totalSize}} totaal (gescand op de bronhost).", - "methodItemSftpLimitation": "Elk bestand gebruikt dezelfde SFTP-stream als een kopie van een enkel bestand, de een na de ander. De voortgang wordt gecombineerd voor alle bestanden, waardoor de voortgangsbalk langzaam beweegt bij grote bestanden.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Je hebt gekozen voor SFTP per bestand.", - "user_tar": "Je hebt gekozen voor een tar-archief.", - "tar_unavailable": "Tar is niet beschikbaar op een of beide hosts; in plaats daarvan wordt SFTP per bestand gebruikt.", - "windows_host": "Er wordt gebruikgemaakt van een Windows-host; tar wordt niet gebruikt.", - "auto_multi_large": "Automatisch: meerdere bestanden, waaronder een groot bestand ({{largestSize}}) met comprimeerbare gegevens — tar bundelt deze tot één overdracht.", - "auto_single_large_in_archive": "Auto: één groot bestand ({{largestSize}}) in deze set — SFTP per bestand.", - "auto_many_incompressible": "Automatisch: voornamelijk niet-comprimeerbare gegevens — SFTP per bestand.", - "auto_many_files": "Auto: veel bestanden ({{fileCount}}) — tar vermindert de overhead per bestand.", - "auto_default": "Automatisch: SFTP per bestand voor deze set." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Annuleren", - "progressCancelling": "Annuleren…", - "progressStalled": "Vastgelopen", - "resumedHint": "De verbinding met een actieve overdracht die in een ander venster is gestart, is hersteld.", - "transferCancelled": "Overdracht geannuleerd", - "transferCancelledCopyHint": "Er kunnen gedeeltelijke bestanden op de bestemming achterblijven.", - "transferCancelledMoveHint": "Er kunnen gedeeltelijke bestanden op de bestemming achterblijven. De bronbestanden zijn niet verwijderd.", - "cleanupDestFiles": "Opruimbestemming", - "cleanupDestFilesSuccess": "Gedeeltelijke bestanden verwijderd van de bestemming.", - "cleanupDestFilesPartial": "Sommige gedeeltelijke bestanden konden niet worden verwijderd.", - "cleanupDestFilesNothing": "Niets op te ruimen op de bestemming.", - "cleanupDestFilesError": "Opruimen mislukt", - "retryTransfer": "Opnieuw proberen", - "retryTransferError": "Opnieuw proberen mislukt", - "transferFailedRetryHint": "Er zijn gedeeltelijke gegevens op de bestemming bewaard gebleven. De herhaalpoging wordt hervat zodra de verbinding hersteld is." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Geen SSH tunnels", - "createFirstTunnelMessage": "U heeft nog geen SSH tunnels aangemaakt. Configureer tunnelverbindingen in de Host Manager om te beginnen.", - "connected": "Verbonden", - "disconnected": "Losgekoppeld", - "connecting": "Verbinden...", - "error": "Foutmelding", - "canceling": "Annuleren...", - "connect": "Verbinden", - "disconnect": "Verbreek", - "cancel": "annuleren", - "port": "Poort", - "localPort": "Lokale poort", - "remotePort": "Externe poort", - "currentHostPort": "Huidige host poort", - "endpointPort": "Eindpunt Poort", - "bindIp": "Lokaal IP-adres", - "endpointSshConfig": "Endpoint SSH configuratie", - "endpointSshHost": "Eindpunt SSH host", - "endpointSshHostPlaceholder": "Selecteer een geconfigureerde host", - "endpointSshHostRequired": "Selecteer een SSH-host voor elke clienttunnel.", - "attempt": "Poging {{current}} van {{max}}", - "nextRetryIn": "Volgende herpoging in {{seconds}} seconden", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", "clientTunnels": "Client Tunnels", "clientTunnel": "Client Tunnel", - "addClientTunnel": "Client-tunnel toevoegen", - "noClientTunnels": "Geen clienttunnels op dit bureaublad geconfigureerd.", - "tunnelName": "Tunnel naam", - "remoteHost": "Externe host", - "autoStart": "Automatisch starten", - "clientAutoStartDesc": "Start wanneer deze desktopclient opent en verbonden blijft.", - "clientManualStartDesc": "Start en Stop vanaf deze rij. Termix zal het niet automatisch openen.", - "clientRemoteServerNote": "Op afstand doorschakelen kan AllowTcpForwarding en GatewayPorts vereisen op de SSH-server van het eindpunt. De externe poort sluit wanneer deze desktop verbinding verbreekt.", - "clientTunnelStarted": "Client tunnel gestart", - "clientTunnelStopped": "Clienttunnel gestopt", - "tunnelTestSucceeded": "Tunnel test geslaagd", - "tunnelTestFailed": "Tunnel test mislukt", - "localSaved": "Client tunnels opgeslagen", - "localSaveError": "Opslaan van lokale clienttunnels mislukt", - "invalidBindIp": "Lokaal IP adres moet een geldig IPv4-adres zijn.", - "invalidLocalTargetIp": "Lokaal doel IP moet een geldig IPv4-adres zijn.", - "invalidLocalPort": "De lokale poort moet tussen 1 en 65535 liggen.", - "invalidRemotePort": "De externe poort moet tussen 1 en 65535 liggen.", - "invalidLocalTargetPort": "Lokale doelpoort moet tussen 1 en 65535 liggen.", - "invalidEndpointPort": "Eindpunt poort moet tussen 1 en 65535 liggen.", - "duplicateAutoStartBind": "Slechts één auto-start tunnel kan {{bind}} gebruiken.", - "manualControlError": "Bijwerken van tunnel status mislukt.", - "active": "actief", - "start": "Beginnen", - "stop": "Stoppen", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", "test": "Test", - "type": "Tunnel type", - "typeLocal": "Lokaal (-L)", - "typeRemote": "Afstandsbediening (-R)", - "typeDynamic": "Dynamisch (-D)", - "typeServerLocalDesc": "Huidige host tot het eindpunt.", - "typeServerRemoteDesc": "Eindig terug naar de huidige host.", - "typeClientLocalDesc": "Lokale computer tot het eindpunt.", - "typeClientRemoteDesc": "Eindpoint terug naar de lokale computer.", - "typeClientDynamicDesc": "SOCKS op lokale computer.", - "typeDynamicDesc": "SOCKS5 CONNECT-verkeer via SSH doorsturen", - "forwardDescriptionServerLocal": "Huidige host {{sourcePort}} → eindpunt {{endpointPort}}.", - "forwardDescriptionServerRemote": "Eindpunt {{endpointPort}} → huidige host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS van huidige host {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokale {{sourcePort}} → remote {{endpointPort}}.", - "forwardDescriptionClientRemote": "Externe {{sourcePort}} → lokale {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS op lokale poort {{sourcePort}}.", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "Lokale {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokale {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", "route": "Route:", - "lastStarted": "Laatst gestart", - "lastTested": "Laatst getest", - "lastError": "Laatste fout", - "maxRetries": "Maximaal aantal pogingen", - "maxRetriesDescription": "Maximale hoeveelheid opnieuw proberen te proberen.", - "retryInterval": "Interval opnieuw proberen (seconden)", - "retryIntervalDescription": "Tijd om te wachten tussen opnieuw proberen pogingen.", - "local": "lokaal", - "remote": "Afstandsbediening", - "destination": "Doelstelling", - "host": "Hostnaam", - "mode": "Modus", - "noHostSelected": "Geen host geselecteerd", - "working": "Bezig..." + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Geheugen", - "disk": "Schijf", - "network": "Netwerk", - "uptime": "Actief", - "processes": "Proces", - "available": "Beschikbaar", - "free": "Vrij", - "connecting": "Verbinden...", - "connectionFailed": "Kan geen verbinding maken met de server", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "{{count}} kern", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU gebruik", - "memoryUsage": "Geheugen gebruik", - "diskUsage": "Schijf gebruik", - "failedToFetchHostConfig": "Hostconfiguratie ophalen mislukt", - "serverOffline": "Server offline", - "cannotFetchMetrics": "Kan statistieken niet ophalen van offline server", - "totpFailed": "TOTP-verificatie mislukt", - "noneAuthNotSupported": "Server Stats ondersteunt geen 'none' authenticatietype.", - "load": "Belasting", - "systemInfo": "Systeem informatie", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Operating systeem", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "seconden", - "networkInterfaces": "Netwerk Interfaces", - "noInterfacesFound": "Geen netwerkinterfaces gevonden", - "noProcessesFound": "Geen processen gevonden", - "loginStats": "SSH loginstatistieken", - "noRecentLoginData": "Geen recente inloggegevens", - "executingQuickAction": "Uitvoeren {{name}}...", - "quickActionSuccess": "{{name}} succesvol voltooid", - "quickActionFailed": "{{name}} is mislukt", - "quickActionError": "Kan {{name}} niet uitvoeren", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Luisterende Poorten", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Poort", - "address": "Adres:", - "process": "Verwerk", - "noData": "Geen afluisterhavengegevens" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inactief", - "policy": "Beleid", - "rules": "regels", - "noData": "Geen firewall gegevens beschikbaar", - "action": "actie", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Poort", - "source": "Bron", - "anywhere": "Overal" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Lading Gem.", - "swap": "Wisselen", - "architecture": "Architectuur", - "refresh": "Vernieuwen", - "retry": "Opnieuw" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Zelf gehoste SSH- en beheerservices voor externe bureaubladen", - "loginTitle": "Inloggen op Termix", - "registerTitle": "Account Aanmaken", - "forgotPassword": "Wachtwoord Vergeten?", - "rememberMe": "Onthoud apparaat voor 30 dagen (inclusief TOTP)", - "noAccount": "Nog geen account?", - "hasAccount": "Heeft u al een account?", - "twoFactorAuth": "Tweestapsverificatie verificatie", - "enterCode": "Voer verificatiecode in", - "backupCode": "Of gebruik back-up code", - "verifyCode": "Verifieer code", - "redirectingToApp": "Omleiden naar app...", - "sshAuthenticationRequired": "SSH-verificatie vereist", - "sshNoKeyboardInteractive": "Keyboard-Interactive Authenticatie niet beschikbaar", - "sshAuthenticationFailed": "Authenticatie mislukt", - "sshAuthenticationTimeout": "Authenticatie Time-out", - "sshNoKeyboardInteractiveDescription": "De server ondersteunt geen keyboard-interactieve authenticatie. Geef uw wachtwoord of SSH sleutel op.", - "sshAuthFailedDescription": "De ingevoerde referenties zijn onjuist. Probeer het opnieuw met geldige inloggegevens.", - "sshTimeoutDescription": "De verificatiepoging is verlopen. Probeer het opnieuw.", - "sshProvideCredentialsDescription": "Geef uw SSH inloggegevens op om verbinding te maken met deze server.", - "sshPasswordDescription": "Voer het wachtwoord in voor deze SSH verbinding.", - "sshKeyPasswordDescription": "Als uw SSH sleutel is versleuteld, voer hier het wachtwoord in.", - "passphraseRequired": "Wachtwoordzin vereist", - "passphraseRequiredDescription": "De SSH sleutel is versleuteld. Voer het wachtwoord in om te ontgrendelen.", - "back": "Achterzijde", - "firstUser": "Eerste gebruiker", - "firstUserMessage": "U bent de eerste gebruiker en zal een beheerder worden. U kunt de beheerdersinstellingen bekijken in de dropdown van de sidebar gebruikers. Als u denkt dat dit een vergissing is, bekijk dan de logboeken van de docker of maak een GitHub probleem.", - "external": "Extern", - "loginWithExternal": "Inloggen met externe provider", - "loginWithExternalDesc": "Login met behulp van uw geconfigureerde externe identiteitsprovider", - "externalNotSupportedInElectron": "Externe authenticatie wordt nog niet ondersteund in de Electron app. Gebruik de web versie voor OIDC login.", - "resetPasswordButton": "Wachtwoord opnieuw instellen", - "sendResetCode": "Reset-code verzenden", - "resetCodeDesc": "Voer uw gebruikersnaam in om een wachtwoord reset code te ontvangen. De code zal worden ingelogd in de container logs van de koppeler.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verifieer code", - "enterResetCode": "Voer de 6-cijferige code van de docker container logs in voor gebruiker:", - "newPassword": "Nieuw wachtwoord", - "confirmNewPassword": "Bevestig wachtwoord", - "enterNewPassword": "Voer uw nieuwe wachtwoord in voor gebruiker:", - "signUp": "Meld je aan", - "desktopApp": "Bureaublad App", - "loggingInToDesktopApp": "Inloggen op de desktopapp", - "loadingServer": "Server laden...", - "dataLossWarning": "Het resetten van uw wachtwoord op deze manier zal al uw opgeslagen SSH hosts, referenties en andere versleutelde data verwijderen. Deze actie kan niet ongedaan worden gemaakt. Gebruik deze actie alleen als u uw wachtwoord bent vergeten en u niet bent ingelogd.", - "authenticationDisabled": "Authenticatie uitgeschakeld", - "authenticationDisabledDesc": "Alle verificatiemethoden zijn momenteel uitgeschakeld. Neem contact op met uw beheerder.", - "attemptsRemaining": "{{count}} resterende pogingen" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifieer SSH host-sleutel", - "keyChangedWarning": "SSH host-sleutel gewijzigd", - "firstConnectionTitle": "Eerste keer verbinden met deze host", - "firstConnectionDescription": "De authenticiteit van deze host kan niet worden vastgesteld. Controleer of de vingerafdruk overeenkomt met uw verwachting.", - "keyChangedDescription": "De hostsleutel voor deze server is veranderd sinds je laatste verbinding. Dit kan duiden op een beveiligingsprobleem.", - "previousKey": "Vorige sleutel", - "newFingerprint": "Nieuwe vingerafdruk", - "fingerprint": "Vingerafdruk", - "verifyInstructions": "Als je deze host vertrouwt, klik je op Accepteren om door te gaan en sla deze vingerafdruk op voor toekomstige verbindingen.", - "securityWarning": "Waarschuwing beveiliging", - "acceptAndContinue": "Accepteren & doorgaan", - "acceptNewKey": "Accepteer Nieuwe Sleutel & Doorgaan" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Kan niet verbinden met de database", - "unknownError": "Onbekende fout.", - "loginFailed": "Inloggen mislukt", - "failedPasswordReset": "Wachtwoord resetten starten mislukt", - "failedVerifyCode": "Verifiëren reset code mislukt", - "failedCompleteReset": "Wachtwoord resetten voltooien mislukt", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "OIDC login starten mislukt", - "silentSigninOidcUnavailable": "Stille aanmelding is aangevraagd, maar OIDC-login is niet beschikbaar.", - "failedUserInfo": "Fout bij het ophalen van gebruikersinformatie na het inloggen", - "oidcAuthFailed": "OIDC authenticatie mislukt", - "invalidAuthUrl": "Ongeldige autorisatie URL ontvangen van de backend", - "requiredField": "Dit veld is verplicht", - "minLength": "Minimale lengte is {{min}}", - "passwordMismatch": "Wachtwoorden komen niet overeen", - "passwordLoginDisabled": "Gebruikersnaam/wachtwoord login is momenteel uitgeschakeld", - "sessionExpired": "Sessie verlopen - Log opnieuw in", - "totpRateLimited": "Tarief beperkt: Te veel TOTP-verificatiepogingen. Probeer het later opnieuw.", - "totpRateLimitedWithTime": "Tarief beperkt: te veel TOTP verificatiepogingen. Wacht {{time}} seconden voordat u het opnieuw probeert.", - "resetCodeRateLimited": "Snelheid beperkt: Te veel verificatiepogingen. Probeer het later opnieuw.", - "resetCodeRateLimitedWithTime": "Tarief beperkt: Te veel verificatiepogingen. Wacht {{time}} seconden voordat u het opnieuw probeert.", - "authTokenSaveFailed": "Authenticatietoken opslaan mislukt", - "failedToLoadServer": "Kon server niet laden" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Nieuwe accountregistratie is momenteel uitgeschakeld door een beheerder. Log in of neem contact op met een beheerder.", - "userNotAllowed": "Uw account is niet gemachtigd om te registreren. Neem contact op met een beheerder.", - "databaseConnectionFailed": "Kan geen verbinding maken met de databaseserver", - "resetCodeSent": "Reset de code verzonden naar de Docker logs", - "codeVerified": "Code succesvol geverifieerd", - "passwordResetSuccess": "Wachtwoord succesvol opnieuw instellen", - "loginSuccess": "Inloggen succesvol", - "registrationSuccess": "Registratie succesvol" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokale desktoptunnels gericht op geconfigureerde SSH hosts.", - "c2sTunnelPresets": "Voorinstellingen voor klanttunnel", - "c2sTunnelPresetsDesc": "Sla de lokale tunnellijst van deze desktop cliënt op als een voorkeursinstelling met naam server of laad een voorinstelling terug naar deze client.", - "c2sTunnelPresetsUnavailable": "Client tunnelvoorinstellingen zijn alleen beschikbaar in de desktop client.", - "c2sPresetName": "Naam voorinstelling", - "c2sPresetNamePlaceholder": "Naam van voorinstelling klant", - "c2sPresetToLoad": "Voorinstelling om te laden", - "c2sNoPresetSelected": "Geen voorinstelling geselecteerd", - "c2sNoPresets": "Geen voorinstellingen opgeslagen", - "c2sLoadPreset": "Belasting", - "c2sCurrentLocalConfig": "{{count}} lokale clienttunnel(s) geconfigureerd op dit bureaublad.", - "c2sPresetSyncNote": "Voorkeursinstellingen zijn expliciete momentopnamen, het laden van deze desktop cliënt tunnellijst vervangt.", - "c2sPresetSaved": "Client tunnel voorinstelling opgeslagen", - "c2sPresetLoaded": "Client tunnel voorinstelling lokaal geladen", - "c2sPresetRenamed": "Klant tunnel voorinstelling hernoemd", - "c2sPresetDeleted": "Client tunnel voorinstelling verwijderd", - "c2sPresetLoadError": "Fout bij het laden van de clienttunnel voorinstellingen" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Taal", - "keyPassword": "sleutel wachtwoord", - "pastePrivateKey": "Plak uw persoonlijke sleutel hier...", - "localListenerHost": "127.0.1 (lokaal beluisteren)", - "localTargetHost": "127.0.1 (doel op deze computer)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Voer je wachtwoord in", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { "title": "Dashboard", - "loading": "Dashboard wordt geladen...", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Ondersteuning", - "discord": "Onenigheid", - "serverOverview": "Server overzicht", - "version": "Versie", - "upToDate": "Tot op heden", - "updateAvailable": "Update beschikbaar", - "beta": "Bèta", - "uptime": "Actief", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", "database": "Database", - "healthy": "Gezond", - "error": "Foutmelding", - "totalHosts": "Totaal aantal hosts", - "totalTunnels": "Totaal aantal tunnels", - "totalCredentials": "Totaal Aanmeldgegevens", - "recentActivity": "Recente Activiteiten", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Recente activiteit laden...", - "noRecentActivity": "Geen recente activiteit", - "quickActions": "Snelle acties", - "addHost": "Host toevoegen", - "addCredential": "Toegangsgegevens toevoegen", - "adminSettings": "Beheerder Instellingen", - "userProfile": "Gebruikers Profiel", - "serverStats": "Server Statistieken", - "loadingServerStats": "Server statistieken laden...", - "noServerData": "Geen servergegevens beschikbaar", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Dashboard aanpassen", - "dashboardSettings": "Dashboard instellingen", - "enableDisableCards": "Kaarten in-/uitschakelen", - "resetLayout": "Standaardinstellingen herstellen", - "serverOverviewCard": "Server overzicht", - "recentActivityCard": "Recente Activiteiten", - "networkGraphCard": "Netwerk Grafiek", - "networkGraph": "Netwerk Grafiek", - "quickActionsCard": "Snelle acties", - "serverStatsCard": "Server Statistieken", - "panelMain": "Algemeen", - "panelSide": "Zijde", - "justNow": "op dit moment" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { "stable": "STABLE", "hostsOnline": "Hosts Online", - "activeTunnels": "Actieve tunnels", - "registerNewServer": "Registreer een nieuwe server", - "storeSshKeysOrPasswords": "SSH sleutels of wachtwoorden opslaan", - "manageUsersAndRoles": "Gebruikers en rollen beheren", - "manageYourAccount": "Beheer je account", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", "hostStatus": "Host Status", - "noHostsConfigured": "Geen hosts geconfigureerd", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", - "offline": "AANBIEDING", + "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Toevoegen:", - "commandPalette": "Command palet", - "done": "Voltooid", - "editModeInstructions": "Sleep kaarten om te rangschikken · Sleep de kolom verdeler om kolommen aan te passen · Sleep de onderrand van een kaart om de hoogte aan te passen · Prullenbak om te verwijderen", - "empty": "Leeg", - "clear": "Verwijderen" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Host toevoegen", - "addGroup": "Groep toevoegen", - "addLink": "Link toevoegen", - "zoomIn": "Zoom in", - "zoomOut": "Zoom uit", - "resetView": "Weergave resetten", - "selectHost": "Selecteer Host", - "chooseHost": "Kies een host...", - "parentGroup": "Bovenliggende groep", - "noGroup": "Geen groep", - "groupName": "Groep Naam", - "color": "Kleur", - "source": "Bron", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Verplaats naar groep", - "selectGroup": "Selecteer groep...", - "addConnection": "Voeg contact toe", - "hostDetails": "Details host", - "removeFromGroup": "Verwijder uit groep", - "addHostHere": "Host hier toevoegen", - "editGroup": "Groep bewerken", - "delete": "Verwijderen", - "add": "Toevoegen", - "create": "Aanmaken", - "move": "Verplaatsen", - "connect": "Verbinden", - "createGroup": "Groep aanmaken", - "selectSourcePlaceholder": "Selecteer bron...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Ongeldig bestand", - "hostAlreadyExists": "Host is al in de topologie", - "connectionExists": "Verbinding bestaat al", - "unknown": "onbekend", - "name": "naam", - "ip": "IP-adres", - "status": "status", - "failedToAddNode": "Toevoegen van node mislukt", - "sourceDifferentFromTarget": "Bron en doel moeten verschillend zijn", - "exportJSON": "JSON exporteren", - "importJSON": "JSON importeren", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Bestands Beheer", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Server Statistieken", - "noNodes": "Nog geen knooppunten" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker is niet ingeschakeld voor deze host", - "validating": "Docker valideren...", - "connecting": "Verbinden...", - "error": "Foutmelding", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Kan geen verbinding maken met Docker", - "containerStarted": "Container {{name}} gestart", - "failedToStartContainer": "Kan container {{name}} niet starten", - "containerStopped": "Container {{name}} gestopt", - "failedToStopContainer": "Stoppen van container {{name}} mislukt", - "containerRestarted": "Container {{name}} herstart", - "failedToRestartContainer": "Herstarten van container {{name}} is mislukt", - "containerPaused": "Container {{name}} onderbroken", - "containerUnpaused": "Container {{name}} ononderbroken", - "failedToTogglePauseContainer": "Pauzestatus voor container {{name}} in- en uitschakelen mislukt", - "containerRemoved": "Container {{name}} verwijderd", - "failedToRemoveContainer": "Verwijderen van container {{name}} mislukt", - "image": "Afbeelding", - "ports": "Poorten", - "noPorts": "Geen poorten", - "start": "Beginnen", - "confirmRemoveContainer": "Weet u zeker dat u de container '{{name}}' wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.", - "runningContainerWarning": "Waarschuwing: Deze container is momenteel actief. Verwijderen zal de container eerst stoppen.", - "loadingContainers": "containers laden...", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", "manager": "Docker Manager", - "autoRefresh": "Automatisch verversen", - "timestamps": "Tijdstempels", - "lines": "Regels", - "filterLogs": "Logboeken filteren...", - "refresh": "Vernieuwen", - "download": "downloaden", - "clear": "Verwijderen", - "logsDownloaded": "Logboeken succesvol gedownload", - "last50": "Laatste 50", - "last100": "Laatste 100", - "last500": "Laatste 500", - "last1000": "Laatste 1000", - "allLogs": "Alle logs", - "noLogsMatching": "Geen logs die overeenkomen met \"{{query}}\"", - "noLogsAvailable": "Geen logs beschikbaar", - "noContainersFound": "Geen containers gevonden", - "noContainersFoundHint": "Er zijn geen Docker containers beschikbaar op deze host", - "searchPlaceholder": "Zoek containers...", - "allStatuses": "Alle statussen", - "stateRunning": "Lopend", - "statePaused": "Gepauzeerd", - "stateExited": "Verlaat", - "stateRestarting": "Herstarten", - "noContainersMatchFilters": "Geen containers die overeenkomen met uw filters", - "noContainersMatchFiltersHint": "Probeer uw zoekopdracht of filtercriteria aan te passen", - "failedToFetchStats": "Kan containerstatistieken niet ophalen", - "containerNotRunning": "Container niet actief", - "startContainerToViewStats": "Begin de container om statistieken te bekijken", - "loadingStats": "Statistieken laden...", - "errorLoadingStats": "Fout bij laden statistieken", - "noStatsAvailable": "Geen statistieken beschikbaar", - "cpuUsage": "CPU gebruik", - "current": "Stroom", - "memoryUsage": "Geheugen gebruik", - "networkIo": "Netwerk I/O", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Uitvoer", - "blockIo": "Blokkeer I/O", - "read": "Lezen", - "write": "Schrijven", - "pids": "PID's", - "containerInformation": "Container informatie", - "name": "naam", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Provincie", - "containerMustBeRunning": "Container moet worden uitgevoerd voor toegangsconsole", - "verificationCodePrompt": "Voer verificatiecode in", - "totpVerificationFailed": "TOTP-verificatie mislukt. Probeer het opnieuw.", - "warpgateVerificationFailed": "Warpgate authenticatie is mislukt. Probeer het opnieuw.", - "connectedTo": "Verbonden met {{containerName}}", - "disconnected": "Losgekoppeld", - "consoleError": "Console fout", - "errorMessage": "Fout: {{message}}", - "failedToConnect": "Kan geen verbinding maken met container", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", "console": "Console", - "selectShell": "Selecteer shell", - "bash": "Basis", - "sh": "sha", - "ash": "as", - "connect": "Verbinden", - "disconnect": "Verbreek", - "notConnected": "Niet verbonden", - "clickToConnect": "Klik op verbinden om een shell sessie te starten", - "connectingTo": "Verbinden met {{containerName}}...", - "containerNotFound": "Container niet gevonden", - "backToList": "Terug naar lijst", - "logs": "Logboeken", - "stats": "Statistieken", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Start de container voor toegang tot de console" + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Algemeen", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Gebruikers", - "sectionSessions": "Sessies", - "sectionRoles": "Rollen", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", "sectionDatabase": "Database", - "sectionApiKeys": "API sleutels", - "allowRegistration": "Gebruikersregistratie toestaan", - "allowRegistrationDesc": "Laat nieuwe gebruikers zichzelf registreren", - "allowPasswordLogin": "Wachtwoord-login toestaan", - "allowPasswordLoginDesc": "Gebruikersnaam/wachtwoord login", - "oidcAutoProvision": "OIDC Auto-Provisie", - "oidcAutoProvisionDesc": "Automatisch accounts aanmaken voor OIDC-gebruikers, zelfs wanneer registratie is uitgeschakeld", - "allowPasswordReset": "Wachtwoord opnieuw instellen toestaan", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", "allowPasswordResetDesc": "Reset code via Docker logs", - "sessionTimeout": "Sessie time-out", - "hours": "Uren", - "sessionTimeoutRange": "Min 1u · Max 720u", - "monitoringDefaults": "Standaardwaarden controleren", - "statusCheck": "Status Controle", - "metrics": "Statistieken", - "sec": "sec.", - "logLevel": "Log niveau", - "enableGuacamole": "Guacamole inschakelen", - "enableGuacamoleDesc": "RDP/VNC afstandsbediening", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Configureer OpenID Connect voor SSO. Velden gemarkeerd * zijn vereist.", - "oidcClientId": "Klant ID", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", "oidcClientSecret": "Client Secret", - "oidcAuthUrl": "URL autorisatie", - "oidcIssuerUrl": "URL uitgever", - "oidcTokenUrl": "URL token", - "oidcUserIdentifier": "Pad voor gebruikersID's", - "oidcDisplayName": "Toon naam pad", - "oidcScopes": "Toepassingsgebieden", - "oidcUserinfoUrl": "Gebruikersinfo URL overschrijven", - "oidcAllowedUsers": "Toegestane gebruikers", - "oidcAllowedUsersDesc": "Eén e-mail per regel. Laat leeg om alles toe te staan.", - "removeOidc": "Verwijderen", - "usersCount": "{{count}} gebruikers", - "createUser": "Aanmaken", - "newRole": "Nieuwe rol", - "roleName": "naam", - "roleDisplayName": "Toon naam", - "roleDescription": "Beschrijving", - "rolesCount": "{{count}} rollen", - "createRole": "Aanmaken", - "creating": "Maken...", - "exportDatabase": "Exporteer database", - "exportDatabaseDesc": "Download een back-up van alle hosts, referenties en instellingen", - "export": "Exporteren", - "exporting": "Exporteren...", - "importDatabase": "Importeer database", - "importDatabaseDesc": "Herstel vanaf een .sqlite reservekopiebestand", - "importDatabaseSelected": "Geselecteerd: {{name}}", - "selectFile": "Bestand selecteren", - "changeFile": "Veranderen", - "import": "Importeren", - "importing": "Importeren...", - "apiKeysCount": "{{count}} sleutels", - "newApiKey": "Nieuwe API-sleutel", - "apiKeyCreatedWarning": "Sleutel gemaakt - kopieer het nu, het zal niet meer worden weergegeven.", - "apiKeyName": "naam", - "apiKeyUser": "Gebruiker", - "apiKeySelectUser": "Selecteer een gebruiker...", - "apiKeyExpiresAt": "Verloopt op", - "createKey": "Sleutel aanmaken", - "apiKeyNoExpiry": "Geen vervaldatum", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Dubbele authenticatie", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "lokaal", - "adminStatusAdministrator": "Beheerder", - "adminStatusRegularUser": "Normale gebruiker", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "AANGEPASTE", - "youBadge": "JIJ", - "sessionsActive": "{{count}} actief", - "sessionActive": "Actief: {{time}}", - "sessionExpires": "Uitbreiden: {{time}}", - "revokeAll": "Allemaal", - "revokeAllSessionsSuccess": "Alle sessies van de gebruiker ingetrokken", - "revokeAllSessionsFailed": "Intrekken van sessies mislukt", - "revokeSessionFailed": "Intrekken van de sessie is mislukt", - "addRole": "Rol toevoegen", - "noCustomRoles": "Geen aangepaste rollen gedefinieerd", - "removeRoleFailed": "Rol verwijderen mislukt", - "assignRoleFailed": "Toewijzen van rol mislukt", - "deleteRoleFailed": "Rol verwijderen mislukt", - "userAdminAccess": "Beheerder", - "userAdminAccessDesc": "Volledige toegang tot alle admin instellingen", - "userRoles": "Rollen", - "revokeAllUserSessions": "Alle sessies intrekken", - "revokeAllUserSessionsDesc": "Forceer opnieuw inloggen op alle apparaten", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Het verwijderen van deze gebruiker is definitief.", - "deleteUser": "{{username}} verwijderen", - "deleting": "Verwijderenchar@@0", - "deleteUserFailed": "Kan gebruiker niet verwijderen", - "deleteUserSuccess": "Gebruiker \"{{username}}\" verwijderd", - "deleteRoleSuccess": "Rol \"{{name}}\" verwijderd", - "revokeKeySuccess": "Sleutel \"{{name}}\" ingetrokken", - "revokeKeyFailed": "Kan sleutel niet intrekken", - "copiedToClipboard": "Gekopieerd naar klembord", - "done": "Voltooid", - "createUserTitle": "Gebruiker aanmaken", - "createUserDesc": "Maak een nieuw lokaal account aan.", - "createUserUsername": "Gebruikersnaam", - "createUserPassword": "Wachtwoord", - "createUserPasswordHint": "Minimaal 6 tekens.", - "createUserEnterUsername": "Gebruikersnaam invoeren", - "createUserEnterPassword": "Wachtwoord invoeren", - "createUserSubmit": "Gebruiker aanmaken", - "editUserTitle": "Gebruiker beheren: {{username}}", - "editUserDesc": "Bewerk rollen, admin status, sessies en account instellingen.", - "editUserUsername": "Gebruikersnaam", - "editUserAuthType": "Authenticatie Type", - "editUserAdminStatus": "Beheerder status", - "editUserUserId": "Gebruiker ID", - "linkAccountTitle": "OIDC koppelen aan wachtwoord account", - "linkAccountDesc": "Voeg de OIDC account {{username}} samen met een bestaand lokaal account.", - "linkAccountWarningTitle": "Dit zal:", - "linkAccountEffect1": "Verwijder de OIDC-only account", - "linkAccountEffect2": "OIDC login toevoegen aan het doelaccount", - "linkAccountEffect3": "OIDC en wachtwoord toestaan", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Voer de lokale gebruikersnaam in om naar te linken", - "linkAccounts": "Accounts koppelen", - "linkAccountSuccess": "OIDC-account gekoppeld aan \"{{username}}\"", - "linkAccountFailed": "Het koppelen van het OIDC-account is mislukt.", - "linkAccountInProgress": "Koppelen...", - "saving": "Opslaan...", - "updateRegistrationFailed": "Fout bij het bijwerken van de registratie instelling", - "updatePasswordLoginFailed": "Bijwerken van wachtwoordaanmeldingsinstelling mislukt", - "updateOidcAutoProvisionFailed": "Update OIDC auto-voorziening instelling mislukt", - "updatePasswordResetFailed": "Bijwerken van wachtwoord-reset instelling mislukt", - "sessionTimeoutRange2": "Sessie time-out moet tussen 1 en 720 uur zijn", - "sessionTimeoutSaved": "Sessie timeout opgeslagen", - "sessionTimeoutSaveFailed": "Opslaan van sessie timeout mislukt", - "monitoringIntervalInvalid": "Ongeldige intervalwaarden", - "monitoringSaved": "Toezicht instellingen opgeslagen", - "monitoringSaveFailed": "Opslaan van monitoring instellingen mislukt", - "guacamoleSaved": "Guacamole instellingen opgeslagen", - "guacamoleSaveFailed": "Guacamole instellingen opslaan mislukt", - "guacamoleUpdateFailed": "Guacamole instelling bijwerken mislukt", - "logLevelUpdateFailed": "Bijwerken van logniveau is mislukt", - "oidcSaved": "OIDC configuratie opgeslagen", - "oidcSaveFailed": "OIDC configuratie opslaan mislukt", - "oidcRemoved": "OIDC configuratie verwijderd", - "oidcRemoveFailed": "OIDC configuratie verwijderen mislukt", - "createUserRequired": "Gebruikersnaam en wachtwoord zijn verplicht", - "createUserPasswordTooShort": "Wachtwoord moet ten minste 6 tekens bevatten", - "createUserSuccess": "Gebruiker \"{{username}}\" aangemaakt", - "createUserFailed": "Gebruiker aanmaken mislukt", - "updateAdminStatusFailed": "Bijwerken van admin status mislukt", - "allSessionsRevoked": "Alle sessies ingetrokken", - "revokeSessionsFailed": "Intrekken van sessies mislukt", - "createRoleRequired": "Naam en weergavenaam zijn verplicht", - "createRoleSuccess": "Rol \"{{name}}\" gemaakt", - "createRoleFailed": "Rol aanmaken mislukt", - "apiKeyNameRequired": "Sleutelnaam is vereist", - "apiKeyUserRequired": "Gebruikers-ID is vereist", - "apiKeyCreatedSuccess": "API sleutel \"{{name}}\" gemaakt", - "apiKeyCreateFailed": "API-sleutel aanmaken mislukt", - "exportSuccess": "Database succesvol geëxporteerd", - "exportFailed": "Database export mislukt", - "importSelectFile": "Selecteer eerst een bestand", - "importCompleted": "Importeren voltooid: {{total}} items geïmporteerd, {{skipped}} is overgeslagen", - "importFailed": "Importeren mislukt: {{error}}", - "importError": "Database import mislukt" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Hostnaam", - "hostPlaceholder": "192.168.1.1 of voorbeeld.com", - "portLabel": "Poort", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Gebruikersnaam", - "usernamePlaceholder": "gebruikersnaam", - "authLabel": "Authenticatie", - "passwordLabel": "Wachtwoord", - "passwordPlaceholder": "Wachtwoord", - "privateKeyLabel": "Persoonlijke sleutel", - "privateKeyPlaceholder": "Private key plakken...", - "credentialLabel": "Toegangsgegevens", - "credentialPlaceholder": "Selecteer een opgeslagen aanmeldgegevens", - "connectToTerminal": "Verbinden met terminal", - "connectToFiles": "Verbinden met bestanden" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Geen terminal geselecteerd", - "noTerminalSelectedHint": "Open een SSH terminal tabblad om de opdrachtgeschiedenis te bekijken", - "searchPlaceholder": "Geschiedenis zoeken...", - "clearAll": "Alles wissen", - "noHistoryEntries": "Geen geschiedenis items", - "trackingDisabled": "Geschiedenis volgen is uitgeschakeld", - "trackingDisabledHint": "Schakel het in bij je profielinstellingen om opdrachten op te nemen." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Sleutel Opname", - "recordToTerminals": "Opnemen naar aansluitingen", - "selectAll": "Allemaal", - "selectNone": "geen", - "noTerminalTabsOpen": "Geen terminal tabbladen geopend", - "selectTerminalsAbove": "Selecteer bovenstaande terminals", - "broadcastInputPlaceholder": "Typ hier om keystrokes...", - "stopRecording": "Opname stoppen", - "startRecording": "Opname starten", - "settingsTitle": "Instellingen", - "enableRightClickCopyPaste": "Rechtsklik kopiëren en plakken inschakelen" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Indeling", - "selectLayoutAbove": "Selecteer een lay-out hierboven", - "selectLayoutHint": "Kies hoeveel panelen getoond moeten worden", - "panesTitle": "Vensters", - "openTabsTitle": "Tabbladen openen", - "dragTabsHint": "Sleep tabbladen naar de vensters hierboven, of gebruik Snel toewijzen", - "dropHere": "Hier neerzetten", - "emptyPane": "Leeg", + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", "dashboard": "Dashboard", - "clearSplitScreen": "Wis Split Scherm", - "quickAssign": "Snel toewijzen", - "alreadyAssigned": "Paneel {{index}}", - "splitTab": "Gesplitst tabblad", - "addToSplit": "Toevoegen aan splitsen", - "removeFromSplit": "Verwijderen uit Split", - "assignToPane": "Toewijzen aan paneel" + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Snippet aanmaken", - "createSnippetDescription": "Maak een nieuwe opdrachtsnippet voor snelle uitvoering", - "nameLabel": "naam", - "namePlaceholder": "b.v. Nginx herstarten", - "descriptionLabel": "Beschrijving", - "descriptionPlaceholder": "Optionele beschrijving", - "optional": "Optioneel", - "folderLabel": "Map", - "noFolder": "Geen map (niet gecategoriseerd)", - "commandLabel": "Opdracht", - "commandPlaceholder": "bijv. nginx herstarten", - "cancel": "annuleren", - "createSnippetButton": "Snippet aanmaken", - "createFolderTitle": "Map aanmaken", - "createFolderDescription": "Organiseer uw snippets in mappen", - "folderNameLabel": "Map Naam", - "folderNamePlaceholder": "b.v. Systeem Commando's, Docker Scripts", - "folderColorLabel": "Map kleur", - "folderIconLabel": "Map pictogram", - "previewLabel": "Voorvertoning", - "folderNameFallback": "Map Naam", - "createFolderButton": "Map aanmaken", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Allemaal", - "selectNone": "geen", - "noTerminalTabsOpen": "Geen terminal tabbladen geopend", - "searchPlaceholder": "Tekstfragmenten zoeken...", - "newSnippet": "Nieuwe snippet", - "newFolder": "Folder toevoegen", - "run": "Uitvoeren", - "noSnippetsInFolder": "Geen tekstbouwstenen in deze map", - "uncategorized": "Ongecategoriseerd", - "editSnippetTitle": "Tekstfragment bewerken", - "editSnippetDescription": "Deze opdrachtsnippet bijwerken", - "saveSnippetButton": "Wijzigingen opslaan", - "createSuccess": "Snippet succesvol aangemaakt", - "createFailed": "Tekstfragment aanmaken mislukt", - "updateSuccess": "Tekstfragment met succes bijgewerkt", - "updateFailed": "Update snippet mislukt", - "deleteFailed": "Verwijderen van snippet mislukt", - "folderCreateSuccess": "Map succesvol aangemaakt", - "folderCreateFailed": "Map maken mislukt", - "editFolderTitle": "Map bewerken", - "editFolderDescription": "Het uiterlijk van deze map wijzigen of hernoemen", - "saveFolderButton": "Wijzigingen opslaan", - "editFolder": "Bewerk map", - "deleteFolder": "Map verwijderen", - "folderDeleteSuccess": "Map \"{{name}}\" verwijderd", - "folderDeleteFailed": "Map verwijderen mislukt", - "folderEditSuccess": "Map succesvol bijgewerkt", - "folderEditFailed": "Bijwerken map mislukt", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", "confirmRunMessage": "Run \"{{name}}\"?", - "confirmRunButton": "Loop", + "confirmRunButton": "Run", "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", - "copySuccess": "Gekopieerd \"{{name}}\" naar klembord", - "shareTitle": "Deel snippet", - "shareUser": "Gebruiker", - "shareRole": "Functie", - "selectUser": "Selecteer een gebruiker...", - "selectRole": "Selecteer een rol...", - "shareSuccess": "Snippet succesvol gedeeld", - "shareFailed": "Delen van snippet mislukt", - "revokeSuccess": "Toegang ingetrokken", - "revokeFailed": "Toegang intrekken mislukt", - "currentAccess": "Huidige Toegang", - "shareLoadError": "Kan deelgegevens niet laden", - "loading": "Laden...", - "close": "Afsluiten", - "reorderFailed": "Het opslaan van de volgorde van de codefragmenten is mislukt." + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Rekening", - "sectionAppearance": "Uiterlijk", - "sectionSecurity": "Beveiliging", - "sectionApiKeys": "API sleutels", - "sectionC2sTunnels": "C2S tunnels", - "usernameLabel": "Gebruikersnaam", - "roleLabel": "Functie", - "roleAdministrator": "Beheerder", - "authMethodLabel": "Auth Methode", - "authMethodLocal": "lokaal", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "op", - "twoFaOff": "UIT", - "versionLabel": "Versie", - "deleteAccount": "Verwijder account", - "deleteAccountDescription": "Account permanent verwijderen", - "deleteButton": "Verwijderen", - "deleteAccountPermanent": "Deze actie is permanent en kan niet ongedaan worden gemaakt.", - "deleteAccountWarning": "Alle sessies, hosts, referenties en instellingen zullen permanent worden verwijderd.", - "confirmPasswordDeletePlaceholder": "Voer uw wachtwoord in ter bevestiging", - "languageLabel": "Taal", - "themeLabel": "Thema", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Accent kleur", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Commando automatisch aanvullen", - "commandAutocompleteDesc": "Automatisch aanvullen weergeven tijdens typen", - "historyTracking": "Geschiedenis bijhouden", - "historyTrackingDesc": "Terminal opdrachten bijhouden", - "syntaxHighlighting": "Syntaxis markering", - "syntaxHighlightingDesc": "Terminal uitvoer markeren", - "commandPalette": "Command palet", - "commandPaletteDesc": "Sneltoets inschakelen", - "reopenTabsOnLogin": "Tabbladen opnieuw openen bij inloggen", - "reopenTabsOnLoginDesc": "Herstel je geopende tabbladen wanneer je inlogt of de pagina vernieuwt, zelfs vanaf een ander apparaat.", - "confirmTabClose": "Tabblad sluiten bevestigen", - "confirmTabCloseDesc": "Vragen voordat terminal tabbladen worden gesloten", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Toon Host Tags", - "showHostTagsDesc": "Toon tags in hostlijst", - "hostTrayOnClick": "Klik om de hostacties uit te breiden", - "hostTrayOnClickDesc": "Toon altijd de verbindingsknoppen; klik om de beheermogelijkheden uit te vouwen in plaats van eroverheen te zweven.", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Houd de app-rail aan de linkerkant altijd uitgevouwen in plaats van deze uit te vouwen wanneer je er met de muis overheen beweegt.", - "settingsSnippets": "Tekstbouwstenen", - "foldersCollapsed": "Mappen samengevouwen", - "foldersCollapsedDesc": "Mappen standaard samenvouwen", - "confirmExecution": "Uitvoering bevestigen", - "confirmExecutionDesc": "Bevestig voor uitvoeren snippets", - "settingsUpdates": "Bijwerken", - "disableUpdateChecks": "Schakel updatecontroles uit", - "disableUpdateChecksDesc": "Niet meer controleren op updates", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", "totpAuthenticator": "TOTP Authenticator", - "totpEnabled": "2FA is ingeschakeld", - "totpDisabled": "Extra inlogbeveiliging toevoegen", - "disable": "Uitschakelen", - "enable": "Inschakelen", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scan QR-code of voer het geheim in in uw authenticator-app, voer dan de 6-cijferige code in", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifiëren", - "changePassword": "Wachtwoord wijzigen", - "currentPasswordLabel": "Huidig wachtwoord", - "currentPasswordPlaceholder": "Huidig wachtwoord", - "newPasswordLabel": "Nieuw wachtwoord", - "newPasswordPlaceholder": "Nieuw wachtwoord", - "confirmPasswordLabel": "Bevestig nieuw wachtwoord", - "confirmPasswordPlaceholder": "Bevestig nieuw wachtwoord", - "updatePassword": "Wachtwoord bijwerken", - "createApiKeyTitle": "API-sleutel aanmaken", - "createApiKeyDescription": "Genereer een nieuwe API-sleutel voor programmatische toegang.", - "apiKeyNameLabel": "naam", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "optioneel", - "cancel": "annuleren", - "createKey": "Sleutel aanmaken", - "apiKeyCount": "{{count}} sleutels", - "newKey": "Nieuwe sleutel", - "noApiKeys": "Nog geen API-sleutels.", - "apiKeyActive": "actief", - "apiKeyUsageHint": "Voeg je sleutel toe aan de", - "apiKeyUsageHintHeader": "kop.", - "apiKeyPermissionsHint": "Sleutels erven de permissies van de aanmaak gebruiker.", - "roleUser": "Gebruiker", - "authMethodDual": "Dubbele authenticatie", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Kan TOTP installatie niet starten", - "totpEnter6Digits": "Voer een 6-cijferige code in", - "totpEnabledSuccess": "Tweestapsverificatie ingeschakeld", - "totpInvalidCode": "Ongeldige code, probeer het opnieuw", - "totpDisableInputRequired": "Voer uw TOTP-code of wachtwoord in", - "totpDisabledSuccess": "Tweestapsverificatie uitgeschakeld", - "totpDisableFailed": "2FA uitschakelen mislukt", - "totpDisableTitle": "2FA uitschakelen", - "totpDisablePlaceholder": "Voer TOTP-code of wachtwoord in", - "totpDisableConfirm": "2FA uitschakelen", - "totpContinueVerify": "Doorgaan met verifiëren", - "totpVerifyTitle": "Verifieer code", - "totpBackupTitle": "Back-up Codes", - "totpDownloadBackup": "Reservekopiecodes downloaden", - "done": "Voltooid", - "secretCopied": "Geheim naar klembord gekopieerd", - "apiKeyNameRequired": "Sleutelnaam is vereist", - "apiKeyCreated": "API sleutel \"{{name}}\" gemaakt", - "apiKeyCreateFailed": "API-sleutel aanmaken mislukt", - "apiKeyUser": "Gebruiker", - "apiKeyExpires": "Verloopt", - "apiKeyRevoked": "API key \"{{name}}\" ingetrokken", - "apiKeyRevokeFailed": "API-sleutel intrekken is mislukt", - "passwordFieldsRequired": "Huidige en nieuwe wachtwoorden zijn vereist", - "passwordMismatch": "Wachtwoorden komen niet overeen", - "passwordTooShort": "Wachtwoord moet ten minste 6 tekens bevatten", - "passwordUpdated": "Wachtwoord succesvol bijgewerkt", - "passwordUpdateFailed": "Bijwerken van wachtwoord mislukt", - "deletePasswordRequired": "Wachtwoord is vereist om je account te verwijderen", - "deleteFailed": "Kan account niet verwijderen", - "deleting": "Verwijderenchar@@0", - "colorPickerTooltip": "Kleurenkiezer openen", - "themeSystem": "Systeem", - "themeLight": "Licht", - "themeDark": "Donker", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Geïntegreerd", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Één Donker", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/no_NO.json b/src/ui/locales/translated/no_NO.json index c9fdb358..9bc5af32 100644 --- a/src/ui/locales/translated/no_NO.json +++ b/src/ui/locales/translated/no_NO.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Mapper", - "folder": "Mappe", - "password": "Passord", - "key": "Nøkkel", - "sshPrivateKey": "SSH privatnøkkel", - "upload": "Last opp", - "keyPassword": "Nøkkelpassord", - "sshKey": "SSH-nøkkel", - "uploadPrivateKeyFile": "Last opp privat nøkkelfil", - "searchCredentials": "Søk i legitimasjon...", - "addCredential": "Legg til legitimasjon", - "caCertificate": "CA-sertifikat (-cert.pub)", - "caCertificateDescription": "Valgfritt: Last opp eller lim inn CA-signert sertifikatfil (f.eks id_ed25519-cert.pub). Kreves når SSH-serveren bruker sertifikatbasert autorisasjon.", - "uploadCertFile": "Last opp -cert.pub fil", - "clearCert": "Tøm", - "certLoaded": "Sertifikat lastet", - "certPublicKeyLabel": "CA sertifikat", - "certTypeLabel": "Sertifikat type", - "pasteOrUploadCert": "Lim inn eller last opp et -cert.pub sertifikat...", - "hasCaCert": "Har CA-sertifikat", - "noCaCert": "Ingen CA-sertifikat" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Kunne ikke laste varsler", - "failedToDismissAlert": "Kunne ikke avvise varsel" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Serverkonfigurasjon", - "description": "Konfigurer Termix-serverens URL for å koble til backend-tjenestene dine", - "serverUrl": "Server-URL", - "enterServerUrl": "Angi en server-URL", - "saveFailed": "Kunne ikke lagre konfigurasjonen", - "saveError": "Feil ved lagring av konfigurasjon", - "saving": "Lagrer...", - "saveConfig": "Lagre konfigurasjon", - "helpText": "Angi URL-en der Termix-serveren din kjører (f.eks. http://localhost:30001 eller https://din-server.com)", - "changeServer": "Bytt server", - "mustIncludeProtocol": "Server-URL må starte med http:// eller https://", - "allowInvalidCertificate": "Tillat ugyldig sertifikat", - "allowInvalidCertificateDesc": "Bruk kun for klarerte, selvhostede servere med selvsignerte sertifikater eller IP-adressesertifikater.", - "useEmbedded": "Bruk lokal server", - "embeddedDesc": "Kjør Termix med den innebygde lokale serveren (ingen ekstern server nødvendig)", - "embeddedConnecting": "Kobler til lokal server...", - "embeddedNotReady": "Lokal server er ikke klar ennå. Vennligst vent et øyeblikk og prøv igjen.", - "localServer": "Lokal Server" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Feil ved versjonssjekk", - "checkFailed": "Kunne ikke se etter oppdateringer", - "upToDate": "Appen er oppdatert", - "currentVersion": "Du kjører versjon {{version}}", - "updateAvailable": "Oppdatering tilgjengelig", - "newVersionAvailable": "En ny versjon er tilgjengelig! Du kjører {{current}}, men {{latest}} er tilgjengelig.", - "betaVersion": "Beta versjon", - "betaVersionDesc": "Du kjører {{current}}, som er nyere enn den nyeste stabile utgivelsen {{latest}}.", - "releasedOn": "Utgitt {{date}}", - "downloadUpdate": "Last ned oppdatering", - "checking": "Ser etter oppdateringer...", - "checkUpdates": "Se etter oppdateringer", - "checkingUpdates": "Ser etter oppdateringer...", - "updateRequired": "Oppdatering påkrevd" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Lukk", - "minimize": "Minimer", - "online": "Tilkoblet", - "offline": "Frakoblet", - "continue": "Fortsett", - "maintenance": "Vedlikehold", - "degraded": "Redusert", - "error": "Feil", - "warning": "Advarsel", - "unsavedChanges": "Ulagrede endringer", - "dismiss": "Avvis", - "loading": "Laster...", - "optional": "Valgfritt", - "connect": "Koble til", - "copied": "Kopiert", - "connecting": "Kobler til...", - "updateAvailable": "Oppdatering tilgjengelig", - "appName": "Termiks", - "openInNewTab": "Åpne i ny fane", - "noReleases": "Ingen utgivelser", - "updatesAndReleases": "Oppdateringer og utgivelser", - "newVersionAvailable": "En ny versjon ({{version}}) er tilgjengelig.", - "failedToFetchUpdateInfo": "Kunne ikke hente oppdateringsinformasjon", - "preRelease": "Forhåndsutgivelse", - "noReleasesFound": "Ingen utgivelser funnet.", - "cancel": "Avbryt", - "username": "Brukernavn", - "login": "Logg inn", - "register": "Registrer", - "password": "Passord", - "confirmPassword": "Bekreft passord", - "back": "Tilbake", - "save": "Lagre", - "saving": "Lagrer...", - "delete": "Slett", - "rename": "Omdøp", - "edit": "Rediger", - "add": "Legg til", - "confirm": "Bekreft", - "no": "Nei", - "or": "ELLER", - "next": "Neste", - "previous": "Forrige", - "refresh": "Oppdater", - "language": "Språk", - "checking": "Kontrollerer...", - "checkingDatabase": "Kontrollerer databaseforbindelse...", - "checkingAuthentication": "Kontrollerer autentisering...", - "backendReconnected": "Servertilkobling gjenopprettet", - "connectionDegraded": "Servertilkoblingen ble avbrutt, gjenopprettes…", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Fjern", - "create": "Opprett", - "update": "Oppdater", - "copy": "Kopier", - "copyFailed": "Kan ikke kopiere til utklippstavlen", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Gjenopprett", - "of": "av" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Hjem", + "home": "Home", "terminal": "Terminal", "docker": "Docker", - "tunnels": "Tunneler", - "fileManager": "Filbehandler", - "serverStats": "Serverstatistikk", - "admin": "Administrator", - "userProfile": "Brukerprofil", - "splitScreen": "Delt skjerm", - "confirmClose": "Lukk denne aktive økten?", - "close": "Lukk", - "cancel": "Avbryt", - "sshManager": "SSH-administrator", - "cannotSplitTab": "Kan ikke dele denne fanen", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopier passordet", - "copySudoPassword": "Kopier Sudo passord", - "passwordCopied": "Passordet er kopiert til utklippstavlen", - "noPasswordAvailable": "Ingen passord tilgjengelig", - "failedToCopyPassword": "Kopiering av passord feilet", - "refreshTab": "Oppdater forbindelse", - "openFileManager": "Åpne filbehandler", - "dashboard": "Kontrollpanel", - "networkGraph": "Nettverksgraf", - "quickConnect": "Kjapp tilkobling", - "sshTools": "SSH verktøy", - "history": "Historikk", - "hosts": "Verter", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", "snippets": "Snippets", - "hostManager": "Vertadministrator", - "credentials": "Legitimasjon", - "connections": "Tilkoblinger", - "roleAdministrator": "Administratorsiden", - "roleUser": "Bruker" + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Verter", - "noHosts": "Ingen SSH-verter", - "retry": "Prøv igjen", - "refresh": "Oppdater", - "optional": "Valgfritt", - "downloadSample": "Last ned eksempel", - "failedToDeleteHost": "{{name}} kunne ikke slettes", - "importSkipExisting": "Importer (hopp over eksisterende)", - "connectionDetails": "Tilkoblingsdetaljer", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Eksternt skrivebord", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Brukernavn", - "folder": "Mappe", - "tags": "Tagger", - "pin": "Fest", - "addHost": "Legg til vert", - "editHost": "Rediger vert", - "cloneHost": "Klon vert", - "enableTerminal": "Aktiver terminal", - "enableTunnel": "Aktiver tunnel", - "enableFileManager": "Aktiver filbehandler", - "enableDocker": "Aktiver Docker", - "defaultPath": "Standardsti", - "connection": "Tilkobling", - "upload": "Last opp", - "authentication": "Autentisering", - "password": "Passord", - "key": "Nøkkel", - "credential": "Legitimasjon", - "none": "Ingen", - "sshPrivateKey": "SSH-privatnøkkel", - "keyType": "Nøkkeltype", - "uploadFile": "Last opp fil", - "tabGeneral": "Generelt", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunneler", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Filer", - "tabStats": "Statistikk", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Deling", - "tabAuthentication": "Autentisering", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Tunnelen", - "fileManager": "Filbehandler", - "serverStats": "Serverstatistikk", - "status": "Status:", - "folderRenamed": "Mappen \"{{oldName}}\" ble endret til \"{{newName}}\"", - "failedToRenameFolder": "Kunne ikke gi mappen nytt navn", - "movedToFolder": "Flyttet til \"{{folder}}\"", - "editHostTooltip": "Redigere vert", - "statusChecks": "Statussjekk", - "metricsCollection": "Beregninger samling", - "metricsInterval": "Intervall for statistikkinnsamling", - "metricsIntervalDesc": "Hvor ofte serverstatistikk skal innhentes (5s - 1t)", - "behavior": "Oppførsel", - "themePreview": "Temavisning", - "theme": "Tema", - "fontFamily": "Skrifttype", - "fontSize": "Skriftstørrelse", - "letterSpacing": "Bokstavavstand", - "lineHeight": "Linjehøyde", - "cursorStyle": "Markørstil", - "cursorBlink": "Blinkende markør", - "scrollbackBuffer": "Tilbakerullebuffer", - "bellStyle": "Varselstil", - "rightClickSelectsWord": "Høyreklikk velger ord", - "fastScrollModifier": "Modifikator for rask rulling", - "fastScrollSensitivity": "Følsomhet for rask rulling", - "sshAgentForwarding": "SSH-agentvideresending", - "backspaceMode": "Backspace-modus", - "startupSnippet": "Oppstarts-snippet", - "selectSnippet": "Velg snippet", - "forceKeyboardInteractive": "Tving tastaturinteraktiv", - "overrideCredentialUsername": "Overstyr brukernavn fra legitimasjon", - "overrideCredentialUsernameDesc": "Bruk brukernavnet som er angitt ovenfor i stedet for legitimasjonsbevisets brukernavn", - "jumpHostChain": "Hoppvertkjede", - "portKnocking": "Port knocking", - "addKnock": "Legge til port", - "addProxyNode": "Legg til Node", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", "proxyNode": "Proxy Node", - "proxyType": "Proxy type", - "quickActions": "Hurtighandlinger", - "sudoPasswordAutoFill": "Sudo passord autoutfyll", - "sudoPassword": "Sudo passord", - "keepaliveInterval": "Keepalive intervall (ms)", - "moshCommand": "MOSH kommando", - "environmentVariables": "Miljøvariabler", - "addVariable": "Legg til variabel", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Kopier URL til terminal", - "copyFileManagerUrl": "Kopier Filbehandling URL", - "copyRemoteDesktopUrl": "Kopier URL til eksternt skrivebord", - "failedToConnect": "Kan ikke koble til konsollen", - "connect": "Nettverk", - "disconnect": "Frakoblet", - "start": "Begynn", - "enableStatusCheck": "Aktiver statussjekk", - "enableMetrics": "Aktiver måltall", - "bulkUpdateFailed": "Masseoppdatering mislyktes", - "selectAll": "Velg alle", - "deselectAll": "Opphev merking", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Sikker Shell", - "virtualNetwork": "Virtuelt nettverk", - "unencryptedShell": "Ukryptert skall", - "addressIp": "Adresse / IP", - "friendlyName": "Vennlig navn", - "folderAndAdvanced": "Mappe og avansert", - "privateNotes": "Private Notater", - "privateNotesPlaceholder": "Detaljer om denne serveren...", - "pinToTop": "Fest øverst", - "pinToTopDesc": "Vis alltid denne verten øverst i listen", - "portKnockingSequence": "Port Knocking sekvens", - "addKnockBtn": "Legg til knip", - "noPortKnocking": "Ingen port knocking konfigurert.", - "knockPort": "Enkelt port", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Forsinkelse etter (ms)", - "useSocks5Proxy": "Bruk SOCKS5 mellomtjener", - "useSocks5ProxyDesc": "Vei-tilkobling gjennom en mellomtjener", - "proxyHost": "Vert for mellomtjener", - "proxyPort": "Port for mellomtjener", - "proxyUsername": "Brukernavn for mellomtjener", - "proxyPassword": "Passord for mellomtjener", - "proxySingleMode": "Enkel proxy", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", "proxyChainMode": "Proxy Chain", - "you": "Du", - "jumpHostChainLabel": "Hopp over vert kjede", - "addJumpBtn": "Legg til Hopp", - "noJumpHosts": "Ingen hopp-verter konfigurert.", - "selectAServer": "Velg en server...", - "sshPort": "SSH port", - "authMethod": "Auth metode", - "storedCredential": "Lagret legitimasjon", - "selectACredential": "Velg en legitimasjon...", - "keyTypeLabel": "Type nøkkel", - "keyTypeAuto": "Automatisk gjenkjenning", - "keyPasteTab": "Lim", - "keyUploadTab": "Last opp", - "keyFileLoaded": "Nøkkelfil lastet", - "keyUploadClick": "Klikk for å laste opp .pem / .key / .ppk", - "clearKey": "Fjern nøkkel", - "keySaved": "SSH-nøkkelen er lagret", - "keyReplaceNotice": "lim inn en ny nøkkel nedenfor for å erstatte den", - "keyPassphraseSaved": "Passfrase lagret, type for å endre", - "replaceKey": "Erstatt nøkkel", - "forceKeyboardInteractiveLabel": "Tving tastatur interaktiv", - "forceKeyboardInteractiveShortDesc": "Tving manuell passord-oppføring selv om nøkler er tilstede", - "terminalAppearance": "Terminal utseende", - "colorTheme": "Farge Tema", - "fontFamilyLabel": "Skriftfamilie", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Markør stil", - "letterSpacingPx": "Avstand mellom brev (px)", - "lineHeightLabel": "Linje høyde", - "bellStyleLabel": "Bell stil", - "backspaceModeLabel": "Bakovertast modus", - "cursorBlinking": "Markør blink", - "cursorBlinkingDesc": "Aktiver blinking av animasjon for terminalmarkøren", - "rightClickSelectsWordLabel": "Høyreklikk Velger ord", - "rightClickSelectsWordShortDesc": "Velg ordet under markøren til høyreklikk", - "behaviorAndAdvanced": "Oppførsel & Avansert", - "scrollbackBufferLabel": "Rullback-buffer", - "scrollbackMaxLines": "Maksimalt antall linjer i historikk", - "sshAgentForwardingLabel": "SSH Agent videresending", - "sshAgentForwardingShortDesc": "Send dine lokale SSH-nøkler til denne verten", - "enableAutoMosh": "Aktiver automatisk Mosh", - "enableAutoMoshDesc": "Foretrekk Mosh over SSH hvis tilgjengelig", - "enableAutoTmux": "Aktiver Auto-Tmux", - "enableAutoTmuxDesc": "Automatisk start eller legg ved tmux-sesjon", - "sudoPasswordAutoFillLabel": "Sudo passord auto-utfylling", - "sudoPasswordAutoFillShortDesc": "Oppgi automatisk superpassord når du blir spurt", - "sudoPasswordLabel": "Sudo passord", - "environmentVariablesLabel": "Miljøvariabler", - "addVariableBtn": "Legg til variabel", - "noEnvVars": "Ingen miljøvariabler konfigurert.", - "fastScrollModifierLabel": "Rask rulleendring", - "fastScrollSensitivityLabel": "Rask rullefølsomhet", - "moshCommandLabel": "Mosh kommando", - "startupSnippetLabel": "Oppstart tekstutdrag", - "keepaliveIntervalLabel": "Keepalive-intervall (sekunder)", - "maxKeepaliveMisses": "Maksimum antall tap av Keepi", - "tunnelSettings": "Innstillinger for tunnel", - "enableTunneling": "Aktiver tunellering", - "enableTunnelingDesc": "Aktiver SSH tunnel funksjonalitet for denne verten", - "serverTunnelsSection": "Server Tunneler", - "addTunnelBtn": "Legg til tunnel", - "noTunnelsConfigured": "Ingen tunneler konfigurert.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Type tunnel", - "tunnelModeLocalDesc": "Videresend en lokal port til en port på den eksterne serveren (eller en vert som kan nås fra den).", - "tunnelModeRemoteDesc": "Videresend en port på den eksterne serveren tilbake til en lokal port på maskinen.", - "tunnelModeDynamicDesc": "Opprett en SOCKS5-proxy på en lokal havn for dynamisk portvideresending.", - "sameHost": "Denne verten (direkte tunnel)", - "endpointHost": "Endepunkt vert", - "endpointPort": "Endepunktport", - "bindHost": "Bind vert", - "sourcePort": "Kildeport", - "maxRetries": "Maks forsøk", - "retryIntervalS": "Prøv igjen intervall(er)", - "autoStartLabel": "Autostart", - "autoStartDesc": "Koble automatisk denne tunnelen når verten er lastet inn", - "tunnelConnecting": "Tunnelen tilkobler...", - "tunnelDisconnected": "Tunnelen er frakoblet", - "failedToConnectTunnel": "Kan ikke koble til", - "failedToDisconnectTunnel": "Kan ikke koble fra", - "dockerIntegration": "Docker integrasjon", - "enableDockerMonitor": "Aktiver Docker", - "enableDockerMonitorDesc": "Overvåk og administrer beholdere på denne verten via Docker", - "enableFileManagerMonitor": "Aktiver filbehandler", - "enableFileManagerMonitorDesc": "Bla gjennom og behandle filer på denne verten over SFTP", - "defaultPathLabel": "Standard sti", - "fileManagerPathHint": "Mappen som skal åpnes når filbehandleren starter for denne verten.", - "statusChecksLabel": "Statussjekk", - "enableStatusChecks": "Aktiver statuskontroll", - "enableStatusChecksDesc": "Periodisk ping av denne verten for å bekrefte tilgjengelighet", - "useGlobalInterval": "Bruk globalt intervall", - "useGlobalIntervalDesc": "Overstyr med statuskontrollintervallet for hele serveren", - "checkIntervalS": "Sjekk intervall (s)", - "checkIntervalDesc": "Sekunder mellom hver tilkobling ping", - "metricsCollectionLabel": "Beregninger samling", - "enableMetricsLabel": "Aktiver måltall", - "enableMetricsDesc": "Samle CPU, RAM, disk og nettverksbruk fra verten", - "useGlobalMetrics": "Bruk globalt intervall", - "useGlobalMetricsDesc": "Overstyr med intervallet for hele serveren", - "metricsIntervalS": "Metrics Intervall (s)", - "metricsIntervalDesc2": "Sekunder mellom måltallsbilder", - "visibleWidgets": "Synlige widgets", - "cpuUsageLabel": "Prosessorbruk", - "cpuUsageDesc": "CPU-prosent, belastet gjennomsnitt, sjakkdiagram", - "memoryLabel": "Minne brukt", - "memoryDesc": "RAM-bruk, swap, bufret", - "storageLabel": "Bruk av diskplass", - "storageDesc": "Disk bruk pr. monteringspunkt", - "networkLabel": "Nettverksgrensesnitt", - "networkDesc": "Liste over grensesnitt og båndbredde", - "uptimeLabel": "Oppetid", - "uptimeDesc": "System oppetid og oppstartstid", - "systemInfoLabel": "System informasjon", - "systemInfoDesc": "OS, kernel, vertsnavn, arkitektur", - "recentLoginsLabel": "Nylige innlogginger", - "recentLoginsDesc": "Vellykket og mislykkede innloggingshendelser", - "topProcessesLabel": "Flest prosesser", - "topProcessesDesc": "PID, CPU%, MEM%, kommando", - "listeningPortsLabel": "Lytter havner", - "listeningPortsDesc": "Åpne porter med prosess og tilstand", - "firewallLabel": "Brannmur", - "firewallDesc": "Flammevei, AppArmor, SELinux-status", - "quickActionsLabel": "Hurtig valg", - "quickActionsToolbar": "Hurtighandlinger vises som knapper i Serverstatistikkverktøylinjen for kjøring av kommando med ett klikk.", - "noQuickActions": "Ingen raske handlinger ennå.", - "buttonLabel": "Etikett for knapp", - "selectSnippetPlaceholder": "Velg snippet...", - "addActionBtn": "Legg til handling", - "hostSharedSuccessfully": "Vert delt vellykket", - "failedToShareHost": "Kunne ikke dele verten", - "accessRevoked": "Tilgang tilbakekalt", - "failedToRevokeAccess": "Kunne ikke oppheve tilgangen", - "cancelBtn": "Avbryt", - "savingBtn": "Lagrer...", - "addHostBtn": "Legg til vert", - "hostUpdated": "Vert oppdatert", - "hostCreated": "Vert opprettet", - "failedToSave": "Klarte ikke å lagre verten", - "credentialUpdated": "Påloggingsrettigheter oppdatert", - "credentialCreated": "Påloggingsrettigheter opprettet", - "failedToSaveCredential": "Kan ikke lagre legitimasjon", - "backToHosts": "Tilbake til verter", - "backToCredentials": "Tilbake til brukeropplysninger", - "pinned": "Festet", - "noHostsFound": "Ingen verter funnet", - "tryDifferentTerm": "Prøv et annet begrep", - "addFirstHost": "Legge til din første vert for å komme i gang", - "noCredentialsFound": "Ingen legitimasjon funnet", - "addCredentialBtn": "Legg til legitimasjon", - "updateCredentialBtn": "Oppdater legitimasjon", - "features": "Funksjoner", - "noFolder": "(Ingen mappe)", - "deleteSelected": "Slett", - "exitSelection": "Avslutt valg", - "importSkip": "Importer (hopp over eksisterende)", - "importOverwrite": "Importer (overskrive)", - "collapseBtn": "Skjul", - "importExportBtn": "Import / Eksport", - "hostStatusesRefreshed": "Vertsstatuser oppdatert", - "failedToRefreshHosts": "Mislyktes i å oppdatere verter", - "movedHostTo": "Flyttet {{host}} til \"{{folder}}\"", - "failedToMoveHost": "Kunne ikke flytte verten", - "folderRenamedTo": "Mappen har endret navn til \"{{name}}\"", - "deletedFolder": "Slettet mappen \"{{name}}\"", - "failedToDeleteFolder": "Kunne ikke slette mappe", - "deleteAllInFolder": "Slett alle verter i \"{{name}}\"? Kan ikke angres.", - "deletedHost": "Slettet {{name}}", - "copiedToClipboard": "Kopiert til utklippstavle", - "terminalUrlCopied": "Terminal URL kopiert", - "fileManagerUrlCopied": "Filbehandling URL kopiert", - "tunnelUrlCopied": "Tunnel URL kopiert", - "dockerUrlCopied": "Docker URL kopiert", - "serverStatsUrlCopied": "Server statistiske URL kopiert", - "rdpUrlCopied": "RDP URL kopiert", - "vncUrlCopied": "VNC URL kopiert", - "telnetUrlCopied": "Telnet URL kopiert", - "remoteDesktopUrlCopied": "URL for eksternt skrivebord", - "expandActions": "Utvid handlinger", - "collapseActions": "Skjul handlinger", - "wakeOnLanAction": "Vekk på LAN", - "wakeOnLanSuccess": "Magisk pakke sendt til {{name}}", - "wakeOnLanError": "Kunne ikke sende magisk pakke", - "cloneHostAction": "Klon vert", - "copyAddress": "Kopier adresse", - "copyLink": "Kopier link", - "copyTerminalUrlAction": "Kopier URL til terminal", - "copyFileManagerUrlAction": "Kopier Filbehandling URL", - "copyTunnelUrlAction": "Kopier tunnel URL", - "copyDockerUrlAction": "Kopier Docker URL", - "copyServerStatsUrlAction": "Kopier Server Statistikk URL", - "copyRdpUrlAction": "Kopier RDP-URL", - "copyVncUrlAction": "Kopier VNC-URL", - "copyTelnetUrlAction": "Kopier Telnet URL", - "copyRemoteDesktopUrlAction": "Kopier URL til eksternt skrivebord", - "deleteCredentialConfirm": "Slett legitimasjon \"{{name}}\"?", - "deletedCredential": "Slettet {{name}}", - "deploySSHKeyTitle": "Installer SSH-nøkkel", - "deployingBtn": "Distribuerer...", - "deployBtn": "Installer", - "failedToDeployKey": "Kunne ikke distribuere nøkkel", - "deleteHostsConfirm": "Slette {{count}} vert{{plural}}? Dette kan ikke angres.", - "movedToRoot": "Flyttet til rot", - "failedToMoveHosts": "Kunne ikke flytte verter", - "enableTerminalFeature": "Aktiver terminal", - "disableTerminalFeature": "Deaktiver terminal", - "enableFilesFeature": "Aktiver filer", - "disableFilesFeature": "Deaktiver filer", - "enableTunnelsFeature": "Aktiver Tunneler", - "disableTunnelsFeature": "Deaktivere Tunneler", - "enableDockerFeature": "Aktiver Docker", - "disableDockerFeature": "Deaktiver Docker", - "addTagsPlaceholder": "Legg til tagger...", - "authDetails": "Autentisering detaljer", - "credType": "Type:", - "generateKeyPairDesc": "Generer et nytt nøkkelpar vil både private og offentlige nøkler bli fylt automatisk.", - "generatingKey": "Genererer...", - "generateLabel": "Generer {{label}}", - "uploadFileBtn": "Last opp fil", - "keyPassphraseOptional": "Passord frase (valgfritt)", - "sshPublicKeyOptional": "SSH offentlig nøkkel (Valgfritt)", - "publicKeyGenerated": "Offentlig nøkkel generert", - "failedToGeneratePublicKey": "Kunne ikke utlede offentlig nøkkel", - "publicKeyCopied": "Offentlig nøkkel kopiert", - "keyPairGenerated": "{{label}} nøkkelpar generert", - "failedToGenerateKeyPair": "Kunne ikke generere nøkkelpar", - "searchHostsPlaceholder": "Søk verter, adresser, tagger…", - "searchCredentialsPlaceholder": "Søk etter legitimasjon…", - "refreshBtn": "Oppdater", - "addTag": "Legg til tagger...", - "deleteConfirmBtn": "Slett", - "tunnelRequirementsText": "SSH-serveren må ha GatewayPorts ja, AllowTcpForwarding ja, and PermitRootLogin yes set in /etc/ssh/sshd_config.", - "deleteHostConfirm": "Slett \"{{name}}\"?", - "enableAtLeastOneProtocol": "Aktiver minst én protokoll over for å konfigurere autentisering og tilkoblingsinnstillinger.", - "keyPassphrase": "Passord", - "connectBtn": "Nettverk", - "disconnectBtn": "Frakoblet", - "basicInformation": "Grunnleggende informasjon", - "authDetailsSection": "Autentisering detaljer", - "credTypeLabel": "Type:", - "hostsTab": "Verter", - "credentialsTab": "Legitimasjon", - "selectMultiple": "Velg flere", - "selectHosts": "Velg verter", - "connectionLabel": "Tilkobling", - "authenticationLabel": "Autentisering", - "generateKeyPairTitle": "Generer nøkkelpar", - "generateKeyPairDescription": "Generer et nytt nøkkelpar vil både private og offentlige nøkler bli fylt automatisk.", - "generateFromPrivateKey": "Generer fra privat nøkkel", - "refreshBtn2": "Oppdater", - "exitSelectionTitle": "Avslutt valg", - "exportAll": "Eksporter alle", - "addHostBtn2": "Legg til vert", - "addCredentialBtn2": "Legg til legitimasjon", - "checkingHostStatuses": "Kontrollerer vertstatuser...", - "pinnedSection": "Festet", - "hostsExported": "Vellykket eksport av verter", - "exportFailed": "Kunne ikke eksportere verter", - "sampleDownloaded": "Eksemplefil lastet ned", - "failedToDeleteCredential2": "Kunne ikke slette legitimasjon", - "noFolderOption": "(Ingen mappe)", - "nSelected": "{{count}} valgt", - "featuresMenu": "Funksjoner", - "moveMenu": "Flytt", - "cancelSelection": "Avbryt", - "deployDialogDesc": "Distribuere {{name}} til en verts autorisert_keys.", - "targetHostLabel": "Mål vert", - "selectHostOption": "Velg en vert...", - "keyDeployedSuccess": "Nøkkelen ble distribuert vellykket", - "failedToDeployKey2": "Kunne ikke distribuere nøkkel", - "deletedCount": "Slettet {{count}} verter", - "failedToDeleteCount": "Kan ikke slette {{count}} verter", - "duplicatedHost": "Duplisert \"{{name}}\"", - "failedToDuplicateHost": "Kunne ikke duplisere verten", - "updatedCount": "Oppdaterte {{count}} verter", - "friendlyNameLabel": "Vennlig navn", - "descriptionLabel": "Beskrivelse", - "loadingHost": "Laster vert...", - "loadingHosts": "Laster verter...", - "loadingCredentials": "Laster inn legitimasjonsbeskrivelser...", - "noHostsYet": "Ingen verter ennå", - "noHostsMatchSearch": "Ingen verter samsvarer med søket ditt", - "hostNotFound": "Verten ikke funnet", - "searchHosts": "Søk verter...", - "sortHosts": "Sorter verter", - "sortDefault": "Standardordre", - "sortNameAsc": "Navn (A → Å)", - "sortNameDesc": "Navn (Å → A)", - "sortIpAsc": "IP-adresse (stigende)", - "sortIpDesc": "IP-adresse (beskrivelse)", - "sortOnlineFirst": "På nett først", - "sortOfflineFirst": "Frakoblet først", - "sortPinnedFirst": "Festet først", - "filterHosts": "Filtrer verter", - "filterClearAll": "Fjern filtre", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "På nett", - "filterOffline": "Frakoblet", - "filterPinned": "Festet", - "filterAuthGroup": "Autorisasjonstype", - "filterAuthPassword": "Passord", - "filterAuthKey": "SSH-nøkkel", - "filterAuthCredential": "Legitimasjon", - "filterAuthNone": "Ingen", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokoll", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Funksjoner", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Filbehandler", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", - "filterFeatureDocker": "Dokker", - "filterTagsGroup": "Tagger", - "shareHost": "Del verten", - "shareHostTitle": "Del: {{name}}", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Denne verten må bruke en legitimasjon for å kunne dele. Rediger verten og tildel en legitimasjon først." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Tilkobling", - "authentication": "Autentisering", - "connectionSettings": "Tilkobling innstillinger", - "displaySettings": "Vis innstillinger", - "audioSettings": "Lydinnstillinger for lyd", - "rdpPerformance": "RDP Ytelse", - "deviceRedirection": "Omadressering av enhet", - "session": "Økt", - "gateway": "Inngang", - "remoteApp": "Fjernapp", - "clipboard": "Utklippstavle", - "sessionRecording": "Økt opptak", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC Innstillinger", - "terminalSettings": "Terminale innstillinger", - "rdpPort": "RDP-port", - "username": "Brukernavn", - "password": "Passord", - "domain": "Domene", - "securityMode": "Sikkerhet Modus", - "colorDepth": "Farge Dybde", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Høyde", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Endre størrelsen", - "clientName": "Klientens navn", - "initialProgram": "Første program", - "serverLayout": "Server layout", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Port for gateway", - "gatewayUsername": "Gateway brukernavn", - "gatewayPassword": "Gateway passord", - "gatewayDomain": "gateway Domene", - "remoteAppProgram": "RemoteApp program", - "workingDirectory": "Arbeidskatalog (arbeid)", - "arguments": "Argumenter", - "normalizeLineEndings": "Normaliser linjeavslutninger", - "recordingPath": "Opptaks sti", - "recordingName": "Navn på opptak", - "macAddress": "MAC Adresse", - "broadcastAddress": "Kringkastings Adresse", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Ventetid (s)", - "driveName": "Stasjon navn", - "drivePath": "Sti disk", - "ignoreCertificate": "Ignorer Sertifikat", - "ignoreCertificateDesc": "Tillat oppkobling mot verter med selvsignerte sertifikater", - "forceLossless": "Tving tapsløs", - "forceLosslessDesc": "Tving tap av bildekoding (høyere kvalitet, mer båndbredde)", - "disableAudio": "Deaktivere lyd", - "disableAudioDesc": "Demp all lyd fra fjernøkten", - "enableAudioInput": "Aktiver lydinndata (mikrotelefon)", - "enableAudioInputDesc": "Videresend lokal mikrofon til fjernøkten", - "wallpaper": "Bakgrunnsbilde", - "wallpaperDesc": "Vis skrivebordsbakgrunnsbilde (deaktiverer bedre ytelse)", - "theming": "Utseende", - "themingDesc": "Aktiver visuelle temaer og stiler", - "fontSmoothing": "Skrift Utjevning", - "fontSmoothingDesc": "Aktiver RenType skrifttype", - "fullWindowDrag": "Dra hele vinduet", - "fullWindowDragDesc": "Vis vindu innholdet ved flytting", - "desktopComposition": "Skrivebord komposisjon", - "desktopCompositionDesc": "Aktiver Aero glass effekter", - "menuAnimations": "Meny Animasjoner", - "menuAnimationsDesc": "Aktiver meny fade og slide animations", - "disableBitmapCaching": "Deaktivere Bitmapcaching", - "disableBitmapCachingDesc": "Slå av bitmapcache (kan hjelpe med glitcher)", - "disableOffscreenCaching": "Deaktiver mellomlager i avslått skjerm", - "disableOffscreenCachingDesc": "Slå av offscreen cache", - "disableGlyphCaching": "Deaktiver Glyph Caching", - "disableGlyphCachingDesc": "Slå av symbol-cache", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Bruke RemoteFX grafikkrørledning", - "enablePrinting": "Aktiver utskrift", - "enablePrintingDesc": "Omdirigere lokale skrivere til fjernsesjonen", - "enableDriveRedirection": "Aktiver omdirigering av stasjon", - "enableDriveRedirectionDesc": "Tilordne en lokal mappe som en stasjon i fjernøkten", - "createDrivePath": "Opprett stasjon sti", - "createDrivePathDesc": "Opprett mappen automatisk hvis den ikke finnes", - "disableDownload": "Deaktiver nedlasting", - "disableDownloadDesc": "Hindre nedlasting av filer fra den eksterne økten", - "disableUpload": "Deaktiver opplasting", - "disableUploadDesc": "Hindre opplasting av filer til den eksterne økten", - "enableTouch": "Aktiver Touch", - "enableTouchDesc": "Aktiver videresending av berøringsdata", - "consoleSession": "Konsoll Økten", - "consoleSessionDesc": "Koble til konsollen (økt 0) i stedet for en ny sesjon", - "sendWolPacket": "Send WOL-pakke", - "sendWolPacketDesc": "Send en magisk pakke for å vekke denne verten før du kobler til", - "disableCopy": "Deaktiver kopi", - "disableCopyDesc": "Hindre kopiering av tekst fra den eksterne økten", - "disablePaste": "Deaktiver lim", - "disablePasteDesc": "Forhindre lim inn tekst i den eksterne økten", - "createPathIfMissing": "Opprett sti hvis mangler", - "createPathIfMissingDesc": "Opprett filmappe automatisk", - "excludeOutput": "Ekskluder utdata", - "excludeOutputDesc": "Ikke ta opp skjermdata (bare metadata)", - "excludeMouse": "Ekskluder musen", - "excludeMouseDesc": "Ikke ta opp musebevegelser", - "includeKeystrokes": "Inkluder tastetrykk", - "includeKeystrokesDesc": "Spill inn rå-tastetrykk i tillegg til skjermutgangen", - "vncPort": "VNC port", - "vncPassword": "VNC passord", - "vncUsernameOptional": "Brukernavn (valgfritt)", - "vncLeaveBlank": "La være blank hvis ikke nødvendig", - "cursorMode": "Markør modus", - "swapRedBlue": "Bytte Rød/blå", - "swapRedBlueDesc": "Bytt mellom røde og blå fargekanaler (fikser noen fargeremidler)", - "readOnly": "Skrivebeskyttet", - "readOnlyDesc": "Vis den eksterne skjermen uten å sende inndata", - "telnetPort": "Telnet port", - "terminalType": "Type terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Farge Tema", - "backspaceKey": "Bakovertast", - "saveHostFirst": "Lagre verten først.", - "sharingOptionsAfterSave": "Delingsalternativer er tilgjengelige etter at verten har blitt lagret.", - "sharingLoadError": "Kan ikke laste delingsdata. Kontroller tilkoblingen og prøv igjen.", - "shareHostSection": "Del verten", - "shareWithUser": "Del med bruker", - "shareWithRole": "Del med rolle", - "selectUser": "Velg bruker", - "selectRole": "Velg rolle", - "selectUserOption": "Velg en bruker...", - "selectRoleOption": "Velg en rolle...", - "permissionLevel": "Tillatelse nivå", - "expiresInHours": "Utløper om (timer)", - "noExpiryPlaceholder": "La være tomt for utløpsdato", - "shareBtn": "Del", - "currentAccess": "Gjeldende tilgang", - "typeHeader": "Type:", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Tillatelse", - "grantedByHeader": "Tildelt av", - "expiresHeader": "Utløper", - "noAccessEntries": "Ingen tilgangsoppføringer enda.", - "expiredLabel": "Utløpt", - "neverLabel": "Aldri", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Avbryt", - "savingBtn": "Lagrer...", - "updateHostBtn": "Oppdater vert", - "addHostBtn": "Legg til vert" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Søk verter, kommandoer eller innstillinger...", - "quickActions": "Hurtig valg", - "hostManager": "Vertshåndtering", - "hostManagerDesc": "Håndtere, legge til eller redigere verter", - "addNewHost": "Legg til ny vert", - "addNewHostDesc": "Registrer en ny vert", - "adminSettings": "Admininnstillinger", - "adminSettingsDesc": "Konfigurer system innstillinger og brukere", - "userProfile": "Brukerprofil", - "userProfileDesc": "Administrer din konto og innstillinger", - "addCredential": "Legg til legitimasjon", - "addCredentialDesc": "Lagre SSH-nøkler eller passord", - "recentActivity": "Nylig aktivitet", - "serversAndHosts": "Servere og verter", - "noHostsFound": "Ingen verter funnet som samsvarer med \"{{search}}\"", - "links": "Lenker", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Velg", - "toggleWith": "Veksle med" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Ingen fane tildelt", - "focusedPane": "Aktiv rute" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Ingen tilkoblinger", - "noConnectionsDesc": "Åpne en terminal, filbehandler eller eksternt skrivebord for å se tilkoblinger her", - "connectedFor": "Tilkoblet for {{duration}}", - "connected": "Tilkoblet", - "disconnected": "Frakoblet", - "closeTab": "Lukk fanen", - "closeConnection": "Lukk forbindelse", - "forgetTab": "Glemme", - "removeBackground": "Fjerne", - "reconnect": "Koble til igjen", - "reopenTab": "Åpne igjen", - "sectionOpen": "Åpne", - "sectionBackground": "Bakgrunn", - "backgroundDesc": "Øktene varer i 30 minutter etter frakobling og kan kobles til på nytt.", - "persisted": "Fortsatt i bakgrunnen", - "expiresIn": "Utløper om {{duration}}", - "search": "Søk etter tilkoblinger...", - "noSearchResults": "Ingen forbindelser samsvarer med søket ditt" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Kobler til {{type}} økt...", - "connectionError": "Feil ved tilkobling", - "connectionFailed": "Tilkobling mislyktes", - "failedToConnect": "Kan ikke hente tilkoblingstoken", - "hostNotFound": "Verten ikke funnet", - "noHostSelected": "Ingen vert valgt", - "reconnect": "Gjenoppkoble", - "retry": "Prøv igjen", - "guacdUnavailable": "Ekstern stasjonstjeneste (guacd) er ikke tilgjengelig. Kontroller at guacd kjører og er tilgjengelig og konfigurert riktig i administratorinnstillinger.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Lås skjerm)", - "winKey": "Windows nøkkel", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Skift", - "win": "Vinn", - "stickyActive": "{{key}} (Avsluttet - Klikk for å slippe)", - "stickyInactive": "{{key}} (Klikk for å lade )", - "esc": "Rømme", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Hjem", - "end": "Slutt", - "pageUp": "Side opp", - "pageDown": "Side ned", - "arrowUp": "Pil opp", - "arrowDown": "Pil ned", - "arrowLeft": "Pil venstre", - "arrowRight": "Pil høyre", - "fnToggle": "Funksjonsnøkler", - "reconnect": "Koble fra økt", - "collapse": "Skjul verktøylinje", - "expand": "Utvid verktøylinje", - "dragHandle": "Dra for å flytte" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Koble til vert", - "clear": "Tøm", - "paste": "Lim inn", - "reconnect": "Koble til på nytt", - "connectionLost": "Tilkobling mistet", - "connected": "Tilkoblet", - "clipboardWriteFailed": "Kopiering til utklippstavlen. Sjekk at siden har tilgang til HTTPS eller localhost.", - "clipboardReadFailed": "Kan ikke lese fra utklippstavlen. Påse at utklippstavle tillatelser er gitt.", - "clipboardHttpWarning": "Lim inn krever HTTPS. Bruk Ctrl+Shift+V eller tjener termiks over HTTPS.", - "unknownError": "Ukjent feil oppstod", - "websocketError": "WebSocket-tilkoblingsfeil", - "connecting": "Kobler til...", - "noHostSelected": "Ingen vert valgt", - "reconnecting": "Kobler til på nytt... ({{attempt}}/{{max}})", - "reconnected": "Tilkoblet på nytt", - "tmuxSessionCreated": "opprettede tmux-økt: {{name}}", - "tmuxSessionAttached": "Tmux økt vedlagt: {{name}}", - "tmuxUnavailable": "tmux er ikke installert på den eksterne verten og faller tilbake til standard skall", - "tmuxSessionPickerTitle": "tmux økter", - "tmuxSessionPickerDesc": "Eksisterende tmux-økter funnet på denne verten. Velg en for å sende på nytt eller opprett en ny sesjon.", - "tmuxWindows": "Vinduer", - "tmuxWindowCount": "{{count}} vindu", - "tmuxAttached": "Vedlagte klienter", - "tmuxAttachedCount": "{{count}} vedlagt", - "tmuxLastActivity": "Siste aktivitet", - "tmuxTimeJustNow": "akkurat nå", - "tmuxTimeMinutes": "{{count}}m siden", - "tmuxTimeHours": "{{count}}t siden", - "tmuxTimeDays": "for {{count}}dager siden", - "tmuxCreateNew": "Start ny økt", - "tmuxCopyHint": "Juster utvelgelse og trykk på Enter for å kopiere fra utklippstavlen", - "tmuxDetach": "Frakoble fra en tmux-økt", - "tmuxDetached": "Frakoblet fra tmux-økten", - "maxReconnectAttemptsReached": "Maks antall tilkoblingsforsøk nådd", - "closeTab": "Lukk", - "connectionTimeout": "Tilkoblingstidsavbrudd", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", - "terminalWithPath": "Terminal – {{host}}:{{path}}", - "runTitle": "Kjører {{command}} - {{host}}", - "totpRequired": "Tofaktorautentisering kreves", - "totpCodeLabel": "Verifiseringskode", - "totpVerify": "Verifiser", - "warpgateAuthRequired": "Warpgate godkjenning kreves", - "warpgateSecurityKey": "Sikkerhetsnøkkel", - "warpgateAuthUrl": "Autentisering URL", - "warpgateOpenBrowser": "Åpne i nettleser", - "warpgateContinue": "Jeg har fullført godkjenning", - "opksshAuthRequired": "OPKSSH godkjenning kreves", - "opksshAuthDescription": "Fullstendig autentisering i nettleseren din for å fortsette. Denne økten er gyldig i 24 timer.", - "opksshOpenBrowser": "Åpne nettleseren for å godkjenne", - "opksshWaitingForAuth": "Venter på godkjenning i nettleseren...", - "opksshAuthenticating": "Behandler autentisering...", - "opksshTimeout": "Autentisering ble tidsavbrutt. Vennligst prøv igjen.", - "opksshAuthFailed": "Godkjenning mislyktes. Kontroller påloggingsinformasjonen, og prøv på nytt.", - "opksshSignInWith": "Logg inn med {{provider}}", - "sudoPasswordPopupTitle": "Sette inn passord?", - "websocketAbnormalClose": "Tilkobling lukket uventet. Dette kan skyldes en revers proxy eller SSL-konfigurasjonsfeil. Kontroller serverloggene.", - "connectionLogTitle": "Tilkobling logg", - "connectionLogCopy": "Kopier logger til utklippstavlen", - "connectionLogEmpty": "Ingen tilkoblingslogger ennå", - "connectionLogWaiting": "Venter på tilkoblingslogger...", - "connectionLogCopied": "Tilkoblingslogger kopiert til utklippstavlen", - "connectionLogCopyFailed": "Kopiering av loggene til utklippstavlen feilet", - "connectionRejected": "Tilkobling avvist av serveren. Kontroller godkjennings- og nettverkskonfigurasjonen.", - "hostKeyRejected": "SSH vertsnøkkelverifisering ble avvist. Tilkoblingen avbrutt.", - "sessionTakenOver": "Økten ble åpnet i en annen fane. Koble til..." + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Ingen vert valgt", - "initializingEditor": "Initialiserer redigeringsprogram...", - "file": "Fil", - "folder": "Mappe", - "uploadFile": "Last opp fil", - "downloadFile": "Last ned", - "extractArchive": "Pakk ut arkiv", - "extractingArchive": "Pakker ut {{name}}...", - "archiveExtractedSuccessfully": "{{name}} ble pakket ut", - "extractFailed": "Utpakking mislyktes", - "compressFile": "Komprimer fil", - "compressFiles": "Komprimer filer", - "compressFilesDesc": "Komprimer {{count}} elementer til ett arkiv", - "archiveName": "Arkivnavn", - "enterArchiveName": "Skriv inn arkivnavn...", - "compressionFormat": "Komprimeringsformat", - "selectedFiles": "Valgte filer", - "andMoreFiles": "og {{count}} til...", - "compress": "Komprimer", - "compressingFiles": "Komprimerer {{count}} elementer til {{name}}...", - "filesCompressedSuccessfully": "{{name}} ble opprettet", - "compressFailed": "Komprimering mislyktes", - "edit": "Rediger", - "preview": "Forhåndsvis", - "previous": "Forrige", - "next": "Neste", - "pageXOfY": "Side {{current}} av {{total}}", - "zoomOut": "Zoom ut", - "zoomIn": "Zoom inn", - "newFile": "Ny fil", - "newFolder": "Ny mappe", - "rename": "Gi nytt navn", - "uploading": "Laster opp...", - "uploadingFile": "Laster opp {{name}}...", - "fileName": "Filnavn", - "folderName": "Mappenavn", - "fileUploadedSuccessfully": "Fil \"{{name}}\" ble lastet opp", - "failedToUploadFile": "Kunne ikke laste opp fil", - "fileDownloadedSuccessfully": "Filen ble lastet ned", - "failedToDownloadFile": "Kunne ikke laste ned fil", - "fileCreatedSuccessfully": "Fil \"{{name}}\" ble opprettet", - "folderCreatedSuccessfully": "Mappe \"{{name}}\" ble opprettet", - "failedToCreateItem": "Kunne ikke opprette element", - "operationFailed": "{{operation}} mislyktes for {{name}}: {{error}}", - "failedToResolveSymlink": "Kunne ikke løse symbolske lenker", - "itemsDeletedSuccessfully": "{{count}} elementer ble slettet", - "failedToDeleteItems": "Kunne ikke slette elementer", - "sudoPasswordRequired": "Administrator passord kreves", - "enterSudoPassword": "Angi superpassord for å fortsette denne operasjonen", - "sudoPassword": "Sudo passord", - "sudoOperationFailed": "Sudo operasjonen mislyktes", - "sudoAuthFailed": "Sudo autentisering mislyktes", - "dragFilesToUpload": "Slipp filer her for å laste opp", - "emptyFolder": "Denne mappen er tom", - "searchFiles": "Søk i filer...", - "upload": "Last opp", - "selectHostToStart": "Velg en vert for å starte filhåndtering", - "sshRequiredForFileManager": "Filbehandler krever SSH. Denne tjeneren har ikke SSH aktivert.", - "failedToConnect": "Kunne ikke koble til SSH", - "failedToLoadDirectory": "Kunne ikke laste katalog", - "noSSHConnection": "Ingen SSH-tilkobling tilgjengelig", - "copy": "Kopier", - "cut": "Klipp ut", - "paste": "Lim inn", - "copyPath": "Kopier sti", - "copyPaths": "Kopier stier", - "delete": "Slett", - "properties": "Egenskaper", - "refresh": "Oppdater", - "downloadFiles": "Last ned {{count}} filer til nettleseren", - "copyFiles": "Kopier {{count}} elementer", - "cutFiles": "Klipp ut {{count}} elementer", - "deleteFiles": "Slett {{count}} elementer", - "filesCopiedToClipboard": "{{count}} elementer kopiert til utklippstavlen", - "filesCutToClipboard": "{{count}} elementer klippet til utklippstavlen", - "pathCopiedToClipboard": "Sti kopiert til utklippstavlen", - "pathsCopiedToClipboard": "{{count}} stier kopiert til utklippstavlen", - "failedToCopyPath": "Kunne ikke kopiere sti", - "movedItems": "Flyttet {{count}} elementer", - "failedToDeleteItem": "Kunne ikke slette element", - "itemRenamedSuccessfully": "{{type}} fikk nytt navn", - "failedToRenameItem": "Kunne ikke gi element nytt navn", - "download": "Last ned", - "permissions": "Tillatelser", - "size": "Størrelse", - "modified": "Endret", - "path": "Sti", - "confirmDelete": "Er du sikker på at du vil slette {{name}}?", - "permissionDenied": "Tillatelse nektet", - "serverError": "Serverfeil", - "fileSavedSuccessfully": "Filen ble lagret", - "failedToSaveFile": "Kunne ikke lagre fil", - "confirmDeleteSingleItem": "Er du sikker på at du vil slette \"{{name}}\" permanent?", - "confirmDeleteMultipleItems": "Er du sikker på at du vil slette {{count}} elementer permanent?", - "confirmDeleteMultipleItemsWithFolders": "Er du sikker på at du vil slette {{count}} elementer permanent? Dette inkluderer mapper og innholdet deres.", - "confirmDeleteFolder": "Er du sikker på at du vil slette mappen \"{{name}}\" og alt innholdet permanent?", - "permanentDeleteWarning": "Denne handlingen kan ikke angres. Elementene blir slettet permanent fra serveren.", - "recent": "Nylige", - "pinned": "Festet", - "folderShortcuts": "Mappesnarveier", - "failedToReconnectSSH": "Kunne ikke koble til SSH-økt på nytt", - "openTerminalHere": "Åpne terminal her", - "run": "Kjør", - "openTerminalInFolder": "Åpne terminal i denne mappen", - "openTerminalInFileLocation": "Åpne terminal ved filplassering", - "runningFile": "Kjører - {{file}}", - "onlyRunExecutableFiles": "Kan bare kjøre kjørbare filer", - "directories": "Kataloger", - "removedFromRecentFiles": "Fjernet \"{{name}}\" fra nylige filer", - "removeFailed": "Fjerning mislyktes", - "unpinnedSuccessfully": "Fjernet festing av \"{{name}}\"", - "unpinFailed": "Fjerning av festing mislyktes", - "removedShortcut": "Fjernet snarvei \"{{name}}\"", - "removeShortcutFailed": "Kunne ikke fjerne snarvei", - "clearedAllRecentFiles": "Tømte alle nylige filer", - "clearFailed": "Tømming mislyktes", - "removeFromRecentFiles": "Fjern fra nylige filer", - "clearAllRecentFiles": "Tøm alle nylige filer", - "unpinFile": "Løsne fil", - "removeShortcut": "Fjern snarvei", - "pinFile": "Fest fil", - "addToShortcuts": "Legg til snarveier", - "pasteFailed": "Innliming mislyktes", - "noUndoableActions": "Ingen handlinger kan angres", - "undoCopySuccess": "Angret kopiering: Slettet {{count}} kopierte filer", - "undoCopyFailedDelete": "Angre mislyktes: Kunne ikke slette kopierte filer", - "undoCopyFailedNoInfo": "Angre mislyktes: Fant ingen info om kopierte filer", - "undoMoveSuccess": "Angret flytting: Flyttet {{count}} filer tilbake", - "undoMoveFailedMove": "Angre mislyktes: Kunne ikke flytte filer tilbake", - "undoMoveFailedNoInfo": "Angre mislyktes: Fant ingen info om flyttede filer", - "undoDeleteNotSupported": "Sletteoperasjon kan ikke angres: Filer er slettet permanent fra server", - "undoTypeNotSupported": "Ikke støttet angretype", - "undoOperationFailed": "Angreoperasjon mislyktes", - "unknownError": "Ukjent feil", - "confirm": "Bekreft", - "find": "Finn...", - "replace": "Erstatt", - "downloadInstead": "Last ned i stedet", - "keyboardShortcuts": "Tastatursnarveier", - "searchAndReplace": "Søk og erstatt", - "editing": "Redigering", - "search": "Søk", - "findNext": "Finn neste", - "findPrevious": "Finn forrige", - "save": "Lagre", - "selectAll": "Marker alt", - "undo": "Angre", - "redo": "Gjør om", - "moveLineUp": "Flytt linje opp", - "moveLineDown": "Flytt linje ned", - "toggleComment": "Veksle kommentar", - "autoComplete": "Autofullfør", - "imageLoadError": "Kunne ikke laste bilde", - "startTyping": "Begynn å skrive...", - "unknownSize": "Ukjent størrelse", - "fileIsEmpty": "Filen er tom", - "largeFileWarning": "Advarsel om stor fil", - "largeFileWarningDesc": "Denne filen er {{size}} stor, noe som kan gi ytelsesproblemer når den åpnes som tekst.", - "fileNotFoundAndRemoved": "Filen «{{name}}» ble ikke funnet og er fjernet fra nylige/festede filer", - "failedToLoadFile": "Kunne ikke laste fil: {{error}}", - "serverErrorOccurred": "Serverfeil oppstod. Prøv igjen senere.", - "autoSaveFailed": "Autolagring mislyktes", - "fileAutoSaved": "Filen ble autolagret", - "moveFileFailed": "Kunne ikke flytte {{name}}", - "moveOperationFailed": "Flytteoperasjon mislyktes", - "canOnlyCompareFiles": "Kan kun sammenligne to filer", - "comparingFiles": "Sammenligner filer: {{file1}} og {{file2}}", - "dragFailed": "Draoperasjon mislyktes", - "filePinnedSuccessfully": "Filen «{{name}}» ble festet", - "pinFileFailed": "Kunne ikke feste fil", - "fileUnpinnedSuccessfully": "Filen «{{name}}» ble løsnet", - "unpinFileFailed": "Kunne ikke løsne fil", - "shortcutAddedSuccessfully": "Mappesnarvei «{{name}}» ble lagt til", - "addShortcutFailed": "Kunne ikke legge til snarvei", - "operationCompletedSuccessfully": "{{operation}} {{count}} elementer", - "operationCompleted": "{{operation}} {{count}} elementer", - "downloadFileSuccess": "Fil {{name}} ble lastet ned", - "downloadFileFailed": "Nedlasting mislyktes", - "moveTo": "Flytt til {{name}}", - "diffCompareWith": "Sammenlign forskjeller med {{name}}", - "dragOutsideToDownload": "Dra utenfor vinduet for å laste ned ({{count}} filer)", - "newFolderDefault": "NyMappe", - "newFileDefault": "NyFil.txt", - "successfullyMovedItems": "Flyttet {{count}} elementer til {{target}}", - "move": "Flytt", - "searchInFile": "Søk i fil (Ctrl+F)", - "showKeyboardShortcuts": "Vis tastatursnarveier", - "startWritingMarkdown": "Begynn å skrive markdown-innhold...", - "loadingFileComparison": "Laster filsammenligning...", - "reload": "Last inn på nytt", - "compare": "Sammenlign", - "sideBySide": "Side om side", - "inline": "Innebygd", - "fileComparison": "Filsammenligning: {{file1}} vs {{file2}}", - "fileTooLarge": "Fil for stor: {{error}}", - "sshConnectionFailed": "SSH-tilkobling mislyktes. Kontroller tilkoblingen til {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Kunne ikke laste fil: {{error}}", - "connecting": "Tilkobler...", - "connectedSuccessfully": "Tilkoblet", - "totpVerificationFailed": "TOTP-verifisering mislyktes", - "warpgateVerificationFailed": "Warpgate autentisering feilet", - "authenticationFailed": "Godkjenning mislyktes", - "verificationCodePrompt": "Bekreftelseskode:", - "changePermissions": "Endre tillatelser", - "currentPermissions": "Gjeldende tillatelser", - "owner": "Eier", - "group": "Gruppe", - "others": "Andre", - "read": "Les", - "write": "Skriv", - "execute": "Kjør", - "permissionsChangedSuccessfully": "Tillatelser endret", - "failedToChangePermissions": "Kunne ikke endre tillatelser", - "name": "Navn", - "sortByName": "Navn", - "sortByDate": "Dato Endret", - "sortBySize": "Størrelse", - "ascending": "Stigende", - "descending": "Synkende", - "root": "Rot", - "new": "Ny", - "sortBy": "Sorter etter", - "items": "Elementer", - "selected": "Valgt", - "editor": "Redaktør", - "octal": "Oktal", - "storage": "Lagring", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", "disk": "Disk", - "used": "Brukt", - "of": "av", - "toggleSidebar": "Bytt sidestolpe", - "cannotLoadPdf": "Kan ikke laste inn PDF", - "pdfLoadError": "Det oppstod en feil under lasting av denne PDF-filen.", - "loadingPdf": "Laster PDF...", - "loadingPage": "Laster side..." + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopier til vert…", - "moveToHost": "Flytt til vert…", - "copyItemsToHost": "Kopier {{count}} elementer til vert for…", - "moveItemsToHost": "Flytt {{count}} elementer til vert for…", - "noHostsConnected": "Ingen andre filbehandlerverter tilgjengelig.", - "noHostsConnectedHint": "Legg til en annen SSH-vert med Filbehandling aktivert i Vertsbehandling.", - "selectDestinationHost": "Velg destinasjonsvert", - "destinationPath": "Destinasjonssti", - "recentDestinations": "Nylige destinasjoner", - "collapseRecentDestinations": "Skjul nylige destinasjoner", - "expandRecentDestinations": "Utvid nylige destinasjoner", - "browseFolders": "Bla gjennom målmapper", - "browseDestination": "Bla gjennom eller skriv inn bane", - "confirmCopy": "Kopiere", - "confirmMove": "Flytte", - "transferring": "Overfører…", - "compressing": "Komprimering…", - "extracting": "Uttrekker…", - "transferringItems": "Overføring av {{current}} av {{total}} elementer…", - "transferSuccess": "Overføringen er fullført", - "transferError": "Overføringen mislyktes", - "transferPartial": "Overføringen er fullført med {{count}} feil", - "transferPartialHint": "Kunne ikke overføre: {{paths}}", - "itemsSummary": "{{count}} elementer", - "destMustBeDirectory": "Destinasjonen må være en katalog for overføringer av flere elementer", - "selectThisFolder": "Velg denne mappen", - "browsePathWillBeCreated": "Denne mappen finnes ikke ennå. Den vil bli opprettet når overføringen starter.", - "browsePathError": "Kunne ikke åpne denne banen på målverten.", - "goUp": "Gå opp", - "copyFolderToHost": "Kopier mappe til vert…", - "moveFolderToHost": "Flytt mappen til verten…", - "hostReady": "Ferdig", - "hostConnecting": "Kobler til…", - "hostDisconnected": "Ikke tilkoblet", - "hostAuthRequired": "Autentisering kreves – åpne Filbehandler på denne verten først", - "hostConnectionFailed": "Tilkoblingen mislyktes", - "metricsTitle": "Overføringstider", - "metricsPrepare": "Forbered destinasjon: {{duration}}", - "metricsCompress": "Komprimer på kilde: {{duration}}", - "metricsHopSourceRead": "Kilde → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → destinasjon (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → dest (lokal): {{throughput}}", - "metricsTransfer": "Ende-til-ende: {{throughput}} ({{duration}})", - "metricsExtract": "Uttrekk på destinasjon: {{duration}}", - "metricsSourceDelete": "Fjern fra kilden: {{duration}}", - "metricsTotal": "Totalt: {{duration}}", - "progressCompressing": "Komprimering på kildevert…", - "progressExtracting": "Uttrekker på destinasjon…", - "progressTransferring": "Overføring av data…", - "progressReconnecting": "Kobler til igjen…", - "parallelSegmentsLabel": "Parallelle overføringsfelt", - "parallelSegmentsOption": "{{count}} baner", - "parallelSegmentsHint": "Store filer deles inn i deler på 256 MB. Flere baner bruker separate tilkoblinger (som å starte flere overføringer) for høyere total gjennomstrømning.", - "progressTotalSpeed": "{{speed}} totalt ({{lanes}} baner)", - "progressTransferringItems": "Overføring av filer ({{current}} av {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} filer", - "sourceNotDeletedPartial": "Kildefiler beholdt (delvis overføring)", - "jumpHostLimitation": "Begge vertene må være tilgjengelige fra Termix-serveren. Direkte vert-til-vert-ruting støttes ikke.", - "cancel": "Kansellere", - "methodLabel": "Overføringsmetode", - "methodAuto": "Bil", - "methodTar": "Tar-arkiv", - "methodItemSftp": "SFTP per fil", - "methodAutoHint": "Velger tar eller SFTP per fil basert på antall filer, størrelse og komprimerbarhet. Enkeltfiler bruker alltid strømme-SFTP.", - "methodTarHint": "Komprimer på kildekode, overfør ett arkiv, pakk ut på destinasjon. Krever tar på begge Unix-vertene.", - "methodItemSftpHint": "Overfør hver fil individuelt via SFTP. Fungerer på alle verter, inkludert Windows.", - "methodPreviewLoading": "Beregning av overføringsmetode…", - "methodPreviewError": "Kunne ikke forhåndsvise overføringsmetoden. Serveren vil fortsatt velge en metode når du starter.", - "methodPreviewWillUseTar": "Vil bruke: Tar-arkiv", - "methodPreviewWillUseItemSftp": "Vil bruke: SFTP per fil", - "methodPreviewScanSummary": "{{fileCount}} filer, {{totalSize}} totalt (skannet på kildeverten).", - "methodItemSftpLimitation": "Hver fil bruker den samme SFTP-strømmen som en enkeltfilskopi, én etter én. Fremdriften kombineres på tvers av alle filene, så linjen beveger seg sakte under store filer.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Du valgte SFTP per fil.", - "user_tar": "Du valgte tar-arkiv.", - "tar_unavailable": "Tar er ikke tilgjengelig på én eller begge vertene – SFTP per fil vil bli brukt i stedet.", - "windows_host": "En Windows-vert er involvert – tar brukes ikke.", - "auto_multi_large": "Auto: flere filer, inkludert en stor fil ({{largestSize}}) med komprimerbare data — tar-pakker i én overføring.", - "auto_single_large_in_archive": "Auto: én stor fil ({{largestSize}}) i dette settet — SFTP per fil.", - "auto_many_incompressible": "Auto: stort sett inkomprimerbare data — SFTP per fil.", - "auto_many_files": "Auto: mange filer ({{fileCount}}) — tar reduserer overhead per fil.", - "auto_default": "Auto: SFTP per fil for dette settet." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Kansellere", - "progressCancelling": "Avbryter…", - "progressStalled": "Fastlåst", - "resumedHint": "Koblet til igjen en aktiv overføring som startet i et annet vindu.", - "transferCancelled": "Overføringen er avbrutt", - "transferCancelledCopyHint": "Delvise filer kan bli liggende igjen på destinasjonen.", - "transferCancelledMoveHint": "Delvise filer kan bli liggende igjen på destinasjonen. Kildefilene ble ikke fjernet.", - "cleanupDestFiles": "Rydd opp destinasjonen", - "cleanupDestFilesSuccess": "Fjernet delvise filer fra destinasjonen", - "cleanupDestFilesPartial": "Kunne ikke fjerne noen delvise filer", - "cleanupDestFilesNothing": "Ingenting å rydde opp i på destinasjonen", - "cleanupDestFilesError": "Opprydding mislyktes", - "retryTransfer": "Prøv på nytt", - "retryTransferError": "Forsøk på nytt mislyktes", - "transferFailedRetryHint": "Delvise data ble lagret på destinasjonen. Forsøket gjenopptas når forbindelsen er tilbake." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Ingen SSH-tunneler", - "createFirstTunnelMessage": "Du har ikke opprettet noen SSH-tunneler ennå. Konfigurer tunneltilkoblinger i Vertadministrator for å komme i gang.", - "connected": "Tilkoblet", - "disconnected": "Frakoblet", - "connecting": "Kobler til...", - "error": "Feil", - "canceling": "Avbryter...", - "connect": "Koble til", - "disconnect": "Koble fra", - "cancel": "Avbryt", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Lokal port", - "remotePort": "Fjernport", - "currentHostPort": "Gjeldende Vert Port", - "endpointPort": "Endepunkt port", - "bindIp": "Lokal IP", - "endpointSshConfig": "Endepunkt SSH konfigurasjon", - "endpointSshHost": "Endepunkt SSH vert", - "endpointSshHostPlaceholder": "Velg en konfigurert vert", - "endpointSshHostRequired": "Velg en endepunkt SSH-vert for hver klienttunnel.", - "attempt": "Forsøk {{current}} av {{max}}", - "nextRetryIn": "Neste forsøk om {{seconds}} sekunder", - "clientTunnels": "Klient tunneler", - "clientTunnel": "Klient tunnel", - "addClientTunnel": "Legg til klienttunnel", - "noClientTunnels": "Ingen klienttunneler konfigurert på denne datamaskinen.", - "tunnelName": "Tunnelnavn", - "remoteHost": "Fjernvert", - "autoStart": "Autostart", - "clientAutoStartDesc": "Starter når denne skrivebordsklienten åpner og forblir tilkoblet.", - "clientManualStartDesc": "Bruk start og stopp fra denne raden. Termix vil ikke åpne den automatisk.", - "clientRemoteServerNote": "Ekstern videresending kan kreve Tillatt TcpForwarding og GatewayPorts når endepunktet SSH-tjener avsluttes. Den eksterne porten lukkes når denne skrivebordet kobles fra.", - "clientTunnelStarted": "Klienttunnel startet", - "clientTunnelStopped": "Klienttunnel stoppet", - "tunnelTestSucceeded": "Tunnelsetest lyktes", - "tunnelTestFailed": "Tunneltesten mislyktes", - "localSaved": "Klienttunneler lagret", - "localSaveError": "Klarte ikke å lagre lokale klienttunneler", - "invalidBindIp": "Lokal IP må være en gyldig IPv4-adresse.", - "invalidLocalTargetIp": "Lokal mål-IP må være en gyldig IPv4-adresse.", - "invalidLocalPort": "Lokal port må være mellom 1 og 65535.", - "invalidRemotePort": "Ekstern port må være mellom 1 og 65535.", - "invalidLocalTargetPort": "Lokal målport må være mellom 1 og 65535.", - "invalidEndpointPort": "Endepunktsporten må være mellom 1 og 65535.", - "duplicateAutoStartBind": "Bare én automatisk start klienttunnel kan bruke {{bind}}.", - "manualControlError": "Kunne ikke oppdatere tunneltilstand.", - "active": "Aktiv", - "start": "Begynn", - "stop": "Stopp", - "test": "Prøve", - "type": "Type tunnel", - "typeLocal": "Lokal (-L)", - "typeRemote": "Ekstern (-R)", - "typeDynamic": "Dynamisk (-D)", - "typeServerLocalDesc": "Nåværende vert til slutt.", - "typeServerRemoteDesc": "Endepunkt tilbake til gjeldende vert.", - "typeClientLocalDesc": "Lokal datamaskin til endepunkt.", - "typeClientRemoteDesc": "Sluttpunkt tilbake til lokal datamaskin.", - "typeClientDynamicDesc": "SOCKS på lokal datamaskin.", - "typeDynamicDesc": "Videresend SOCKS5 CONNECT trafikk gjennom SSH", - "forwardDescriptionServerLocal": "Nåværende vert {{sourcePort}} → endepunkt {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endepunkt {{endpointPort}} → gjeldende vert {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS på gjeldende vert {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokal {{sourcePort}} → ekstern {{endpointPort}}.", - "forwardDescriptionClientRemote": "Ekstern {{sourcePort}} → lokal {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS på lokal port {{sourcePort}}.", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "Lokalt {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokale {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", - "route": "Rute:", - "lastStarted": "Sist startet", - "lastTested": "Sist testet", - "lastError": "Siste feil", - "maxRetries": "Maks antall forsøk", - "maxRetriesDescription": "Maksimum antall forsøk på nytt.", - "retryInterval": "Gjenværende intervall (sekunder)", - "retryIntervalDescription": "Tid å vente mellom forsøk på nytt.", - "local": "Lokal", - "remote": "Fjern", - "destination": "Mål", - "host": "Vert", - "mode": "Modus", - "noHostSelected": "Ingen vert valgt", - "working": "Arbeider..." + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Prosessor", - "memory": "Minne", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", "disk": "Disk", - "network": "Nettverk", - "uptime": "Oppetid", - "processes": "Prosesser", - "available": "Tilgjengelig", - "free": "Ledig", - "connecting": "Tilkobler...", - "connectionFailed": "Kan ikke koble til serveren", - "naCpus": "N/A CPU(er)", - "cpuCores_one": "{{count}} kjerne", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU-bruk", - "memoryUsage": "Minnebruk", - "diskUsage": "Diskbruk", - "failedToFetchHostConfig": "Kunne ikke hente vertskonfigurasjon", - "serverOffline": "Server frakoblet", - "cannotFetchMetrics": "Kan ikke hente målinger fra frakoblet server", - "totpFailed": "TOTP-verifisering mislyktes", - "noneAuthNotSupported": "Serverstatistikk støtter ikke 'ikke' autentiseringstype.", - "load": "Last", - "systemInfo": "Systeminformasjon", - "hostname": "Vertsnavn", - "operatingSystem": "Operativsystem", - "kernel": "Kjerne", - "seconds": "sekunder", - "networkInterfaces": "Nettverksgrensesnitt", - "noInterfacesFound": "Fant ingen nettverksgrensesnitt", - "noProcessesFound": "Fant ingen prosesser", - "loginStats": "SSH-påloggingsstatistikk", - "noRecentLoginData": "Ingen nylige påloggingsdata", - "executingQuickAction": "Utfører {{name}}...", - "quickActionSuccess": "{{name}} fullført", - "quickActionFailed": "{{name}} mislyktes", - "quickActionError": "Kunne ikke utføre {{name}}", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Lytter havner", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adresse", - "process": "Prosess", - "noData": "Ingen lytter til ports-data" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Brannmur", - "inactive": "Inaktiv", - "policy": "Vilkår", - "rules": "regler", - "noData": "Ingen brannmurdata tilgjengelig", - "action": "Handling", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Kilde", - "anywhere": "Uansett" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Last snitt", - "swap": "Bytte", - "architecture": "arkitektur", - "refresh": "Oppdater", - "retry": "Prøv igjen" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Selvhostet SSH og ekstern skrivebordsadministrasjon", - "loginTitle": "Logg inn i Termix", - "registerTitle": "Opprett konto", - "forgotPassword": "Glemt passord?", - "rememberMe": "Husk enhet for 30 dager (inkluderer TOTP)", - "noAccount": "Har du ikke konto?", - "hasAccount": "Har du allerede konto?", - "twoFactorAuth": "Tofaktorautentisering", - "enterCode": "Angi verifikasjonskode", - "backupCode": "Eller bruk reservekode", - "verifyCode": "Bekreft kode", - "redirectingToApp": "Videresender til app...", - "sshAuthenticationRequired": "SSH-autentisering kreves", - "sshNoKeyboardInteractive": "Tastaturinteraktiv autentisering er ikke tilgjengelig", - "sshAuthenticationFailed": "Autentisering mislyktes", - "sshAuthenticationTimeout": "Autentisering tidsavbrutt", - "sshNoKeyboardInteractiveDescription": "Serveren støtter ikke tastaturinteraktiv autentisering. Oppgi passord eller SSH-nøkkel.", - "sshAuthFailedDescription": "Oppgitte legitimasjoner var feil. Prøv igjen med gyldige opplysninger.", - "sshTimeoutDescription": "Autentiseringsforsøket fikk tidsavbrudd. Prøv igjen.", - "sshProvideCredentialsDescription": "Oppgi SSH-legitimasjonen din for å koble til denne serveren.", - "sshPasswordDescription": "Skriv inn passordet for denne SSH-tilkoblingen.", - "sshKeyPasswordDescription": "Hvis SSH-nøkkelen er kryptert, skriv inn passordfrasen her.", - "passphraseRequired": "Passfrase kreves", - "passphraseRequiredDescription": "SSH-nøkkelen er kryptert. Skriv inn adgangskoden for å låse opp den.", - "back": "Tilbake", - "firstUser": "Første bruker", - "firstUserMessage": "Du er den første brukeren og blir gjort til administrator. Du finner admininnstillinger i brukerlisten i sidepanelet. Hvis dette er en feil, sjekk Docker-loggene eller opprett en GitHub-sak.", - "external": "Ekstern", - "loginWithExternal": "Logg inn med ekstern leverandør", - "loginWithExternalDesc": "Logg inn med den konfigurerte eksterne identitetsleverandøren", - "externalNotSupportedInElectron": "Ekstern autentisering støttes ikke i Electron-appen ennå. Bruk webversjonen for OIDC-innlogging.", - "resetPasswordButton": "Tilbakestill passord", - "sendResetCode": "Send tilbakestillingskode", - "resetCodeDesc": "Skriv inn brukernavnet ditt for å få en tilbakestillingskode. Koden logges i Docker-containerens logger.", - "resetCode": "Tilbakestillingskode", - "verifyCodeButton": "Bekreft kode", - "enterResetCode": "Skriv inn 6-sifret kode fra Docker-loggene for bruker:", - "newPassword": "Nytt passord", - "confirmNewPassword": "Bekreft passord", - "enterNewPassword": "Skriv inn nytt passord for bruker:", - "signUp": "Registrer deg", - "desktopApp": "Desktop-app", - "loggingInToDesktopApp": "Logger inn i desktop-appen", - "loadingServer": "Laster server...", - "dataLossWarning": "Å tilbakestille passordet ditt på denne måten vil slette alle lagrede SSH-verter, legitimasjoner og annen kryptert data. Dette kan ikke angres. Bruk bare dette hvis du har glemt passordet og ikke er innlogget.", - "authenticationDisabled": "Autentisering deaktivert", - "authenticationDisabledDesc": "Alle autentiseringsmetoder er for øyeblikket deaktivert. Kontakt administratoren din.", - "attemptsRemaining": "{{count}} forsøk gjenstår" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifiser SSH vertsnøkkel", - "keyChangedWarning": "SSH vertsnøkkel endret", - "firstConnectionTitle": "Første gang du kobler til denne verten", - "firstConnectionDescription": "Vertens autensitet kan ikke etableres. Kontroller fingeravtrykket samsvarer med det du forventer.", - "keyChangedDescription": "Vertsnøkkelen for denne serveren er endret siden forrige tilkobling. Dette kan angi et sikkerhetsproblem.", - "previousKey": "Forrige nøkkel", - "newFingerprint": "Nytt fingeravtrykk", - "fingerprint": "Fingeravtrykk", - "verifyInstructions": "Hvis du stoler på denne verten, klikk Godta for å fortsette og lagre dette fingeravtrykket for fremtidige tilkoblinger.", - "securityWarning": "Sikkerhetsadvarsel", - "acceptAndContinue": "Godta og fortsett", - "acceptNewKey": "Aksepter ny nøkkel og fortsett" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Kunne ikke koble til databasen", - "unknownError": "Ukjent feil", - "loginFailed": "Innlogging mislyktes", - "failedPasswordReset": "Kunne ikke starte passordtilbakestilling", - "failedVerifyCode": "Kunne ikke verifisere tilbakestillingskode", - "failedCompleteReset": "Kunne ikke fullføre passordtilbakestilling", - "invalidTotpCode": "Ugyldig TOTP-kode", - "failedOidcLogin": "Kunne ikke starte OIDC-innlogging", - "silentSigninOidcUnavailable": "Stille pålogging ble forespurt, men OIDC innlogging er ikke tilgjengelig.", - "failedUserInfo": "Klarte ikke å hente brukerinformasjon etter innlogging", - "oidcAuthFailed": "OIDC-autentisering mislyktes", - "invalidAuthUrl": "Ugyldig autorisasjons-URL mottatt fra backend", - "requiredField": "Dette feltet er obligatorisk", - "minLength": "Minste lengde er {{min}}", - "passwordMismatch": "Passordene er ikke like", - "passwordLoginDisabled": "Brukernavn/passord-innlogging er deaktivert", - "sessionExpired": "Økten er utløpt – logg inn på nytt", - "totpRateLimited": "Frekvens begrenset: For mange TOTP-verifiseringsforsøk. Prøv igjen senere.", - "totpRateLimitedWithTime": "Frekvens begrenset: For mange TOTP-verifiseringsforsøk. Vennligst vent {{time}} sekunder før du prøver igjen.", - "resetCodeRateLimited": "Frekvens begrenset: For mange forsøk på verifisering. Prøv igjen senere.", - "resetCodeRateLimitedWithTime": "Frekvens begrenset: For mange forsøk på verifisering. Vennligst vent {{time}} sekunder før du prøver igjen.", - "authTokenSaveFailed": "Kunne ikke lagre godkjenningstoken", - "failedToLoadServer": "Kan ikke laste server" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Registrering av nye kontoer er deaktivert av en administrator. Logg inn eller kontakt administrator.", - "userNotAllowed": "Kontoen din er ikke autorisert for registrering. Vennligst kontakt en administrator.", - "databaseConnectionFailed": "Kunne ikke koble til databaseserveren", - "resetCodeSent": "Tilbakestillingskode sendt til Docker-loggene", - "codeVerified": "Kode verifisert", - "passwordResetSuccess": "Passord tilbakestilt", - "loginSuccess": "Innlogging vellykket", - "registrationSuccess": "Registrering vellykket" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokalt skrivebordtunneler som har målrettinger mot SSH-verter.", - "c2sTunnelPresets": "Forhåndsinnstillinger for klienttunnel", - "c2sTunnelPresetsDesc": "Lagre denne stasjonære klientens lokale tunnelliste som en navngitt server forhåndsinnstilling, eller last en forhåndsinnstilling tilbake i denne klienten.", - "c2sTunnelPresetsUnavailable": "Forhåndsinnstillinger for klienter er bare tilgjengelig på skrivebordsklienten.", - "c2sPresetName": "Forhåndsinnstilt navn", - "c2sPresetNamePlaceholder": "Klientens forhåndsinnstillingsnavn", - "c2sPresetToLoad": "Forhåndsinnstilling til lasting", - "c2sNoPresetSelected": "Ingen forhåndsinnstilling valgt", - "c2sNoPresets": "Ingen forhåndsinnstillinger er lagret", - "c2sLoadPreset": "Last", - "c2sCurrentLocalConfig": "{{count}} lokale klient tunnel(er) konfigurert på denne skrivebordet.", - "c2sPresetSyncNote": "Forhåndsinstillinger er eksplisitte øyeblikksbilder; lasting én erstatter denne stasjonære klientens lokale klient tunnel liste.", - "c2sPresetSaved": "Klienttunnel forhåndsinnstilling lagret", - "c2sPresetLoaded": "Forhåndsinnstilling for klient lastet lokalt", - "c2sPresetRenamed": "Klienttunnel forhåndsinnstilling endret navn", - "c2sPresetDeleted": "Klienttunnel forhåndsinnstilling slettet", - "c2sPresetLoadError": "Kunne ikke laste klienttunnelen forhåndsinnstillinger" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Språk", - "keyPassword": "nøkkelpassord", - "pastePrivateKey": "Lim inn privatnøkkelen her...", - "localListenerHost": "127.0.0.1 (lytt lokalt)", - "localTargetHost": "127.0.0.1 (mål på denne maskinen)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Skriv inn passordet ditt", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Dashbord", - "loading": "Laster oversikten ...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Hjelp", - "discord": "Splid", - "serverOverview": "Serveroversikt", - "version": "Versjon", - "upToDate": "Oppdatert", - "updateAvailable": "Oppdatering tilgjengelig", - "beta": "beta", - "uptime": "Oppetid", - "database": "Databasen", - "healthy": "Frisk", - "error": "Feil", - "totalHosts": "Totalt antall verter", - "totalTunnels": "Totalt antall tunneler", - "totalCredentials": "Totalt antall legitimasjoner", - "recentActivity": "Nylig aktivitet", - "reset": "Tilbakestill", - "loadingRecentActivity": "Laster nylig aktivitet...", - "noRecentActivity": "Ingen nylig aktivitet", - "quickActions": "Hurtighandlinger", - "addHost": "Legg til vert", - "addCredential": "Legg til legitimasjon", - "adminSettings": "Admininnstillinger", - "userProfile": "Brukerprofil", - "serverStats": "Serverstatistikk", - "loadingServerStats": "Laster serverstatistikk...", - "noServerData": "Ingen serverdata tilgjengelig", - "cpu": "Prosessor", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Tilpass dashbord", - "dashboardSettings": "Dashboard innstillinger", - "enableDisableCards": "Aktiver/deaktiver kort", - "resetLayout": "Tilbakestill til standard", - "serverOverviewCard": "Server oversikt", - "recentActivityCard": "Nylig aktivitet", - "networkGraphCard": "Nettverksgraf", - "networkGraph": "Nettverksgraf", - "quickActionsCard": "Hurtig valg", - "serverStatsCard": "Server Statistikk", - "panelMain": "Hoved", - "panelSide": "Farge", - "justNow": "akkurat nå" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "TILBAKESTILL", - "hostsOnline": "Verter online", - "activeTunnels": "Aktive tunneler", - "registerNewServer": "Registrer en ny server", - "storeSshKeysOrPasswords": "Lagre SSH-nøkler eller passord", - "manageUsersAndRoles": "Behandle brukere og roller", - "manageYourAccount": "Administrer kontoen din", - "hostStatus": "Vert status", - "noHostsConfigured": "Ingen verter konfigurert", - "online": "AKTIV", - "offline": "FRAKOBLET", - "onlineLower": "Pålogget", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Legg til:", - "commandPalette": "Kommandobalett palett", - "done": "Ferdig", - "editModeInstructions": "Dra oppgavelapper for å endre rekkefølgen · Dra kolonneskiller for å endre størrelse på kolonnene · Dra bunnen av et kort for å endre størrelsen på høyden · Papirkurv for å fjerne", - "empty": "Tom", - "clear": "Tøm" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Legg til vert", - "addGroup": "Legg til gruppe", - "addLink": "Legg til kobling", - "zoomIn": "Zoom inn", - "zoomOut": "Zoom ut", - "resetView": "Tilbakestill visning", - "selectHost": "Velg vert", - "chooseHost": "Velg en vert...", - "parentGroup": "Overordnet gruppe", - "noGroup": "Ingen gruppe", - "groupName": "Gruppens navn", - "color": "Farge", - "source": "Kilde", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Flytt til gruppe", - "selectGroup": "Velg gruppe...", - "addConnection": "Legg til tilkobling", - "hostDetails": "Vert detaljer", - "removeFromGroup": "Fjern fra gruppe", - "addHostHere": "Legg til vert her", - "editGroup": "Rediger gruppe", - "delete": "Slett", - "add": "Legg til", - "create": "Opprett", - "move": "Flytt", - "connect": "Nettverk", - "createGroup": "Opprett gruppe", - "selectSourcePlaceholder": "Velg kilde...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Ugyldig fil", - "hostAlreadyExists": "Vert er allerede i topologien", - "connectionExists": "Forbindelsen eksisterer allerede", - "unknown": "Ukjent", - "name": "Navn", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Status:", - "failedToAddNode": "Kan ikke legge til node", - "sourceDifferentFromTarget": "Kilde og mål må være forskjellig", - "exportJSON": "Eksporter JSON", - "importJSON": "Importer JSON", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Fil Behandler", - "tunnel": "Tunnelen", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Server Statistikk", - "noNodes": "Ingen noder ennå" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker er ikke aktivert for denne verten", - "validating": "Validerer Docker...", - "connecting": "Tilkobler...", - "error": "Feil", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Kunne ikke koble til Docker", - "containerStarted": "Beholder {{name}} startet", - "failedToStartContainer": "Kan ikke starte container {{name}}", - "containerStopped": "Beholder {{name}} stoppet", - "failedToStopContainer": "Kan ikke stoppe container {{name}}", - "containerRestarted": "Beholder {{name}} startet på nytt", - "failedToRestartContainer": "Kan ikke starte {{name}} på nytt", - "containerPaused": "Beholder {{name}} pauset", - "containerUnpaused": "Beholder {{name}} ikke aktivert", - "failedToTogglePauseContainer": "Kan ikke veksle pausestatus for beholderen {{name}}", - "containerRemoved": "Beholder {{name}} fjernet", - "failedToRemoveContainer": "Kan ikke fjerne container {{name}}", - "image": "Bilde", - "ports": "Porter", - "noPorts": "Ingen porter", - "start": "Begynn", - "confirmRemoveContainer": "Er du sikker på at du vil fjerne containeren '{{name}}'? Denne handlingen kan ikke angres.", - "runningContainerWarning": "Advarsel: Denne beholderen kjører. Fjerning av den vil stoppe beholderen først.", - "loadingContainers": "Laster containere ...", - "manager": "Docker behandler", - "autoRefresh": "Automatisk oppdatering", - "timestamps": "Tidsstempler", - "lines": "Linjer", - "filterLogs": "Filtrer logger...", - "refresh": "Oppdater", - "download": "Nedlasting", - "clear": "Tøm", - "logsDownloaded": "Nedlasting av logger var vellykket", - "last50": "Siste 50", - "last100": "Siste 100", - "last500": "Siste 500", - "last1000": "Siste 1000", - "allLogs": "Alle logger", - "noLogsMatching": "Ingen logger matcher \"{{query}}\"", - "noLogsAvailable": "Ingen logger tilgjengelig", - "noContainersFound": "Ingen beholdere funnet", - "noContainersFoundHint": "Ingen Docker beholdere er tilgjengelig på denne verten", - "searchPlaceholder": "Søk etter beholdere...", - "allStatuses": "Alle statuser", - "stateRunning": "Kjører", - "statePaused": "Pauset", - "stateExited": "Avsluttet", - "stateRestarting": "Omstart", - "noContainersMatchFilters": "Ingen beholdere samsvarer med filtrene", - "noContainersMatchFiltersHint": "Prøv å endre søke- eller filterkriteriene dine", - "failedToFetchStats": "Kan ikke hente containerstatistikk", - "containerNotRunning": "Beholder ikke kjører", - "startContainerToViewStats": "Start beholderen for å se statistikk", - "loadingStats": "Laster inn statistikk...", - "errorLoadingStats": "Feil ved lasting av statistikk", - "noStatsAvailable": "Ingen statistikk tilgjengelig", - "cpuUsage": "Prosessorbruk", - "current": "Nåværende", - "memoryUsage": "Minne brukt", - "networkIo": "Nettverk I/O", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Utgang", - "blockIo": "Blokker I/O", - "read": "Les", - "write": "Skriv", - "pids": "PID-er", - "containerInformation": "Container informasjon", - "name": "Navn", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Tilstand", - "containerMustBeRunning": "Container må kjøre for å få tilgang til konsollen", - "verificationCodePrompt": "Angi bekreftelseskode", - "totpVerificationFailed": "TOTP-verifisering mislyktes. Vennligst prøv igjen.", - "warpgateVerificationFailed": "Warpgate godkjenning mislyktes. Prøv på nytt.", - "connectedTo": "Koblet til {{containerName}}", - "disconnected": "Frakoblet", - "consoleError": "Konsoll feil", - "errorMessage": "Feil: {{message}}", - "failedToConnect": "Kan ikke koble til beholderen", - "console": "Konsoll", - "selectShell": "Velg skall", - "bash": "Baskisk", - "sh": "i.i.", - "ash": "aske", - "connect": "Nettverk", - "disconnect": "Frakoblet", - "notConnected": "Ikke tilkoblet", - "clickToConnect": "Klikk på koble for å starte en skalløkt", - "connectingTo": "Kobler til {{containerName}}...", - "containerNotFound": "Beholder ikke funnet", - "backToList": "Tilbake til listen", - "logs": "Logger", - "stats": "Statistikk", - "consoleTab": "Konsoll", - "startContainerToAccess": "Start beholderen for å få tilgang til konsollen" + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Generelt", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Brukere", - "sectionSessions": "Økter", - "sectionRoles": "Roller", - "sectionDatabase": "Databasen", - "sectionApiKeys": "API nøkler", - "allowRegistration": "Tillat brukerregistrering", - "allowRegistrationDesc": "La nye brukere selvregistrere seg", - "allowPasswordLogin": "Tillat passordpålogging", - "allowPasswordLoginDesc": "Brukernavn/passord innlogging", - "oidcAutoProvision": "OIDC auto-levering", - "oidcAutoProvisionDesc": "Opprett kontoer automatisk for OIDC-brukere selv når registrering er deaktivert", - "allowPasswordReset": "Tillat tilbakestilling av passord", - "allowPasswordResetDesc": "Tilbakestill koden via Docker-loggene", - "sessionTimeout": "Tidsavbrudd for økt", - "hours": "timer", - "sessionTimeoutRange": "Min. 1h · Maks 720h", - "monitoringDefaults": "Standarder for overvåking", - "statusCheck": "Statussjekk", - "metrics": "Måltall", - "sec": "sek", - "logLevel": "Loggnivå (Automatic Translation)", - "enableGuacamole": "Aktiver Guacamol", - "enableGuacamoleDesc": "RDP/VNC eksternt skrivebord", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Konfigurer OpenID tilkobling for SSO. Felter merket * er påkrevd.", - "oidcClientId": "Klient ID", - "oidcClientSecret": "Klient hemmelighet", - "oidcAuthUrl": "Autorisasjons URL", - "oidcIssuerUrl": "Utsteders URL", - "oidcTokenUrl": "Sjetongens URL", - "oidcUserIdentifier": "Bruker identifikasjons sti", - "oidcDisplayName": "Vis navnesti", - "oidcScopes": "Omfang", - "oidcUserinfoUrl": "Overstyr brukerinfo URL", - "oidcAllowedUsers": "Tillatte brukere", - "oidcAllowedUsersDesc": "En e-post per linje. La stå tom for å tillate alle.", - "removeOidc": "Fjern", - "usersCount": "{{count}} brukere", - "createUser": "Opprett", - "newRole": "Ny rolle", - "roleName": "Navn", - "roleDisplayName": "Vis navn", - "roleDescription": "Beskrivelse", - "rolesCount": "{{count}} roller", - "createRole": "Opprett", - "creating": "Oppretter...", - "exportDatabase": "Eksporter database", - "exportDatabaseDesc": "Last ned en sikkerhetskopi av alle verter, brukeropplysninger og innstillinger", - "export": "Eksporter", - "exporting": "Eksporterer...", - "importDatabase": "Importer Database", - "importDatabaseDesc": "Gjenopprett fra en .sqlite sikkerhetskopifil", - "importDatabaseSelected": "Valgt: {{name}}", - "selectFile": "Velg fil", - "changeFile": "Endre", - "import": "Importer", - "importing": "Importerer...", - "apiKeysCount": "{{count}} nøkler", - "newApiKey": "Ny API-nøkkel", - "apiKeyCreatedWarning": "Nøkkel lagd. Kopier den, den vil ikke bli vist igjen.", - "apiKeyName": "Navn", - "apiKeyUser": "Bruker", - "apiKeySelectUser": "Velg en bruker...", - "apiKeyExpiresAt": "Utløper ved", - "createKey": "Lag nøkkel", - "apiKeyNoExpiry": "Ingen utløpsdato", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Dobbel Auth", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokal", - "adminStatusAdministrator": "Administratorsiden", - "adminStatusRegularUser": "Vanlig bruker", - "adminBadge": "ADMINE", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "TILPASSET", - "youBadge": "DU", - "sessionsActive": "{{count}} aktiv", - "sessionActive": "Aktiv: {{time}}", - "sessionExpires": "Uttrykk: {{time}}", - "revokeAll": "Alle", - "revokeAllSessionsSuccess": "Alle økter for brukeren tilbakekalt", - "revokeAllSessionsFailed": "Kan ikke tilbakekalle økter", - "revokeSessionFailed": "Kan ikke tilbakekalle sesjonen", - "addRole": "Legg til rolle", - "noCustomRoles": "Ingen egendefinerte roller definert", - "removeRoleFailed": "Kan ikke fjerne rolle", - "assignRoleFailed": "Kunne ikke tildele rolle", - "deleteRoleFailed": "Kunne ikke slette rolle", - "userAdminAccess": "Administratorsiden", - "userAdminAccessDesc": "Full tilgang til alle administrative innstillinger", - "userRoles": "Roller", - "revokeAllUserSessions": "Opphev alle økter", - "revokeAllUserSessionsDesc": "Tving innlogging på nytt på alle enheter", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Sletting av denne brukeren er permanent.", - "deleteUser": "Slett {{username}}", - "deleting": "Sletter...", - "deleteUserFailed": "Kunne ikke slette bruker", - "deleteUserSuccess": "Bruker \"{{username}}\" slettet", - "deleteRoleSuccess": "Rollen \"{{name}}\" slettet", - "revokeKeySuccess": "Nøkkel \"{{name}}\" tilbakekalt", - "revokeKeyFailed": "Kan ikke tilbakekalle nøkkel", - "copiedToClipboard": "Kopiert til utklippstavle", - "done": "Ferdig", - "createUserTitle": "Opprett bruker", - "createUserDesc": "Opprett en ny lokal konto.", - "createUserUsername": "Brukernavn", - "createUserPassword": "Passord", - "createUserPasswordHint": "Minimum 6 tegn.", - "createUserEnterUsername": "Skriv inn brukernavn", - "createUserEnterPassword": "Skriv inn passord", - "createUserSubmit": "Opprett bruker", - "editUserTitle": "Administrere bruker: {{username}}", - "editUserDesc": "Rediger roller, admin status, økter og kontoinnstillinger.", - "editUserUsername": "Brukernavn", - "editUserAuthType": "Autentisering Type", - "editUserAdminStatus": "Admins status", - "editUserUserId": "Bruker ID", - "linkAccountTitle": "Koble OIDC til passordkonto", - "linkAccountDesc": "Slå sammen OIDC-kontoen {{username}} med en eksisterende lokal konto.", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", "linkAccountWarningTitle": "This will:", - "linkAccountEffect1": "Slett bare OIDC-kontoen", - "linkAccountEffect2": "Legg til OIDC innlogging til målkontoen", - "linkAccountEffect3": "Tillate både OIDC og passord innlogging", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Skriv inn lokalt brukernavn for lenk til", - "linkAccounts": "Koble til kontoer", - "linkAccountSuccess": "OIDC-konto koblet til «{{username}}»", - "linkAccountFailed": "Kunne ikke koble til OIDC-kontoen", - "linkAccountInProgress": "Kobler...", - "saving": "Lagrer...", - "updateRegistrationFailed": "Kan ikke oppdatere registreringsinnstillingene", - "updatePasswordLoginFailed": "Kunne ikke oppdatere innloggingsinnstillingene for passord", - "updateOidcAutoProvisionFailed": "Kan ikke oppdatere OIDC auto-levering innstillinger", - "updatePasswordResetFailed": "Kan ikke oppdatere innstillingene for tilbakestilling av passord", - "sessionTimeoutRange2": "Sesjonens timeout må være mellom 1 og 720 timer", - "sessionTimeoutSaved": "Sesjonens tidsavbrudd lagret", - "sessionTimeoutSaveFailed": "Kunne ikke lagre økttidsavbrudd", - "monitoringIntervalInvalid": "Ugyldige intervallverdier", - "monitoringSaved": "Innstillingene er lagret", - "monitoringSaveFailed": "Klarte ikke å lagre overvåkingsinnstillingene", - "guacamoleSaved": "Innstillinger for Guacamol lagret", - "guacamoleSaveFailed": "Lagring av Guacamol-innstillingene mislyktes", - "guacamoleUpdateFailed": "Kan ikke oppdatere innstillinger for Guacamole", - "logLevelUpdateFailed": "Kunne ikke oppdatere loggnivå", - "oidcSaved": "OIDC-konfigurasjon lagret", - "oidcSaveFailed": "Kunne ikke lagre OIDC config", - "oidcRemoved": "OIDC konfigurasjon fjernet", - "oidcRemoveFailed": "Kunne ikke fjerne OIDC config", - "createUserRequired": "Brukernavn og passord er påkrevd", - "createUserPasswordTooShort": "Passord må bestå av minst 6 tegn", - "createUserSuccess": "Bruker \"{{username}}\" opprettet", - "createUserFailed": "Kunne ikke opprette bruker", - "updateAdminStatusFailed": "Kunne ikke oppdatere admin status", - "allSessionsRevoked": "Alle økter opphevet", - "revokeSessionsFailed": "Kan ikke tilbakekalle økter", - "createRoleRequired": "Navn og visningsnavn er påkrevd", - "createRoleSuccess": "Rollen \"{{name}}\" opprettet", - "createRoleFailed": "Kunne ikke opprette rolle", - "apiKeyNameRequired": "Nøkkelnavn er påkrevd", - "apiKeyUserRequired": "Bruker ID kreves", - "apiKeyCreatedSuccess": "API-nøkkel \"{{name}}\" opprettet", - "apiKeyCreateFailed": "Kunne ikke opprette API-nøkkel", - "exportSuccess": "Databasen ble eksportert", - "exportFailed": "Databaseeksporten mislyktes", - "importSelectFile": "Vennligst velg en fil først", - "importCompleted": "Import fullført: {{total}} varer importert, {{skipped}} er hoppet over", - "importFailed": "Import mislyktes: {{error}}", - "importError": "Databaseimport mislyktes" + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Vert", - "hostPlaceholder": "192.168.1.1 eller eksempel.no", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Brukernavn", - "usernamePlaceholder": "brukernavn", - "authLabel": "Godkjenning", - "passwordLabel": "Passord", - "passwordPlaceholder": "passord", - "privateKeyLabel": "Privat nøkkel", - "privateKeyPlaceholder": "Lim inn privat nøkkel...", - "credentialLabel": "Påloggingsrettigheter", - "credentialPlaceholder": "Velg en lagret legitimasjon", - "connectToTerminal": "Koble til terminal", - "connectToFiles": "Koble til filer" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Ingen terminal valgt", - "noTerminalSelectedHint": "Åpne en SSH terminal fane for å vise sin kommandologell", - "searchPlaceholder": "Søk etter historikk...", - "clearAll": "Fjern alle", - "noHistoryEntries": "Ingen oppføringer i historien", - "trackingDisabled": "Historie-sporing er deaktivert", - "trackingDisabledHint": "Aktiver den i profilinnstillingene dine for å ta opp kommandoer." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Nøkkel opptak", - "recordToTerminals": "Registrer terminaler", - "selectAll": "Alle", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Ingen terminalfaner åpne", - "selectTerminalsAbove": "Velg terminaler over", - "broadcastInputPlaceholder": "Skriv her for å kringkaste tastetrykk...", - "stopRecording": "Stopp lagring", - "startRecording": "Start opptak", - "settingsTitle": "Innstillinger", - "enableRightClickCopyPaste": "Aktiver høyreklikk kopier/lim" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Oppsett", - "selectLayoutAbove": "Velg et oppsett over", - "selectLayoutHint": "Velg hvor mange paneler som skal vises", - "panesTitle": "Paneler", - "openTabsTitle": "Åpne faner", - "dragTabsHint": "Dra faner inn i rutene ovenfor, eller bruk Hurtigtildeling", - "dropHere": "Slipp her", - "emptyPane": "Tom", - "dashboard": "Kontrollpanel", - "clearSplitScreen": "Tøm delt skjerm", - "quickAssign": "Hurtigtildeling", - "alreadyAssigned": "Rute {{index}}", - "splitTab": "Del fane", - "addToSplit": "Legg til i deling", - "removeFromSplit": "Fjern fra Split", - "assignToPane": "Tilordne til rute" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Opprett tekstutdrag", - "createSnippetDescription": "Opprett en ny kommando utdrag for rask kjøring", - "nameLabel": "Navn", - "namePlaceholder": "f.eks omstart Nginx", - "descriptionLabel": "Beskrivelse", - "descriptionPlaceholder": "Valgfri beskrivelse", - "optional": "Valgfritt", - "folderLabel": "Mappe", - "noFolder": "Ingen mappe (Ikke kategorisert)", - "commandLabel": "Kommando", - "commandPlaceholder": "F.eks. sudo-systemctl omstart av nginx", - "cancel": "Avbryt", - "createSnippetButton": "Opprett tekstutdrag", - "createFolderTitle": "Opprett mappe", - "createFolderDescription": "Organiser snippeten dine i mapper", - "folderNameLabel": "Mappe navn", - "folderNamePlaceholder": "F.eks. systemkommandoer, Docker skripter", - "folderColorLabel": "Farge på mappe", - "folderIconLabel": "Mappe ikon", - "previewLabel": "Forhåndsvisning", - "folderNameFallback": "Mappe navn", - "createFolderButton": "Opprett mappe", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Alle", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Ingen terminalfaner åpne", - "searchPlaceholder": "Søk snippet...", - "newSnippet": "Ny tekstutdrag", - "newFolder": "Ny mappe", - "run": "Kjør", - "noSnippetsInFolder": "Ingen snippets i denne mappen", - "uncategorized": "Ukategorisert", - "editSnippetTitle": "Rediger tekstutdrag", - "editSnippetDescription": "Oppdater denne kommandoen utdrag", - "saveSnippetButton": "Lagre endringer", - "createSuccess": "Snutt er opprettet", - "createFailed": "Kunne ikke opprette utdraget", - "updateSuccess": "Sniktippen er oppdatert", - "updateFailed": "Kunne ikke oppdatere utdraget", - "deleteFailed": "Kunne ikke slette utdragsutdrag", - "folderCreateSuccess": "Mappen ble opprettet", - "folderCreateFailed": "Kunne ikke opprette mappen", - "editFolderTitle": "Rediger mappe", - "editFolderDescription": "Endre navn på eller endre utseendet til denne mappen", - "saveFolderButton": "Lagre endringer", - "editFolder": "Rediger mappe", - "deleteFolder": "Slett mappe", - "folderDeleteSuccess": "Mappe \"{{name}}slettet", - "folderDeleteFailed": "Kunne ikke slette mappe", - "folderEditSuccess": "Mappen ble oppdatert", - "folderEditFailed": "Kunne ikke oppdatere mappen", - "confirmRunMessage": "Kjør \"{{name}}\"?", - "confirmRunButton": "Løp", - "runSuccess": "Ran \"{{name}}\" i terminaler for {{count}}", - "copySuccess": "Kopiert \"{{name}}\" til utklippstavle", - "shareTitle": "Del tekstutdrag", - "shareUser": "Bruker", - "shareRole": "Rolle", - "selectUser": "Velg en bruker...", - "selectRole": "Velg en rolle...", - "shareSuccess": "Slutt ble delt", - "shareFailed": "Kunne ikke dele rulling", - "revokeSuccess": "Tilgang tilbakekalt", - "revokeFailed": "Kunne ikke oppheve tilgangen", - "currentAccess": "Gjeldende tilgang", - "shareLoadError": "Kunne ikke laste delingsdata", - "loading": "Laster...", - "close": "Lukk", - "reorderFailed": "Kunne ikke lagre rekkefølgen på kodeutdraget" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Konto", - "sectionAppearance": "Utseende", - "sectionSecurity": "Sikkerhet", - "sectionApiKeys": "API nøkler", - "sectionC2sTunnels": "C2S tunneler", - "usernameLabel": "Brukernavn", - "roleLabel": "Rolle", - "roleAdministrator": "Administratorsiden", - "authMethodLabel": "Auth metode", - "authMethodLocal": "Lokal", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "På", - "twoFaOff": "Av", - "versionLabel": "Versjon", - "deleteAccount": "Slett konto", - "deleteAccountDescription": "Slett kontoen din permanent", - "deleteButton": "Slett", - "deleteAccountPermanent": "Denne handlingen er permanent og kan ikke angres.", - "deleteAccountWarning": "Alle økter, verter, brukeropplysninger og innstillinger vil bli slettet permanent.", - "confirmPasswordDeletePlaceholder": "Skriv inn passord for å bekrefte", - "languageLabel": "Språk", - "themeLabel": "Tema", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Inngående farge", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Kommando Autofullfør", - "commandAutocompleteDesc": "Vis autofullfør mens du skriver", - "historyTracking": "Historikk sporing", - "historyTrackingDesc": "Spor terminalkommandoer", - "syntaxHighlighting": "Syntaksutheving", - "syntaxHighlightingDesc": "Fremhev terminal utgang", - "commandPalette": "Kommandobalett palett", - "commandPaletteDesc": "Aktiver hurtigtast", - "reopenTabsOnLogin": "Åpne faner igjen ved pålogging", - "reopenTabsOnLoginDesc": "Gjenopprett åpne faner når du logger inn eller oppdaterer siden, selv fra en annen enhet", - "confirmTabClose": "Bekreft lukking av fane", - "confirmTabCloseDesc": "Spør før lukking av terminalfaner", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Vis vertsmerker", - "showHostTagsDesc": "Vis emneord i vertslisten", - "hostTrayOnClick": "Klikk for å utvide vertshandlinger", - "hostTrayOnClickDesc": "Vis alltid tilkoblingsknapper; klikk for å utvide administrasjonsalternativer i stedet for å holde musepekeren over", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Hold appskinnen i venstre sidefelt alltid utvidet i stedet for å utvides når du holder musepekeren over den", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", "settingsSnippets": "Snippets", - "foldersCollapsed": "Mapper samlet", - "foldersCollapsedDesc": "Skjul mapper som standard", - "confirmExecution": "Bekreft kjøring", - "confirmExecutionDesc": "Bekreft før kjøring av snippets", - "settingsUpdates": "Oppdateringer", - "disableUpdateChecks": "Deaktiver oppdateringskontroller", - "disableUpdateChecksDesc": "Slutt å sjekke etter oppdateringer", - "totpAuthenticator": "TOTP autentisering", - "totpEnabled": "2FA er aktivert", - "totpDisabled": "Legg til ekstra pålogging", - "disable": "Deaktiver", - "enable": "Aktiver", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Skann QR-kode eller skriv inn hemmelig i autentiseringsappen din, skriv så inn den 6-sifrede koden", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifiser", - "changePassword": "Endre passord", - "currentPasswordLabel": "Nåværende passord", - "currentPasswordPlaceholder": "Nåværende passord", - "newPasswordLabel": "Nytt passord", - "newPasswordPlaceholder": "Nytt passord", - "confirmPasswordLabel": "Bekreft nytt passord", - "confirmPasswordPlaceholder": "Bekreft nytt passord", - "updatePassword": "Oppdater passord", - "createApiKeyTitle": "Opprett API-nøkkel", - "createApiKeyDescription": "Generer en ny API-nøkkel for programmatisk tilgang.", - "apiKeyNameLabel": "Navn", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "valgfritt", - "cancel": "Avbryt", - "createKey": "Lag nøkkel", - "apiKeyCount": "{{count}} nøkler", - "newKey": "Ny nøkkel", - "noApiKeys": "Ingen API nøkler ennå.", - "apiKeyActive": "Aktiv", - "apiKeyUsageHint": "Inkluder nøkkelen din i", - "apiKeyUsageHintHeader": "topptekst.", - "apiKeyPermissionsHint": "Nøkler arver tillatelser for opprettelse av bruker.", - "roleUser": "Bruker", - "authMethodDual": "Dobbel Auth", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Kunne ikke starte TOTP-oppsettet", - "totpEnter6Digits": "Angi en 6-sifret kode", - "totpEnabledSuccess": "To-faktor autentisering aktivert", - "totpInvalidCode": "Ugyldig kode, prøv igjen", - "totpDisableInputRequired": "Skriv inn TOTP-koden eller passordet", - "totpDisabledSuccess": "To-faktor autentisering deaktivert", - "totpDisableFailed": "Kunne ikke deaktivere 2FA", - "totpDisableTitle": "Deaktiver 2FA", - "totpDisablePlaceholder": "Skriv inn TOTP-kode eller passord", - "totpDisableConfirm": "Deaktiver 2FA", - "totpContinueVerify": "Fortsett til godkjenning", - "totpVerifyTitle": "Verifiser koden", - "totpBackupTitle": "Sikkerhetskopierings koder", - "totpDownloadBackup": "Last ned backup-koder", - "done": "Ferdig", - "secretCopied": "Hemmelig kopiert til utklippstavlen", - "apiKeyNameRequired": "Nøkkelnavn er påkrevd", - "apiKeyCreated": "API-nøkkel \"{{name}}\" opprettet", - "apiKeyCreateFailed": "Kunne ikke opprette API-nøkkel", - "apiKeyUser": "Bruker", - "apiKeyExpires": "Utløper", - "apiKeyRevoked": "API-nøkkel \"{{name}}\" opphevet", - "apiKeyRevokeFailed": "Kan ikke tilbakekalle API-nøkkel", - "passwordFieldsRequired": "Nåværende og nye passord er påkrevd", - "passwordMismatch": "Passordene samsvarer ikke", - "passwordTooShort": "Passord må bestå av minst 6 tegn", - "passwordUpdated": "Passordet er oppdatert", - "passwordUpdateFailed": "Kunne ikke oppdatere passord", - "deletePasswordRequired": "Passord er nødvendig for å slette kontoen din", - "deleteFailed": "Kunne ikke slette konto", - "deleting": "Sletter...", - "colorPickerTooltip": "Åpne fargevelger", - "themeSystem": "Systemadministrasjon", - "themeLight": "Lys", - "themeDark": "Mørk", - "themeDracula": "Drakula", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarisert", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "En mørk", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/pl_PL.json b/src/ui/locales/translated/pl_PL.json index ef25026a..9bc5af32 100644 --- a/src/ui/locales/translated/pl_PL.json +++ b/src/ui/locales/translated/pl_PL.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Foldery", + "folders": "Folders", "folder": "Folder", - "password": "Hasło", - "key": "Klucz", - "sshPrivateKey": "Klucz prywatny SSH", - "upload": "Prześlij", - "keyPassword": "Hasło klucza", - "sshKey": "Klucz SSH", - "uploadPrivateKeyFile": "Prześlij plik klucza prywatnego", - "searchCredentials": "Szukaj danych logowania...", - "addCredential": "Dodaj poświadczenia", - "caCertificate": "Certyfikat CA (-cert.pub)", - "caCertificateDescription": "Opcjonalnie: Prześlij lub wklej plik certyfikatu CA-signed (np. id_ed25519-cert.pub). Wymagane, gdy serwer SSH używa autoryzacji opartej na certyfikacie.", - "uploadCertFile": "Prześlij plik -cert.pub", - "clearCert": "Wyczyść", - "certLoaded": "Certyfikat załadowany", - "certPublicKeyLabel": "Certyfikat CA", - "certTypeLabel": "Typ certyfikatu", - "pasteOrUploadCert": "Wklej lub wgraj certyfikat -cert.pub...", - "hasCaCert": "Posiada certyfikat CA", - "noCaCert": "Brak certyfikatu CA" + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Nie udało się załadować alertów", - "failedToDismissAlert": "Nie udało się odrzucić alertu" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Konfiguracja serwera", - "description": "Skonfiguruj adres URL serwera Termix, aby połączyć się z usługami backend", - "serverUrl": "Adres URL serwera", - "enterServerUrl": "Wprowadź adres URL serwera", - "saveFailed": "Nie udało się zapisać konfiguracji", - "saveError": "Błąd podczas zapisywania konfiguracji", - "saving": "Zapisywanie...", - "saveConfig": "Zapisz konfigurację", - "helpText": "Wprowadź adres URL, w którym działa Twój serwer Termix (np. http://localhost:30001 lub https://your-server.com)", - "changeServer": "Zmień serwer", - "mustIncludeProtocol": "Adres URL serwera musi zaczynać się od http:// lub https://", - "allowInvalidCertificate": "Zezwól na nieprawidłowy certyfikat", - "allowInvalidCertificateDesc": "Używaj tylko w przypadku zaufanych serwerów hostowanych samodzielnie, z certyfikatami podpisanymi samodzielnie lub opartymi na adresie IP.", - "useEmbedded": "Użyj serwera lokalnego", - "embeddedDesc": "Uruchom Termix z wbudowanym serwerem lokalnym (nie jest wymagany zdalny serwer)", - "embeddedConnecting": "Łączenie z serwerem lokalnym...", - "embeddedNotReady": "Serwer lokalny nie jest jeszcze gotowy. Poczekaj chwilę i spróbuj ponownie.", - "localServer": "Serwer lokalny" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Błąd sprawdzania wersji", - "checkFailed": "Nie udało się sprawdzić aktualizacji", - "upToDate": "Aplikacja jest aktualna", - "currentVersion": "Używasz wersji {{version}}", - "updateAvailable": "Dostępna aktualizacja", - "newVersionAvailable": "Dostępna jest nowa wersja! Używasz {{current}}, ale {{latest}} jest dostępny.", - "betaVersion": "Wersja beta", - "betaVersionDesc": "Używasz {{current}}, który jest nowszy niż najnowsze stabilne wydanie {{latest}}.", - "releasedOn": "Wydano dnia {{date}}", - "downloadUpdate": "Pobierz aktualizację", - "checking": "Sprawdzanie aktualizacji...", - "checkUpdates": "Sprawdź aktualizacje", - "checkingUpdates": "Sprawdzanie aktualizacji...", - "updateRequired": "Wymagana aktualizacja" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Zamknij", + "close": "Close", "minimize": "Minimize", - "online": "Dostępny", + "online": "Online", "offline": "Offline", - "continue": "Kontynuuj", - "maintenance": "Konserwacja", - "degraded": "Degradacja", - "error": "Błąd", - "warning": "Ostrzeżenie", - "unsavedChanges": "Niezapisane zmiany", - "dismiss": "Odrzuć", - "loading": "Ładowanie...", - "optional": "Opcjonalnie", - "connect": "Połącz", - "copied": "Skopiowano", - "connecting": "Łączenie...", - "updateAvailable": "Dostępna aktualizacja", - "appName": "Termiks", - "openInNewTab": "Otwórz w nowej karcie", - "noReleases": "Brak wydań", - "updatesAndReleases": "Aktualizacje i wydania", - "newVersionAvailable": "Dostępna jest nowa wersja ({{version}}).", - "failedToFetchUpdateInfo": "Nie udało się pobrać informacji o aktualizacji", - "preRelease": "Wydanie wstępne", - "noReleasesFound": "Nie znaleziono wydań.", - "cancel": "Anuluj", - "username": "Nazwa użytkownika", - "login": "Logowanie", - "register": "Rejestracja", - "password": "Hasło", - "confirmPassword": "Potwierdź hasło", - "back": "Powrót", - "save": "Zapisz", - "saving": "Zapisywanie...", - "delete": "Usuń", - "rename": "Zmień nazwę", - "edit": "Edytuj", - "add": "Dodaj", - "confirm": "Potwierdź", - "no": "Nie", - "or": "LUB", - "next": "Następny", - "previous": "Poprzedni", - "refresh": "Odśwież", - "language": "Język", - "checking": "Sprawdzanie...", - "checkingDatabase": "Sprawdzanie połączenia z bazą danych...", - "checkingAuthentication": "Sprawdzanie uwierzytelniania...", - "backendReconnected": "Połączenie z serwerem przywrócone", - "connectionDegraded": "Połączenie z serwerem zostało utracone, odzyskiwanie…", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Usuń", - "create": "Utwórz", - "update": "Aktualizuj", - "copy": "Kopiuj", - "copyFailed": "Nie udało się skopiować do schowka", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Przywróć", - "of": "z" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Strona główna", + "home": "Home", "terminal": "Terminal", - "docker": "Dokujący", - "tunnels": "Tunele", - "fileManager": "Menedżer plików", - "serverStats": "Statystyki serwera", - "admin": "Administrator", - "userProfile": "Profil użytkownika", - "splitScreen": "Podziel ekran", - "confirmClose": "Zamknąć tę aktywną sesję?", - "close": "Zamknij", - "cancel": "Anuluj", - "sshManager": "Menedżer SSH", - "cannotSplitTab": "Nie można podzielić tej karty", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Skopiuj hasło", - "copySudoPassword": "Skopiuj hasło Sudo", - "passwordCopied": "Hasło skopiowane do schowka", - "noPasswordAvailable": "Hasło nie jest dostępne", - "failedToCopyPassword": "Nie udało się skopiować hasła", - "refreshTab": "Odśwież połączenie", - "openFileManager": "Otwórz menedżera plików", - "dashboard": "Pulpit", - "networkGraph": "Wykres sieci", - "quickConnect": "Szybkie połączenie", - "sshTools": "Narzędzia SSH", - "history": "Historia", - "hosts": "Hosty", - "snippets": "Fragmenty", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", "hostManager": "Host Manager", - "credentials": "Dane logowania", - "connections": "Znajomości", + "credentials": "Credentials", + "connections": "Connections", "roleAdministrator": "Administrator", - "roleUser": "Użytkownik" + "roleUser": "User" }, "hosts": { - "hosts": "Hosty", - "noHosts": "Brak hostów SSH", - "retry": "Ponów próbę", - "refresh": "Odśwież", - "optional": "Opcjonalnie", - "downloadSample": "Pobierz próbkę", - "failedToDeleteHost": "Nie udało się usunąć {{name}}", - "importSkipExisting": "Import (pomiń istniejące)", - "connectionDetails": "Szczegóły połączenia", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Zdalny pulpit", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Nazwa użytkownika", + "username": "Username", "folder": "Folder", - "tags": "Tagi", - "pin": "Przypnij", - "addHost": "Dodaj hosta", - "editHost": "Edytuj hosta", - "cloneHost": "Klonuj hosta", - "enableTerminal": "Włącz terminal", - "enableTunnel": "Włącz tunel", - "enableFileManager": "Włącz menedżera plików", - "enableDocker": "Włącz Docker", - "defaultPath": "Domyślna ścieżka", - "connection": "Połączenie", - "upload": "Prześlij", - "authentication": "Uwierzytelnianie", - "password": "Hasło", - "key": "Klucz", - "credential": "Dane logowania", - "none": "Brak", - "sshPrivateKey": "Klucz prywatny SSH", - "keyType": "Typ klucza", - "uploadFile": "Prześlij plik", - "tabGeneral": "Ogólny", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunele", - "tabDocker": "Dokujący", - "tabFiles": "Pliki", - "tabStats": "Statystyki", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Udostępnianie", - "tabAuthentication": "Uwierzytelnianie", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Tunel", - "fileManager": "Menedżer plików", - "serverStats": "Statystyki serwera", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Katalog \"{{oldName}}\" zmieniono na \"{{newName}}\" pomyślnie", - "failedToRenameFolder": "Nie udało się zmienić nazwy folderu", - "movedToFolder": "Przeniesiono do \"{{folder}}\"", - "editHostTooltip": "Edytuj hosta", - "statusChecks": "Sprawdzanie statusu", - "metricsCollection": "Kolekcja metryk", - "metricsInterval": "Interwał kolekcji liczników", - "metricsIntervalDesc": "Jak często zbierać statystyki serwera (5s - 1h)", - "behavior": "Zachowanie", - "themePreview": "Podgląd motywu", - "theme": "Motyw", - "fontFamily": "Rodzina czcionek", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Odstępy między literami", - "lineHeight": "Wysokość linii", - "cursorStyle": "Styl kursora", - "cursorBlink": "Miganie kursora", - "scrollbackBuffer": "Bufor przewijania", - "bellStyle": "Styl bella", - "rightClickSelectsWord": "Kliknij prawym przyciskiem myszy na wybranie słowa", - "fastScrollModifier": "Modyfikator szybkiego przewijania", - "fastScrollSensitivity": "Czułość szybkiego przewijania", - "sshAgentForwarding": "Przekazywanie SSH Agent", - "backspaceMode": "Tryb Backspace", - "startupSnippet": "Snippet startowy", - "selectSnippet": "Wybierz fragment", - "forceKeyboardInteractive": "Wymuś Interaktywną Klawiaturę", - "overrideCredentialUsername": "Zastąp nazwę użytkownika", - "overrideCredentialUsernameDesc": "Użyj nazwy użytkownika podanej powyżej zamiast nazwy użytkownika poświadczenia", - "jumpHostChain": "Skok hosta skoku", - "portKnocking": "Odrzucenie portu", - "addKnock": "Dodaj port", - "addProxyNode": "Dodaj węzeł", - "proxyNode": "Węzeł proxy", - "proxyType": "Typ serwera proxy", - "quickActions": "Szybkie akcje", - "sudoPasswordAutoFill": "Automatyczne wypełnienie hasła Sudo", - "sudoPassword": "Hasło Sudo", - "keepaliveInterval": "Interwał Keepalive (ms)", - "moshCommand": "Polecenie MOSH", - "environmentVariables": "Zmienne środowiskowe", - "addVariable": "Dodaj zmienną", - "docker": "Dokujący", - "copyTerminalUrl": "Skopiuj adres URL terminalu", - "copyFileManagerUrl": "Skopiuj URL menedżera plików", - "copyRemoteDesktopUrl": "Skopiuj adres URL zdalnego pulpitu", - "failedToConnect": "Nie udało się połączyć z konsolą", - "connect": "Połącz", - "disconnect": "Rozłącz", - "start": "Rozpocznij", - "enableStatusCheck": "Włącz sprawdzanie statusu", - "enableMetrics": "Włącz metryki", - "bulkUpdateFailed": "Aktualizacja zbiorcza nie powiodła się", - "selectAll": "Zaznacz wszystko", - "deselectAll": "Odznacz wszystkie", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Bezpieczna powłoka", - "virtualNetwork": "Sieć wirtualna", - "unencryptedShell": "Niezaszyfrowana powłoka", - "addressIp": "Adres / IP", - "friendlyName": "Nazwa przyjacielska", - "folderAndAdvanced": "Folder & Zaawansowane", - "privateNotes": "Prywatne notatki", - "privateNotesPlaceholder": "Szczegóły dotyczące tego serwera...", - "pinToTop": "Przypnij do góry", - "pinToTopDesc": "Zawsze pokazuj ten host na górze listy", - "portKnockingSequence": "Sekwencja odrzucania portów", - "addKnockBtn": "Dodaj Knock", - "noPortKnocking": "Nie skonfigurowano odrzucania portów.", - "knockPort": "Port Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Opóźnienie po (ms)", - "useSocks5Proxy": "Użyj serwera proxy SOCKS5", - "useSocks5ProxyDesc": "Trasa połączenia przez serwer proxy", - "proxyHost": "Serwer proxy", - "proxyPort": "Port serwera proxy", - "proxyUsername": "Nazwa użytkownika proxy", - "proxyPassword": "Hasło serwera proxy", - "proxySingleMode": "Pojedyncze proxy", - "proxyChainMode": "Łańcuch proxy", - "you": "Ty", - "jumpHostChainLabel": "Skok hosta skoku", - "addJumpBtn": "Dodaj skok", - "noJumpHosts": "Brak skonfigurowanych hostów skoku.", - "selectAServer": "Wybierz serwer...", - "sshPort": "Port SSH", - "authMethod": "Metoda uwierzytelniania", - "storedCredential": "Przechowywane poświadczenia", - "selectACredential": "Wybierz poświadczenie...", - "keyTypeLabel": "Typ klucza", - "keyTypeAuto": "Automatyczne wykrywanie", - "keyPasteTab": "Wklej", - "keyUploadTab": "Prześlij", - "keyFileLoaded": "Plik klucza załadowany", - "keyUploadClick": "Kliknij, aby wgrać .pem / .key / .ppk", - "clearKey": "Wyczyść klucz", - "keySaved": "Klucz SSH zapisany", - "keyReplaceNotice": "wklej nowy klucz poniżej, aby zastąpić go", - "keyPassphraseSaved": "Hasło zapisane, wpisz, aby zmienić", - "replaceKey": "Zamień klucz", - "forceKeyboardInteractiveLabel": "Wymuś Interaktywną Klawiaturę", - "forceKeyboardInteractiveShortDesc": "Wymuś ręczne wprowadzanie hasła, nawet jeśli istnieją klucze", - "terminalAppearance": "Wygląd terminalu", - "colorTheme": "Kolor motywu", - "fontFamilyLabel": "Rodzina czcionek", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Styl kursora", - "letterSpacingPx": "Odstępy między literami (px)", - "lineHeightLabel": "Wysokość linii", - "bellStyleLabel": "Styl bella", - "backspaceModeLabel": "Tryb Backspace", - "cursorBlinking": "Miganie kursora", - "cursorBlinkingDesc": "Włącz animację migania dla kursora terminala", - "rightClickSelectsWordLabel": "Kliknij prawym przyciskiem myszy na wybranie słowa", - "rightClickSelectsWordShortDesc": "Wybierz słowo pod kursorem po prawym kliknięciu", - "behaviorAndAdvanced": "Zachowanie i zaawansowane", - "scrollbackBufferLabel": "Bufor przewijania", - "scrollbackMaxLines": "Maksymalna liczba linii utrzymywanych w historii", - "sshAgentForwardingLabel": "Przekazywanie SSH Agent", - "sshAgentForwardingShortDesc": "Przekaż swoje lokalne klucze SSH do tego hosta", - "enableAutoMosh": "Włącz Auto-Mosh", - "enableAutoMoshDesc": "Preferuj Moosh nad SSH, jeśli jest dostępny", - "enableAutoTmux": "Włącz Auto-Tmux", - "enableAutoTmuxDesc": "Automatycznie uruchom lub dołącz do sesji tmux", - "sudoPasswordAutoFillLabel": "Autouzupełnianie hasła Sudo", - "sudoPasswordAutoFillShortDesc": "Automatycznie podaj hasło sudo gdy zostaniesz o to poproszony", - "sudoPasswordLabel": "Hasło Sudo", - "environmentVariablesLabel": "Zmienne środowiskowe", - "addVariableBtn": "Dodaj zmienną", - "noEnvVars": "Brak skonfigurowanych zmiennych środowiskowych.", - "fastScrollModifierLabel": "Modyfikator szybkiego przewijania", - "fastScrollSensitivityLabel": "Czułość szybkiego przewijania", - "moshCommandLabel": "Dowództwo Mosh", - "startupSnippetLabel": "Snippet startowy", - "keepaliveIntervalLabel": "Interwał utrzymywania połączenia (sekundy)", - "maxKeepaliveMisses": "Maks. ilość pominięć Keepalive", - "tunnelSettings": "Ustawienia tunelu", - "enableTunneling": "Włącz Tunelowanie", - "enableTunnelingDesc": "Włącz funkcję tunelu SSH dla tego hosta", - "serverTunnelsSection": "Tunele serwera", - "addTunnelBtn": "Dodaj Tunel", - "noTunnelsConfigured": "Brak skonfigurowanych tuneli.", - "tunnelLabel": "Tunel {{number}}", - "tunnelType": "Typ tunelu", - "tunnelModeLocalDesc": "Przekieruj port lokalny do portu na zdalnym serwerze (lub hosta osiągalnego z niego).", - "tunnelModeRemoteDesc": "Przekieruj port na zdalnym serwerze z powrotem do lokalnego portu na komputerze.", - "tunnelModeDynamicDesc": "Utwórz proxy SOCKS5 na lokalnym porcie dla dynamicznego przekazywania portu.", - "sameHost": "Ten host (tunel bezpośredni)", - "endpointHost": "Host punktu końcowego", - "endpointPort": "Port punktu końcowego", - "bindHost": "Powiąż hosta", - "sourcePort": "Port źródłowy", - "maxRetries": "Maksymalna liczba prób", - "retryIntervalS": "Ponów interwał (s)", - "autoStartLabel": "Automatyczne uruchamianie", - "autoStartDesc": "Automatycznie podłącz ten tunel po załadowaniu hosta", - "tunnelConnecting": "Łączenie tunelu...", - "tunnelDisconnected": "Tunel odłączony", - "failedToConnectTunnel": "Nie udało się połączyć", - "failedToDisconnectTunnel": "Nie udało się odłączyć", - "dockerIntegration": "Integracja z Dockerem", - "enableDockerMonitor": "Włącz Docker", - "enableDockerMonitorDesc": "Monitoruj kontenery na tym serwerze za pośrednictwem Dockera i zarządzaj nimi", - "enableFileManagerMonitor": "Włącz menedżera plików", - "enableFileManagerMonitorDesc": "Przeglądaj i zarządzaj plikami tego hosta przez SFTP", - "defaultPathLabel": "Domyślna ścieżka", - "fileManagerPathHint": "Katalog do otwarcia po uruchomieniu menedżera plików dla tego hosta.", - "statusChecksLabel": "Sprawdzanie statusu", - "enableStatusChecks": "Włącz sprawdzanie statusu", - "enableStatusChecksDesc": "Okresowo ping tego hosta aby zweryfikować dostępność", - "useGlobalInterval": "Użyj Globalnego Interwału", - "useGlobalIntervalDesc": "Zastąp całym serwerem okresem sprawdzania stanu", - "checkIntervalS": "Sprawdź interwał (s)", - "checkIntervalDesc": "Sekundy między każdym pingiem łączności", - "metricsCollectionLabel": "Kolekcja metryk", - "enableMetricsLabel": "Włącz metryki", - "enableMetricsDesc": "Zbieraj CPU, RAM, dysk i korzystanie z sieci z tego hosta", - "useGlobalMetrics": "Użyj Globalnego Interwału", - "useGlobalMetricsDesc": "Zastąp interwałem metrycznym dla całego serwera", - "metricsIntervalS": "Interwał (interwalenty) metryki", - "metricsIntervalDesc2": "Sekund między migawkami metrycznymi", - "visibleWidgets": "Widoczne widżety", - "cpuUsageLabel": "Użycie procesora", - "cpuUsageDesc": "Procent procesora, średnie obciążenia, wykres linii sparkline", - "memoryLabel": "Użycie pamięci", - "memoryDesc": "Zużycie pamięci RAM, zamiana, pamięć podręczna", - "storageLabel": "Użycie dysku", - "storageDesc": "Użycie dysku na punkt montowania", - "networkLabel": "Interfejsy sieciowe", - "networkDesc": "Lista interfejsów i szerokość pasma", - "uptimeLabel": "Czas pracy", - "uptimeDesc": "Systemowy czas pracy i czas rozruchu", - "systemInfoLabel": "Informacje o systemie", - "systemInfoDesc": "System operacyjny, jąder, nazwa hosta, architektura", - "recentLoginsLabel": "Ostatnie logowania", - "recentLoginsDesc": "Pomyślne i nieudane logowanie", - "topProcessesLabel": "Najlepsze procesy", - "topProcessesDesc": "PID, CPU%, MEM%, komenda", - "listeningPortsLabel": "Porty słuchające", - "listeningPortsDesc": "Otwieraj porty z procesem i stanem", - "firewallLabel": "Zapora", - "firewallDesc": "Firewall, AppArmor, status SELinux", - "quickActionsLabel": "Szybkie akcje", - "quickActionsToolbar": "Szybkie akcje pojawiają się jako przyciski na pasku statystyk serwera do wykonania poleceń jednego kliknięcia.", - "noQuickActions": "Brak szybkich akcji.", - "buttonLabel": "Etykieta przycisku", - "selectSnippetPlaceholder": "Wybierz snippet...", - "addActionBtn": "Dodaj akcję", - "hostSharedSuccessfully": "Host udostępniony pomyślnie", - "failedToShareHost": "Nie udało się udostępnić hosta", - "accessRevoked": "Dostęp odwołany", - "failedToRevokeAccess": "Nie udało się odwołać dostępu", - "cancelBtn": "Anuluj", - "savingBtn": "Zapisywanie...", - "addHostBtn": "Dodaj hosta", - "hostUpdated": "Host zaktualizowany", - "hostCreated": "Host utworzony", - "failedToSave": "Nie udało się zapisać hosta", - "credentialUpdated": "Zaktualizowano poświadczenia", - "credentialCreated": "Utworzono poświadczenia", - "failedToSaveCredential": "Nie można zapisać danych logowania", - "backToHosts": "Powrót do hostów", - "backToCredentials": "Wróć do danych logowania", - "pinned": "Przypięte", - "noHostsFound": "Nie znaleziono hostów", - "tryDifferentTerm": "Wypróbuj inne wyrażenie", - "addFirstHost": "Dodaj swojego pierwszego hosta, aby rozpocząć", - "noCredentialsFound": "Nie znaleziono danych logowania", - "addCredentialBtn": "Dodaj poświadczenia", - "updateCredentialBtn": "Aktualizuj dane logowania", - "features": "Funkcje", - "noFolder": "(Brak folderu)", - "deleteSelected": "Usuń", - "exitSelection": "Wyjdź z zaznaczenia", - "importSkip": "Import (pomiń istniejące)", - "importOverwrite": "Import (nadpisanie)", - "collapseBtn": "Zwiń", - "importExportBtn": "Import / Eksport", - "hostStatusesRefreshed": "Statusy hosta odświeżone", - "failedToRefreshHosts": "Nie udało się odświeżyć hostów", - "movedHostTo": "Przeniesiono {{host}} do \"{{folder}}\"", - "failedToMoveHost": "Nie udało się przenieść hosta", - "folderRenamedTo": "Zmieniono nazwę folderu na \"{{name}}\"", - "deletedFolder": "Usunięto folder \"{{name}}\"", - "failedToDeleteFolder": "Nie udało się usunąć folderu", - "deleteAllInFolder": "Usunąć wszystkie hosty w \"{{name}}\"? Tej operacji nie można cofnąć.", - "deletedHost": "Usunięto {{name}}", - "copiedToClipboard": "Skopiowano do schowka", - "terminalUrlCopied": "Adres URL terminalu skopiowany", - "fileManagerUrlCopied": "Adres URL menedżera plików skopiowany", - "tunnelUrlCopied": "Adres URL tunelu skopiowany", - "dockerUrlCopied": "Skopiowano URL dokera", - "serverStatsUrlCopied": "Skopiowano URL statystyk serwera", - "rdpUrlCopied": "URL RDP skopiowany", - "vncUrlCopied": "Adres URL VNC skopiowany", - "telnetUrlCopied": "Skopiowano adres URL Telnet", - "remoteDesktopUrlCopied": "Adres URL zdalnego pulpitu skopiowany", - "expandActions": "Rozwiń działania", - "collapseActions": "Zwiń akcje", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Pakiet magiczny wysłany do {{name}}", - "wakeOnLanError": "Nie udało się wysłać pakietu magicznego", - "cloneHostAction": "Klonuj hosta", - "copyAddress": "Kopiuj adres", - "copyLink": "Kopiuj link", - "copyTerminalUrlAction": "Skopiuj adres URL terminalu", - "copyFileManagerUrlAction": "Skopiuj URL menedżera plików", - "copyTunnelUrlAction": "Skopiuj adres URL tunelu", - "copyDockerUrlAction": "Skopiuj URL dokera", - "copyServerStatsUrlAction": "Skopiuj adres URL statystyk serwera", - "copyRdpUrlAction": "Kopiuj adres URL RDP", - "copyVncUrlAction": "Kopiuj URL VNC", - "copyTelnetUrlAction": "Kopiuj adres Telnet", - "copyRemoteDesktopUrlAction": "Skopiuj adres URL zdalnego pulpitu", - "deleteCredentialConfirm": "Usunąć dane logowania \"{{name}}\"?", - "deletedCredential": "Usunięto {{name}}", - "deploySSHKeyTitle": "Wdrożenie klucza SSH", - "deployingBtn": "Wdrażanie...", - "deployBtn": "Wdrożenie", - "failedToDeployKey": "Nie udało się wdrożyć klucza", - "deleteHostsConfirm": "Usunąć hosta {{count}}{{plural}}? Tej operacji nie można cofnąć.", - "movedToRoot": "Przeniesiono do roota", - "failedToMoveHosts": "Nie udało się przenieść hostów", - "enableTerminalFeature": "Włącz terminal", - "disableTerminalFeature": "Wyłącz Terminal", - "enableFilesFeature": "Włącz pliki", - "disableFilesFeature": "Wyłącz pliki", - "enableTunnelsFeature": "Włącz tunele", - "disableTunnelsFeature": "Wyłącz tunele", - "enableDockerFeature": "Włącz Docker", - "disableDockerFeature": "Wyłącz Docker", - "addTagsPlaceholder": "Dodaj tagi...", - "authDetails": "Szczegóły uwierzytelniania", - "credType": "Typ", - "generateKeyPairDesc": "Wygeneruj nową parę kluczy, klucze prywatne i publiczne będą wypełniane automatycznie.", - "generatingKey": "Generowanie...", - "generateLabel": "Generuj {{label}}", - "uploadFileBtn": "Prześlij plik", - "keyPassphraseOptional": "Hasło klucza (opcjonalne)", - "sshPublicKeyOptional": "Klucz publiczny SSH (opcjonalnie)", - "publicKeyGenerated": "Klucz publiczny wygenerowany", - "failedToGeneratePublicKey": "Nie udało się uzyskać klucza publicznego", - "publicKeyCopied": "Klucz publiczny skopiowany", - "keyPairGenerated": "Wygenerowano parę kluczy {{label}}", - "failedToGenerateKeyPair": "Nie udało się wygenerować pary kluczy", - "searchHostsPlaceholder": "Szukaj hostów, adresów, tagów…", - "searchCredentialsPlaceholder": "Szukaj poświadczeń…", - "refreshBtn": "Odśwież", - "addTag": "Dodaj tagi...", - "deleteConfirmBtn": "Usuń", - "tunnelRequirementsText": "Serwer SSH musi mieć GatewayPorts Tak, AllowTcpForwarding Tak i PermitRootLogin ustawione w /etc/ssh/sshd_config.", - "deleteHostConfirm": "Usunąć \"{{name}}\"?", - "enableAtLeastOneProtocol": "Włącz co najmniej jeden protokół powyżej, aby skonfigurować ustawienia uwierzytelniania i połączenia.", - "keyPassphrase": "Hasło klawisza", - "connectBtn": "Połącz", - "disconnectBtn": "Rozłącz", - "basicInformation": "Podstawowe informacje", - "authDetailsSection": "Szczegóły uwierzytelniania", - "credTypeLabel": "Typ", - "hostsTab": "Hosty", - "credentialsTab": "Dane logowania", - "selectMultiple": "Wybierz wiele", - "selectHosts": "Wybierz hosty", - "connectionLabel": "Połączenie", - "authenticationLabel": "Uwierzytelnianie", - "generateKeyPairTitle": "Wygeneruj parę kluczy", - "generateKeyPairDescription": "Wygeneruj nową parę kluczy, klucze prywatne i publiczne będą wypełniane automatycznie.", - "generateFromPrivateKey": "Wygeneruj z klucza prywatnego", - "refreshBtn2": "Odśwież", - "exitSelectionTitle": "Wyjdź z zaznaczenia", - "exportAll": "Eksportuj wszystko", - "addHostBtn2": "Dodaj hosta", - "addCredentialBtn2": "Dodaj poświadczenia", - "checkingHostStatuses": "Sprawdzanie statusów hosta...", - "pinnedSection": "Przypięte", - "hostsExported": "Hosty wyeksportowano pomyślnie", - "exportFailed": "Nie udało się wyeksportować hostów", - "sampleDownloaded": "Pobrano przykładowy plik", - "failedToDeleteCredential2": "Nie udało się usunąć danych logowania", - "noFolderOption": "(Brak folderu)", - "nSelected": "Wybrano {{count}}", - "featuresMenu": "Funkcje", - "moveMenu": "Przenieś", - "cancelSelection": "Anuluj", - "deployDialogDesc": "Wdroż {{name}} do autoryzowanych kluczy hosta.", - "targetHostLabel": "Host docelowy", - "selectHostOption": "Wybierz host...", - "keyDeployedSuccess": "Klucz został pomyślnie wdrożony", - "failedToDeployKey2": "Nie udało się wdrożyć klucza", - "deletedCount": "Usunięto hosty {{count}}", - "failedToDeleteCount": "Nie udało się usunąć hostów {{count}}", - "duplicatedHost": "Duplikowane \"{{name}}\"", - "failedToDuplicateHost": "Nie można zduplikować hosta", - "updatedCount": "Zaktualizowano hosty {{count}}", - "friendlyNameLabel": "Nazwa przyjacielska", - "descriptionLabel": "Opis", - "loadingHost": "Ładowanie hosta...", - "loadingHosts": "Ładowanie hostów...", - "loadingCredentials": "Ładowanie poświadczeń...", - "noHostsYet": "Brak hostów", - "noHostsMatchSearch": "Brak hostów pasujących do Twojego wyszukiwania", - "hostNotFound": "Nie znaleziono hosta", - "searchHosts": "Szukaj hostów...", - "sortHosts": "Sortuj hosty", - "sortDefault": "Domyślna kolejność", - "sortNameAsc": "Imię (A → Z)", - "sortNameDesc": "Imię (Z → A)", - "sortIpAsc": "Adres IP (rosnąco)", - "sortIpDesc": "Adres IP (opis)", - "sortOnlineFirst": "Pierwszy online", - "sortOfflineFirst": "Najpierw offline", - "sortPinnedFirst": "Przypięte jako pierwsze", - "filterHosts": "Filtruj hosty", - "filterClearAll": "Wyczyść filtry", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "W sieci", - "filterOffline": "Niedostępny", - "filterPinned": "Przypięte", - "filterAuthGroup": "Typ autoryzacji", - "filterAuthPassword": "Hasło", - "filterAuthKey": "Klucz SSH", - "filterAuthCredential": "Mandat", - "filterAuthNone": "Nic", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokół", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Cechy", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Menedżer plików", - "filterFeatureTunnel": "Tunel", - "filterFeatureDocker": "Doker", - "filterTagsGroup": "Tagi", - "shareHost": "Udostępnij hosta", - "shareHostTitle": "Udostępnianie: {{name}}", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Ten host musi użyć danych logowania, aby włączyć udostępnianie. Edytuj host i najpierw przypisz dane logowania." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Połączenie", - "authentication": "Uwierzytelnianie", - "connectionSettings": "Ustawienia połączenia", - "displaySettings": "Ustawienia wyświetlania", - "audioSettings": "Ustawienia audio", - "rdpPerformance": "Wydajność RDP", - "deviceRedirection": "Przekierowanie urządzenia", - "session": "Sesja", - "gateway": "Brama", - "remoteApp": "Zdalna aplikacja", - "clipboard": "Schowek", - "sessionRecording": "Nagrywanie sesji", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Ustawienia VNC", - "terminalSettings": "Ustawienia terminalu", - "rdpPort": "Port RDP", - "username": "Nazwa użytkownika", - "password": "Hasło", - "domain": "Domena", - "securityMode": "Tryb bezpieczeństwa", - "colorDepth": "Głębokość koloru", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Wysokość", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Metoda zmiany rozmiaru", - "clientName": "Nazwa klienta", - "initialProgram": "Program początkowy", - "serverLayout": "Układ serwera", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Port bramy", - "gatewayUsername": "Nazwa użytkownika bramy", - "gatewayPassword": "Hasło bramki", - "gatewayDomain": "Domena bramy", - "remoteAppProgram": "Program zdalnej aplikacji", - "workingDirectory": "Katalog roboczy", - "arguments": "Argumenty", - "normalizeLineEndings": "Normalizuj zakończenie linii", - "recordingPath": "Ścieżka nagrywania", - "recordingName": "Nazwa nagrania", - "macAddress": "Adres MAC", - "broadcastAddress": "Adres nadawania", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Czas oczekiwania (s)", - "driveName": "Nazwa napędu", - "drivePath": "Ścieżka jazdy", - "ignoreCertificate": "Ignoruj certyfikat", - "ignoreCertificateDesc": "Zezwalaj na połączenia z hostami z własnoręcznymi certyfikatami", - "forceLossless": "Wymuś bezstratne", - "forceLosslessDesc": "Wymuś kodowanie obrazów bez strat (wyższa jakość, większa przepustowość)", - "disableAudio": "Wyłącz dźwięk", - "disableAudioDesc": "Wycisz wszystkie dźwięki z sesji zdalnej", - "enableAudioInput": "Włącz wejście audio (mikrofon)", - "enableAudioInputDesc": "Przekieruj lokalny mikrofon do sesji zdalnej", - "wallpaper": "Tapeta", - "wallpaperDesc": "Pokaż tapetę pulpitu (wyłączenie poprawia wydajność)", - "theming": "Motyw", - "themingDesc": "Włącz wizualne motywy i style", - "fontSmoothing": "Wygładzanie czcionki", - "fontSmoothingDesc": "Włącz renderowanie czcionki ClearType", - "fullWindowDrag": "Przeciąganie pełnego okna", - "fullWindowDragDesc": "Pokaż zawartość okna podczas przeciągania", - "desktopComposition": "Kompozycja pulpitu", - "desktopCompositionDesc": "Włącz efekty ze szkła Aero", - "menuAnimations": "Animacje menu", - "menuAnimationsDesc": "Włącz animacje zanikania i slajdu menu", - "disableBitmapCaching": "Wyłącz buforowanie bitmap", - "disableBitmapCachingDesc": "Wyłącz pamięć podręczną mapy (może pomóc z glitchami)", - "disableOffscreenCaching": "Wyłącz buforowanie", - "disableOffscreenCachingDesc": "Wyłącz pamięć podręczną", - "disableGlyphCaching": "Wyłącz buforowanie glifów", - "disableGlyphCachingDesc": "Wyłącz cache glifów", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Użyj potoku Zdalne Grafiki", - "enablePrinting": "Włącz drukowanie", - "enablePrintingDesc": "Przekierowanie lokalnych drukarek na sesję zdalną", - "enableDriveRedirection": "Włącz przekierowanie na dysku", - "enableDriveRedirectionDesc": "Mapuj lokalny folder jako dysk w sesji zdalnej", - "createDrivePath": "Utwórz ścieżkę napędu", - "createDrivePathDesc": "Automatycznie utwórz folder jeśli nie istnieje", - "disableDownload": "Wyłącz pobieranie", - "disableDownloadDesc": "Zapobiegaj pobieraniu plików z sesji zdalnej", - "disableUpload": "Wyłącz wysyłanie", - "disableUploadDesc": "Zapobiegaj przesyłaniu plików na sesję zdalną", - "enableTouch": "Włącz dotyk", - "enableTouchDesc": "Włącz przekazywanie danych dotykowych", - "consoleSession": "Sesja konsoli", - "consoleSessionDesc": "Połącz z konsolą (sesja 0) zamiast z nową sesją", - "sendWolPacket": "Wyślij pakiet WOL", - "sendWolPacketDesc": "Wyślij magiczny pakiet do obudzenia tego hosta przed połączeniem", - "disableCopy": "Wyłącz kopiowanie", - "disableCopyDesc": "Zapobiegaj kopiowaniu tekstu z sesji zdalnej", - "disablePaste": "Wyłącz wklej", - "disablePasteDesc": "Zapobiegaj wklejaniu tekstu do sesji zdalnej", - "createPathIfMissing": "Utwórz ścieżkę jeśli nie ma", - "createPathIfMissingDesc": "Automatycznie utwórz katalog nagrywania", - "excludeOutput": "Wyklucz dane wyjściowe", - "excludeOutputDesc": "Nie nagrywaj wyjścia ekranu (tylko metadane)", - "excludeMouse": "Wyklucz mysz", - "excludeMouseDesc": "Nie rejestruj ruchów myszy", - "includeKeystrokes": "Dołącz przyciski", - "includeKeystrokesDesc": "Nagrywaj przyciski klawiszowe poza wyjściem ekranu", - "vncPort": "Port VNC", - "vncPassword": "Hasło VNC", - "vncUsernameOptional": "Nazwa użytkownika (opcjonalnie)", - "vncLeaveBlank": "Pozostaw puste, jeśli nie jest wymagane", - "cursorMode": "Tryb kursora", - "swapRedBlue": "Zamień Czerwony/Niebieski", - "swapRedBlueDesc": "Zamień kanały czerwonego i niebieskiego koloru (naprawia niektóre problemy z kolorami)", - "readOnly": "Tylko do odczytu", - "readOnlyDesc": "Zobacz zdalny ekran bez wysyłania żadnych danych wejściowych", - "telnetPort": "Port Telnet", - "terminalType": "Typ terminalu", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Schemat kolorów", - "backspaceKey": "Klucz Backspace", - "saveHostFirst": "Najpierw zapisz hosta.", - "sharingOptionsAfterSave": "Opcje udostępniania są dostępne po zapisaniu hosta.", - "sharingLoadError": "Nie udało się załadować danych udostępniania. Sprawdź swoje połączenie i spróbuj ponownie.", - "shareHostSection": "Udostępnij hosta", - "shareWithUser": "Udostępnij użytkownikowi", - "shareWithRole": "Udostępnij z rolą", - "selectUser": "Wybierz użytkownika", - "selectRole": "Wybierz rolę", - "selectUserOption": "Wybierz użytkownika...", - "selectRoleOption": "Wybierz rolę...", - "permissionLevel": "Poziom uprawnień", - "expiresInHours": "Wygasa za (godziny)", - "noExpiryPlaceholder": "Pozostaw puste bez wygaśnięcia", - "shareBtn": "Udostępnij", - "currentAccess": "Bieżący dostęp", - "typeHeader": "Typ", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Uprawnienie", - "grantedByHeader": "Przyznane przez", - "expiresHeader": "Wygasa", - "noAccessEntries": "Brak wpisów dostępu.", - "expiredLabel": "Wygasły", - "neverLabel": "Nigdy", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Anuluj", - "savingBtn": "Zapisywanie...", - "updateHostBtn": "Aktualizacja hosta", - "addHostBtn": "Dodaj hosta" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Szukaj hostów, poleceń lub ustawień...", - "quickActions": "Szybkie akcje", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", "hostManager": "Host Manager", - "hostManagerDesc": "Zarządzaj, dodaj lub edytuj hosty", - "addNewHost": "Dodaj nowego hosta", - "addNewHostDesc": "Zarejestruj nowy host", - "adminSettings": "Ustawienia administratora", - "adminSettingsDesc": "Skonfiguruj ustawienia systemowe i użytkowników", - "userProfile": "Profil użytkownika", - "userProfileDesc": "Zarządzaj kontem i preferencjami", - "addCredential": "Dodaj poświadczenia", - "addCredentialDesc": "Przechowuj klucze SSH lub hasła", - "recentActivity": "Ostatnia aktywność", - "serversAndHosts": "Serwery i hosty", - "noHostsFound": "Nie znaleziono hostów pasujących do \"{{search}}\"", - "links": "Linki", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Wybierz", - "toggleWith": "Przełącz z" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Brak przypisanych kart", - "focusedPane": "Aktywny panel" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Brak połączeń", - "noConnectionsDesc": "Otwórz terminal, menedżera plików lub pulpit zdalny, aby zobaczyć połączenia tutaj", - "connectedFor": "Połączono dla {{duration}}", - "connected": "Połączony", - "disconnected": "Bezładny", - "closeTab": "Zamknij kartę", - "closeConnection": "Bliskie połączenie", - "forgetTab": "Zapominać", - "removeBackground": "Usunąć", - "reconnect": "Na nowo połączyć", - "reopenTab": "Otworzyć na nowo", - "sectionOpen": "Otwarte", - "sectionBackground": "Tło", - "backgroundDesc": "Sesje trwają przez 30 minut od momentu rozłączenia, po czym można je ponownie nawiązać.", - "persisted": "Trwały w tle", - "expiresIn": "Wygasa za {{duration}}", - "search": "Wyszukaj połączenia...", - "noSearchResults": "Brak połączeń odpowiadających Twojemu wyszukiwaniu" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Łączenie z sesją {{type}}...", - "connectionError": "Błąd połączenia", - "connectionFailed": "Połączenie nie powiodło się", - "failedToConnect": "Nie udało się uzyskać tokenu połączenia", - "hostNotFound": "Nie znaleziono hosta", - "noHostSelected": "Nie wybrano hosta", - "reconnect": "Połącz ponownie", - "retry": "Ponów próbę", - "guacdUnavailable": "Usługa zdalnego pulpitu (guacd) nie jest dostępna. Upewnij się, że guacd jest uruchomiony i dostępny oraz skonfigurowany poprawnie w ustawieniach administratora.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Zwycięstwo+L (ekran stopniowy)", - "winKey": "Klucz Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Zmiana", - "win": "Wygraj", - "stickyActive": "{{key}} (zamknięty - kliknij aby wydać)", - "stickyInactive": "{{key}} (kliknij, aby zamknąć)", - "esc": "Ucieczka", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Strona główna", - "end": "Koniec", - "pageUp": "Strona w górę", - "pageDown": "Strona w dół", - "arrowUp": "Strzałka w górę", - "arrowDown": "Strzałka w dół", - "arrowLeft": "Strzałka w lewo", - "arrowRight": "Strzałka w prawo", - "fnToggle": "Klucze funkcji", - "reconnect": "Połącz ponownie sesję", - "collapse": "Zwiń pasek narzędzi", - "expand": "Rozwiń pasek narzędzi", - "dragHandle": "Przeciągnij, aby zmienić położenie" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Połącz z hostem", - "clear": "Wyczyść", - "paste": "Wklej", - "reconnect": "Połącz ponownie", - "connectionLost": "Utracono połączenie", - "connected": "Połączono", - "clipboardWriteFailed": "Nie udało się skopiować do schowka. Upewnij się, że strona jest obsługiwana przez HTTPS lub localhost.", - "clipboardReadFailed": "Nie udało się odczytać ze schowka. Upewnij się, że uprawnienia do schowka są przyznane.", - "clipboardHttpWarning": "Wklej wymaga HTTPS. Użyj Ctrl+Shift+V lub podaj Termix przez HTTPS.", - "unknownError": "Wystąpił nieznany błąd", - "websocketError": "Błąd połączenia WebSocket", - "connecting": "Łączenie...", - "noHostSelected": "Nie wybrano hosta", - "reconnecting": "Ponowne łączenie... ({{attempt}}/{{max}})", - "reconnected": "Ponowne połączenie zakończone pomyślnie", - "tmuxSessionCreated": "Sesja tmux utworzona: {{name}}", - "tmuxSessionAttached": "Dołączono sesję tmuxa: {{name}}", - "tmuxUnavailable": "tmux nie jest zainstalowany na zdalnym serwerze, powrót do standardowej powłoki", - "tmuxSessionPickerTitle": "Sesje tmux", - "tmuxSessionPickerDesc": "Znaleziono istniejące sesje tmux na tym serwerze. Wybierz jedną, aby ponowić lub utworzyć nową sesję.", - "tmuxWindows": "Okna", - "tmuxWindowCount": "Okno {{count}}", - "tmuxAttached": "Dołączone klienci", - "tmuxAttachedCount": "{{count}} dołączony", - "tmuxLastActivity": "Ostatnia aktywność", - "tmuxTimeJustNow": "właśnie teraz", - "tmuxTimeMinutes": "{{count}}m temu", - "tmuxTimeHours": "{{count}}h temu", - "tmuxTimeDays": "{{count}}dni temu", - "tmuxCreateNew": "Rozpocznij nową sesję", - "tmuxCopyHint": "Dostosuj zaznaczenie i naciśnij Enter, aby skopiować do schowka", - "tmuxDetach": "Odłącz od sesji tmux", - "tmuxDetached": "Odłączone od sesji tmux", - "maxReconnectAttemptsReached": "Osiągnięto maksymalną liczbę prób ponownego połączenia", - "closeTab": "Zamknij", - "connectionTimeout": "Limit czasu połączenia", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Uruchamianie {{command}} - {{host}}", - "totpRequired": "Wymagane uwierzytelnianie dwuetapowe", - "totpCodeLabel": "Kod weryfikacyjny", - "totpVerify": "Weryfikacja", - "warpgateAuthRequired": "Wymagane uwierzytelnienie Warpgate", - "warpgateSecurityKey": "Klucz bezpieczeństwa", - "warpgateAuthUrl": "Adres URL uwierzytelniania", - "warpgateOpenBrowser": "Otwórz w przeglądarce", - "warpgateContinue": "Zakończyłem uwierzytelnianie", - "opksshAuthRequired": "Wymagane uwierzytelnienie OPKSSH", - "opksshAuthDescription": "Zakończ uwierzytelnianie w przeglądarce, aby kontynuować. Ta sesja pozostanie ważna przez 24 godziny.", - "opksshOpenBrowser": "Otwórz przeglądarkę do uwierzytelniania", - "opksshWaitingForAuth": "Oczekiwanie na uwierzytelnianie w przeglądarce...", - "opksshAuthenticating": "Przetwarzanie uwierzytelniania...", - "opksshTimeout": "Upłynął limit czasu uwierzytelniania. Spróbuj ponownie.", - "opksshAuthFailed": "Uwierzytelnianie nie powiodło się. Sprawdź swoje poświadczenia i spróbuj ponownie.", - "opksshSignInWith": "Zaloguj się za pomocą {{provider}}", - "sudoPasswordPopupTitle": "Wprowadzić hasło?", - "websocketAbnormalClose": "Połączenie nieoczekiwanie zamknięte. Może to być spowodowane problemem odwrotnego serwera proxy lub konfiguracji SSL. Sprawdź logi serwera.", - "connectionLogTitle": "Dziennik połączeń", - "connectionLogCopy": "Kopiuj logi do schowka", - "connectionLogEmpty": "Brak dzienników połączeń", - "connectionLogWaiting": "Oczekiwanie na logi połączeń...", - "connectionLogCopied": "Dzienniki połączeń skopiowane do schowka", - "connectionLogCopyFailed": "Nie udało się skopiować logów do schowka", - "connectionRejected": "Połączenie odrzucone przez serwer. Sprawdź swoje uwierzytelnianie i konfigurację sieci.", - "hostKeyRejected": "Weryfikacja klucza hosta SSH odrzucona. Połączenie anulowane.", - "sessionTakenOver": "Sesja została otwarta w innej karcie. Ponowne łączenie..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nie wybrano hosta", - "initializingEditor": "Inicjowanie edytora...", - "file": "Plik", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", "folder": "Folder", - "uploadFile": "Prześlij plik", - "downloadFile": "Pobierz", - "extractArchive": "Wyodrębnij archiwum", - "extractingArchive": "Rozpakowywanie {{name}}...", - "archiveExtractedSuccessfully": "{{name}} wypakowano pomyślnie", - "extractFailed": "Wypakowywanie nie powiodło się", - "compressFile": "Kompresuj plik", - "compressFiles": "Kompresuj pliki", - "compressFilesDesc": "Kompresuj elementy {{count}} do archiwum", - "archiveName": "Nazwa archiwum", - "enterArchiveName": "Wprowadź nazwę archiwum...", - "compressionFormat": "Format kompresji", - "selectedFiles": "Wybrane pliki", - "andMoreFiles": "i {{count}} więcej...", - "compress": "Kompresuj", - "compressingFiles": "Kompresja {{count}} przedmiotów do {{name}}...", - "filesCompressedSuccessfully": "{{name}} został pomyślnie utworzony", - "compressFailed": "Kompresja nie powiodła się", - "edit": "Edytuj", - "preview": "Podgląd", - "previous": "Poprzedni", - "next": "Następny", - "pageXOfY": "Strona {{current}} z {{total}}", - "zoomOut": "Pomniejsz", - "zoomIn": "Powiększ", - "newFile": "Nowy plik", - "newFolder": "Nowy folder", - "rename": "Zmień nazwę", - "uploading": "Wgrywanie...", - "uploadingFile": "Przesyłanie {{name}}...", - "fileName": "Nazwa pliku", - "folderName": "Nazwa folderu", - "fileUploadedSuccessfully": "Plik \"{{name}}\" został pomyślnie przesłany", - "failedToUploadFile": "Nie udało się przesłać pliku", - "fileDownloadedSuccessfully": "Plik \"{{name}}\" pobrano pomyślnie", - "failedToDownloadFile": "Nie udało się pobrać pliku", - "fileCreatedSuccessfully": "Plik \"{{name}}\" został utworzony pomyślnie", - "folderCreatedSuccessfully": "Folder \"{{name}}\" utworzony pomyślnie", - "failedToCreateItem": "Nie udało się utworzyć elementu", - "operationFailed": "Operacja {{operation}} nie powiodła się dla {{name}}: {{error}}", - "failedToResolveSymlink": "Nie udało się rozwiązać dowiązania symbolicznego", - "itemsDeletedSuccessfully": "{{count}} elementów pomyślnie usunięto", - "failedToDeleteItems": "Nie udało się usunąć elementów", - "sudoPasswordRequired": "Wymagane hasło administratora", - "enterSudoPassword": "Wprowadź hasło sudo aby kontynuować tę operację", - "sudoPassword": "Hasło Sudo", - "sudoOperationFailed": "Operacja Sudo nie powiodła się", - "sudoAuthFailed": "Uwierzytelnianie Sudo nie powiodło się", - "dragFilesToUpload": "Upuść pliki tutaj, aby przesłać", - "emptyFolder": "Ten folder jest pusty", - "searchFiles": "Szukaj plików...", - "upload": "Prześlij", - "selectHostToStart": "Wybierz hosta do rozpoczęcia zarządzania plikami", - "sshRequiredForFileManager": "Menedżer plików wymaga SSH. Ten host nie ma włączonego SSH.", - "failedToConnect": "Nie udało się połączyć z SSH", - "failedToLoadDirectory": "Nie udało się załadować katalogu", - "noSSHConnection": "Brak dostępnego połączenia SSH", - "copy": "Kopiuj", - "cut": "Wytnij", - "paste": "Wklej", - "copyPath": "Kopiuj ścieżkę", - "copyPaths": "Kopiuj ścieżki", - "delete": "Usuń", - "properties": "Właściwości", - "refresh": "Odśwież", - "downloadFiles": "Pobierz pliki {{count}} do przeglądarki", - "copyFiles": "Kopiuj elementy {{count}}", - "cutFiles": "Wytnij elementy {{count}}", - "deleteFiles": "Usuń elementy {{count}}", - "filesCopiedToClipboard": "{{count}} elementy skopiowane do schowka", - "filesCutToClipboard": "{{count}} elementy przycięte do schowka", - "pathCopiedToClipboard": "Ścieżka skopiowana do schowka", - "pathsCopiedToClipboard": "Ścieżki {{count}} skopiowane do schowka", - "failedToCopyPath": "Nie udało się skopiować ścieżki do schowka", - "movedItems": "Przeniesiono {{count}} przedmiotów", - "failedToDeleteItem": "Nie udało się usunąć elementu", - "itemRenamedSuccessfully": "Zmieniono nazwę {{type}}", - "failedToRenameItem": "Nie udało się zmienić nazwy elementu", - "download": "Pobierz", - "permissions": "Uprawnienia", - "size": "Rozmiar", - "modified": "Zmodyfikowane", - "path": "Ścieżka", - "confirmDelete": "Czy na pewno chcesz usunąć {{name}}?", - "permissionDenied": "Odmowa dostępu", - "serverError": "Błąd serwera", - "fileSavedSuccessfully": "Plik zapisany pomyślnie", - "failedToSaveFile": "Nie udało się zapisać pliku", - "confirmDeleteSingleItem": "Czy na pewno chcesz trwale usunąć \"{{name}}\"?", - "confirmDeleteMultipleItems": "Czy na pewno chcesz trwale usunąć {{count}} elementów?", - "confirmDeleteMultipleItemsWithFolders": "Czy na pewno chcesz trwale usunąć elementy {{count}} ? Obejmuje to foldery i ich zawartość.", - "confirmDeleteFolder": "Czy na pewno chcesz trwale usunąć folder \"{{name}}\" i całą jego zawartość?", - "permanentDeleteWarning": "Tej akcji nie można cofnąć. Element(y) zostaną trwale usunięte z serwera.", - "recent": "Najnowsze", - "pinned": "Przypięte", - "folderShortcuts": "Skróty folderu", - "failedToReconnectSSH": "Nie udało się ponownie połączyć sesji SSH", - "openTerminalHere": "Otwórz terminal tutaj", - "run": "Uruchom", - "openTerminalInFolder": "Otwórz Terminal w tym folderze", - "openTerminalInFileLocation": "Otwórz terminal w lokalizacji pliku", - "runningFile": "Uruchamianie - {{file}}", - "onlyRunExecutableFiles": "Można uruchamiać tylko pliki wykonywalne", - "directories": "Katalogi", - "removedFromRecentFiles": "Usunięto \"{{name}}\" z ostatnich plików", - "removeFailed": "Usunięcie nie powiodło się", - "unpinnedSuccessfully": "Odpięto \"{{name}}\" zakończone powodzeniem", - "unpinFailed": "Nie udało się odpiąć", - "removedShortcut": "Usunięto skrót \"{{name}}\"", - "removeShortcutFailed": "Usunięcie skrótu nie powiodło się", - "clearedAllRecentFiles": "Wyczyszczono wszystkie ostatnie pliki", - "clearFailed": "Wyczyszczenie nie powiodło się", - "removeFromRecentFiles": "Usuń z ostatnich plików", - "clearAllRecentFiles": "Wyczyść wszystkie ostatnie pliki", - "unpinFile": "Odepnij plik", - "removeShortcut": "Usuń skrót", - "pinFile": "Przypnij plik", - "addToShortcuts": "Dodaj do skrótów", - "pasteFailed": "Wklej nie powiodło się", - "noUndoableActions": "Brak niedopuszczalnych działań", - "undoCopySuccess": "Operacja niewykonana: Skopiowano pliki {{count}}", - "undoCopyFailedDelete": "Cofnięcie nie powiodło się: Nie można usunąć żadnych skopiowanych plików", - "undoCopyFailedNoInfo": "Cofnięcie nie powiodło się: Nie można odnaleźć skopiowanych informacji o pliku", - "undoMoveSuccess": "Cofnij przeniesienie operacji: Przeniesiono pliki {{count}} z powrotem do oryginalnej lokalizacji", - "undoMoveFailedMove": "Cofnięcie nie powiodło się: Nie można przenieść żadnych plików z powrotem", - "undoMoveFailedNoInfo": "Cofnięcie nie powiodło się: Nie można odnaleźć przeniesionego pliku", - "undoDeleteNotSupported": "Nie można cofnąć operacji: Pliki zostały trwale usunięte z serwera", - "undoTypeNotSupported": "Nieobsługiwany typ operacji cofania", - "undoOperationFailed": "Cofnięcie operacji nie powiodło się", - "unknownError": "Nieznany błąd", - "confirm": "Potwierdź", - "find": "Znajdź...", - "replace": "Zamień", - "downloadInstead": "Pobierz zamiast", - "keyboardShortcuts": "Skróty klawiaturowe", - "searchAndReplace": "Wyszukaj i zamień", - "editing": "Edycja", - "search": "Szukaj", - "findNext": "Znajdź następny", - "findPrevious": "Znajdź Poprzedni", - "save": "Zapisz", - "selectAll": "Zaznacz wszystko", - "undo": "Cofnij", - "redo": "Ponów", - "moveLineUp": "Przesuń linię w górę", - "moveLineDown": "Przesuń linię w dół", - "toggleComment": "Przełącz komentarz", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Nie udało się załadować obrazu", - "startTyping": "Zacznij pisać...", - "unknownSize": "Nieznany rozmiar", - "fileIsEmpty": "Plik jest pusty", - "largeFileWarning": "Ostrzeżenie o dużym pliku", - "largeFileWarningDesc": "Ten plik ma rozmiar {{size}} , co może powodować problemy z wydajnością po otwarciu jako tekst.", - "fileNotFoundAndRemoved": "Plik \"{{name}}\" nie został znaleziony i został usunięty z ostatnich / przypiętych plików", - "failedToLoadFile": "Nie udało się załadować pliku: {{error}}", - "serverErrorOccurred": "Wystąpił błąd serwera. Spróbuj ponownie później.", - "autoSaveFailed": "Automatyczne zapisywanie nie powiodło się", - "fileAutoSaved": "Automatycznie zapisano plik", - "moveFileFailed": "Nie udało się przenieść {{name}}", - "moveOperationFailed": "Przenoszenie nie powiodło się", - "canOnlyCompareFiles": "Można porównać tylko dwa pliki", - "comparingFiles": "Porównywanie plików: {{file1}} i {{file2}}", - "dragFailed": "Przeciągnięcie nie powiodło się", - "filePinnedSuccessfully": "Plik \"{{name}}\" przypięty pomyślnie", - "pinFileFailed": "Nie udało się przypiąć pliku", - "fileUnpinnedSuccessfully": "Plik \"{{name}}\" odpięty pomyślnie", - "unpinFileFailed": "Nie udało się odpiąć pliku", - "shortcutAddedSuccessfully": "Skrót folderu \"{{name}}\" został pomyślnie dodany", - "addShortcutFailed": "Nie udało się dodać skrótu", - "operationCompletedSuccessfully": "Pomyślnie {{operation}} {{count}}", - "operationCompleted": "{{operation}} {{count}} przedmiotów", - "downloadFileSuccess": "Plik {{name}} pomyślnie pobrany", - "downloadFileFailed": "Pobieranie nie powiodło się", - "moveTo": "Przenieś do {{name}}", - "diffCompareWith": "Różnica z {{name}}", - "dragOutsideToDownload": "Przeciągnij na zewnątrz okna, aby pobrać (pliki{{count}})", - "newFolderDefault": "Nowy Folder", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Pomyślnie przeniesiono elementy {{count}} do {{target}}", - "move": "Przenieś", - "searchInFile": "Szukaj w pliku (Ctrl+F)", - "showKeyboardShortcuts": "Pokaż skróty klawiaturowe", - "startWritingMarkdown": "Zacznij pisać swoją zawartość markdown...", - "loadingFileComparison": "Ładowanie porównania plików...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Porównaj", - "sideBySide": "Obok strony", - "inline": "Wbudowany", - "fileComparison": "Porównanie plików: {{file1}} vs {{file2}}", - "fileTooLarge": "Plik za duży: {{error}}", - "sshConnectionFailed": "Połączenie SSH nie powiodło się. Sprawdź swoje połączenie z {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Nie udało się załadować pliku: {{error}}", - "connecting": "Łączenie...", - "connectedSuccessfully": "Połączono pomyślnie", - "totpVerificationFailed": "Weryfikacja TOTP nie powiodła się", - "warpgateVerificationFailed": "Autoryzacja Warpgate nie powiodła się", - "authenticationFailed": "Uwierzytelnianie nie powiodło się", - "verificationCodePrompt": "Kod weryfikacyjny:", - "changePermissions": "Zmień uprawnienia", - "currentPermissions": "Bieżące uprawnienia", - "owner": "Właściciel", - "group": "Grupa", - "others": "Pozostałe", - "read": "Czytaj", - "write": "Napisz", - "execute": "Wykonaj", - "permissionsChangedSuccessfully": "Uprawnienia zmienione pomyślnie", - "failedToChangePermissions": "Nie udało się zmienić uprawnień", - "name": "Nazwisko", - "sortByName": "Nazwisko", - "sortByDate": "Data modyfikacji", - "sortBySize": "Rozmiar", - "ascending": "Rosnąco", - "descending": "Malejąco", - "root": "Główny", - "new": "Nowy", - "sortBy": "Sortuj wg", - "items": "Przedmioty", - "selected": "Wybrane", - "editor": "Edytor", - "octal": "Otoczka", - "storage": "Pamięć", - "disk": "Dysk", - "used": "Używane", - "of": "z", - "toggleSidebar": "Przełącz pasek boczny", - "cannotLoadPdf": "Nie można załadować PDF", - "pdfLoadError": "Wystąpił błąd podczas ładowania tego pliku PDF.", - "loadingPdf": "Ładowanie PDF...", - "loadingPage": "Ładowanie strony..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopiuj do hosta…", - "moveToHost": "Przenieś do hosta…", - "copyItemsToHost": "Skopiuj {{count}} elementów do hosta…", - "moveItemsToHost": "Przenieś {{count}} elementów do hosta…", - "noHostsConnected": "Brak innych dostępnych hostów menedżerów plików.", - "noHostsConnectedHint": "Dodaj kolejny host SSH z włączonym Menedżerem plików w Menedżerze hosta.", - "selectDestinationHost": "Wybierz hosta docelowego", - "destinationPath": "Ścieżka docelowa", - "recentDestinations": "Ostatnie miejsca docelowe", - "collapseRecentDestinations": "Zwiń ostatnie miejsca docelowe", - "expandRecentDestinations": "Rozszerz ostatnie miejsca docelowe", - "browseFolders": "Przeglądaj foldery docelowe", - "browseDestination": "Przeglądaj lub wprowadź ścieżkę", - "confirmCopy": "Kopia", - "confirmMove": "Przenosić", - "transferring": "Przenoszenie…", - "compressing": "Kompresja…", - "extracting": "Wyodrębnianie…", - "transferringItems": "Przenoszenie {{current}} z {{total}} elementów…", - "transferSuccess": "Transfer zakończony", - "transferError": "Transfer nie powiódł się", - "transferPartial": "Transfer zakończony z błędami {{count}}", - "transferPartialHint": "Nie można przesłać: {{paths}}", - "itemsSummary": "{{count}} elementów", - "destMustBeDirectory": "Miejsce docelowe musi być katalogiem w przypadku transferów obejmujących wiele pozycji", - "selectThisFolder": "Wybierz ten folder", - "browsePathWillBeCreated": "Ten folder jeszcze nie istnieje. Zostanie utworzony po rozpoczęciu transferu.", - "browsePathError": "Nie można otworzyć tej ścieżki na hoście docelowym.", - "goUp": "Wchodzić", - "copyFolderToHost": "Kopiuj folder do hosta…", - "moveFolderToHost": "Przenieś folder do hosta…", - "hostReady": "Gotowy", - "hostConnecting": "Łączenie…", - "hostDisconnected": "Nie połączony", - "hostAuthRequired": "Wymagane uwierzytelnienie — najpierw otwórz Menedżera plików na tym hoście", - "hostConnectionFailed": "Połączenie nieudane", - "metricsTitle": "Czasy transferu", - "metricsPrepare": "Przygotuj miejsce docelowe: {{duration}}", - "metricsCompress": "Kompresja w źródle: {{duration}}", - "metricsHopSourceRead": "Źródło → serwer: {{throughput}}", - "metricsHopDestSftpWrite": "Serwer → cel (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Serwer → dest (lokalny): {{throughput}}", - "metricsTransfer": "Od końca do końca: {{throughput}} ({{duration}})", - "metricsExtract": "Wyciąg w miejscu docelowym: {{duration}}", - "metricsSourceDelete": "Usuń ze źródła: {{duration}}", - "metricsTotal": "Razem: {{duration}}", - "progressCompressing": "Kompresja na hoście źródłowym…", - "progressExtracting": "Wyodrębnianie w miejscu docelowym…", - "progressTransferring": "Przesyłanie danych…", - "progressReconnecting": "Ponowne łączenie…", - "parallelSegmentsLabel": "Równoległe pasy transferowe", - "parallelSegmentsOption": "{{count}} pasów", - "parallelSegmentsHint": "Duże pliki są dzielone na fragmenty o rozmiarze 256 MB. Wiele pasm korzysta z oddzielnych połączeń (np. uruchamiając kilka transferów), co zapewnia większą całkowitą przepustowość.", - "progressTotalSpeed": "{{speed}} łącznie ({{lanes}} pasów)", - "progressTransferringItems": "Przesyłanie plików ({{current}} z {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "Pliki {{current}} / {{total}}", - "sourceNotDeletedPartial": "Zachowano pliki źródłowe (częściowy transfer)", - "jumpHostLimitation": "Oba hosty muszą być dostępne z serwera Termix. Bezpośrednie routing między hostami nie jest obsługiwany.", - "cancel": "Anulować", - "methodLabel": "Metoda transferu", - "methodAuto": "Automatyczny", - "methodTar": "Archiwum Tar", - "methodItemSftp": "SFTP dla każdego pliku", - "methodAutoHint": "Wybiera protokół tar lub protokół SFTP dla poszczególnych plików na podstawie liczby plików, ich rozmiaru i możliwości kompresji. Pojedyncze pliki zawsze korzystają ze strumieniowego protokołu SFTP.", - "methodTarHint": "Kompresja w źródle, transfer jednego archiwum, rozpakowanie w miejscu docelowym. Wymagany jest tar na obu hostach Unix.", - "methodItemSftpHint": "Przesyłaj każdy plik osobno przez SFTP. Działa na wszystkich hostach, w tym Windows.", - "methodPreviewLoading": "Obliczanie metody transferu…", - "methodPreviewError": "Nie można wyświetlić podglądu metody transferu. Serwer i tak wybierze metodę po uruchomieniu.", - "methodPreviewWillUseTar": "Użyje: archiwum Tar", - "methodPreviewWillUseItemSftp": "Będzie używać: SFTP dla każdego pliku", - "methodPreviewScanSummary": "{{fileCount}} plików, łącznie {{totalSize}} (przeskanowano na hoście źródłowym).", - "methodItemSftpLimitation": "Każdy plik korzysta z tego samego strumienia SFTP jako pojedyncza kopia pliku, jeden po drugim. Postęp jest łączony we wszystkich plikach, więc pasek przesuwa się powoli w przypadku dużych plików.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Wybrano SFTP dla poszczególnych plików.", - "user_tar": "Wybrano archiwum tar.", - "tar_unavailable": "Tar nie jest dostępny na jednym lub obu hostach — zamiast tego używany będzie protokół SFTP dla każdego pliku.", - "windows_host": "Wymagany jest host Windows — nie używa się pliku tar.", - "auto_multi_large": "Auto: wiele plików, w tym duży plik ({{largestSize}}) z kompresowalnymi danymi — pakiety tar w jednym transferze.", - "auto_single_large_in_archive": "Auto: jeden duży plik ({{largestSize}}) w tym zestawie — SFTP dla każdego pliku.", - "auto_many_incompressible": "Auto: dane w większości niekompresowalne — SFTP dla każdego pliku.", - "auto_many_files": "Auto: wiele plików ({{fileCount}}) — tar zmniejsza obciążenie pojedynczego pliku.", - "auto_default": "Auto: SFTP dla każdego pliku dla tego zestawu." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Anulować", - "progressCancelling": "Anulowanie…", - "progressStalled": "Zatrzymany", - "resumedHint": "Połączono ponownie z aktywnym transferem rozpoczętym w innym oknie.", - "transferCancelled": "Transfer anulowany", - "transferCancelledCopyHint": "W miejscu docelowym mogą pozostać częściowe pliki.", - "transferCancelledMoveHint": "W miejscu docelowym mogą pozostać częściowe pliki. Pliki źródłowe nie zostały usunięte.", - "cleanupDestFiles": "Miejsce sprzątania", - "cleanupDestFilesSuccess": "Usunięto częściowe pliki z miejsca docelowego", - "cleanupDestFilesPartial": "Niektórych częściowych plików nie można było usunąć", - "cleanupDestFilesNothing": "Nic do posprzątania w miejscu docelowym", - "cleanupDestFilesError": "Czyszczenie nie powiodło się", - "retryTransfer": "Spróbować ponownie", - "retryTransferError": "Ponowna próba nie powiodła się", - "transferFailedRetryHint": "W miejscu docelowym zachowano częściowe dane. Ponowna próba zostanie wznowiona po przywróceniu połączenia." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Brak tuneli SSH", - "createFirstTunnelMessage": "Nie utworzyłeś jeszcze żadnych tuneli SSH. Skonfiguruj połączenia tuneli w Host Manager, aby rozpocząć.", - "connected": "Połączono", - "disconnected": "Rozłączony", - "connecting": "Łączenie...", - "error": "Błąd", - "canceling": "Anulowanie...", - "connect": "Połącz", - "disconnect": "Rozłącz", - "cancel": "Anuluj", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Port lokalny", - "remotePort": "Port zdalny", - "currentHostPort": "Bieżący port hosta", - "endpointPort": "Port punktu końcowego", - "bindIp": "Lokalny adres IP", - "endpointSshConfig": "Konfiguracja SSH punktu końcowego", - "endpointSshHost": "Host SSH punktu końcowego", - "endpointSshHostPlaceholder": "Wybierz skonfigurowany host", - "endpointSshHostRequired": "Wybierz host SSH punktu końcowego dla każdego tunelu klienta.", - "attempt": "Próba {{current}} dla {{max}}", - "nextRetryIn": "Następna próba za {{seconds}} sekund", - "clientTunnels": "Tunele klienta", - "clientTunnel": "Tunel klienta", - "addClientTunnel": "Dodaj Tunel Klienta", - "noClientTunnels": "Nie skonfigurowano tuneli klienckich na tym komputerze.", - "tunnelName": "Nazwa tunelu", - "remoteHost": "Zdalny host", - "autoStart": "Automatyczne uruchamianie", - "clientAutoStartDesc": "Rozpoczyna się, gdy ten klient na pulpicie otworzy się i zostanie połączony.", - "clientManualStartDesc": "Użyj startu i zatrzymania z tego wiersza. Termiks nie otworzy się automatycznie.", - "clientRemoteServerNote": "Przekazywanie zdalne może wymagać AllowTcpForwarding i GatewayPorts na serwerze SSH punktu końcowego. Zdalny port zamyka się po rozłączeniu tego pulpitu.", - "clientTunnelStarted": "Tunel klienta rozpoczęty", - "clientTunnelStopped": "Tunel klienta zatrzymany", - "tunnelTestSucceeded": "Test tunelu zakończony sukcesem", - "tunnelTestFailed": "Test tunelu nie powiódł się", - "localSaved": "Tunele klienta zapisane", - "localSaveError": "Nie udało się zapisać lokalnych tuneli klienta", - "invalidBindIp": "Lokalny adres IP musi być prawidłowym adresem IPv4.", - "invalidLocalTargetIp": "Lokalny docelowy adres IP musi być prawidłowym adresem IPv4.", - "invalidLocalPort": "Port lokalny musi mieć od 1 do 65535.", - "invalidRemotePort": "Zdalny port musi być pomiędzy 1 a 65535.", - "invalidLocalTargetPort": "Lokalny port docelowy musi znajdować się w przedziale od 1 do 65535.", - "invalidEndpointPort": "Port punktu końcowego musi być pomiędzy 1 a 65535.", - "duplicateAutoStartBind": "Tylko jeden tunel klienta automatycznego startu może użyć {{bind}}.", - "manualControlError": "Nie udało się zaktualizować stanu tunelu.", - "active": "Aktywne", - "start": "Rozpocznij", - "stop": "Zatrzymaj", - "test": "Badanie", - "type": "Typ tunelu", - "typeLocal": "Lokalna (-L)", - "typeRemote": "Zdalny (-R)", - "typeDynamic": "Dynamiczne (-D)", - "typeServerLocalDesc": "Bieżący host do punktu końcowego.", - "typeServerRemoteDesc": "Końcowy punkt wstecz do bieżącego hosta.", - "typeClientLocalDesc": "Lokalny komputer do punktu końcowego.", - "typeClientRemoteDesc": "Punkt końcowy z powrotem do komputera lokalnego.", - "typeClientDynamicDesc": "SOCKS na komputerze lokalnym.", - "typeDynamicDesc": "Przekaż ruch SOCKS5 CONNECT przez SSH", - "forwardDescriptionServerLocal": "Bieżący host {{sourcePort}} → punkt końcowy {{endpointPort}}.", - "forwardDescriptionServerRemote": "Punkt końcowy {{endpointPort}} → bieżący host {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS dla bieżącego hosta {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokalny {{sourcePort}} → zdalny {{endpointPort}}.", - "forwardDescriptionClientRemote": "Zdalny {{sourcePort}} → lokalny {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS na lokalnym porcie {{sourcePort}}.", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS przez {{endpoint}}", - "autoNameClientLocal": "Lokalny {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokalny {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} przez {{endpoint}}", - "route": "Droga:", - "lastStarted": "Ostatnio uruchomione", - "lastTested": "Ostatnio testowane", - "lastError": "Ostatni błąd", - "maxRetries": "Maksymalna liczba prób", - "maxRetriesDescription": "Maksymalna ilość powtórzeń próby.", - "retryInterval": "Powtórz interwał (w sekundach)", - "retryIntervalDescription": "Czas oczekiwania pomiędzy ponownymi próbami.", - "local": "Lokalny", - "remote": "Zdalny", - "destination": "Miejsce przeznaczenia", + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", "host": "Host", - "mode": "Tryb", - "noHostSelected": "Nie wybrano hosta", - "working": "Pracuję..." + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Procesor", - "memory": "Pamięć", - "disk": "Dysk", - "network": "Sieć", - "uptime": "Czas pracy", - "processes": "Procesy", - "available": "Dostępny", - "free": "Darmowe", - "connecting": "Łączenie...", - "connectionFailed": "Nie udało się połączyć z serwerem", - "naCpus": "Brak CPU(ów)", - "cpuCores_one": "Rdzeń {{count}}", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Użycie procesora", - "memoryUsage": "Użycie pamięci", - "diskUsage": "Użycie dysku", - "failedToFetchHostConfig": "Nie udało się pobrać konfiguracji hosta", - "serverOffline": "Serwer offline", - "cannotFetchMetrics": "Nie można pobrać metryki z serwera offline", - "totpFailed": "Weryfikacja TOTP nie powiodła się", - "noneAuthNotSupported": "Statystyki serwera nie obsługują typu \"brak\" uwierzytelniania.", - "load": "Załaduj", - "systemInfo": "Informacje o systemie", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "System operacyjny", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "sekundy", - "networkInterfaces": "Interfejsy sieciowe", - "noInterfacesFound": "Nie znaleziono interfejsów sieciowych", - "noProcessesFound": "Nie znaleziono procesów", - "loginStats": "Statystyki logowania SSH", - "noRecentLoginData": "Brak danych logowania", - "executingQuickAction": "Wykonywanie {{name}}...", - "quickActionSuccess": "Ukończono {{name}}", - "quickActionFailed": "{{name}} nie powiódł się", - "quickActionError": "Nie udało się wykonać {{name}}", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Porty słuchające", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adres", - "process": "Proces", - "noData": "Brak danych o portach" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Zapora", - "inactive": "Nieaktywny", - "policy": "Polityka", - "rules": "zasady", - "noData": "Brak dostępnych danych zapory", - "action": "Akcja", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Źródło", - "anywhere": "Gdziekolwiek" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Załaduj średnią", - "swap": "Zamień", - "architecture": "Architektura", - "refresh": "Odśwież", - "retry": "Ponów próbę" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Samodzielnie hostowany SSH i zdalne zarządzanie pulpitem", - "loginTitle": "Zaloguj się do Termix", - "registerTitle": "Utwórz konto", - "forgotPassword": "Zapomniałeś hasła?", - "rememberMe": "Zapamiętaj urządzenie przez 30 dni (w tym TOTP)", - "noAccount": "Nie masz konta?", - "hasAccount": "Masz już konto?", - "twoFactorAuth": "Uwierzytelnianie dwuetapowe", - "enterCode": "Wprowadź kod weryfikacyjny", - "backupCode": "Lub użyj kodu zapasowego", - "verifyCode": "Potwierdź kod", - "redirectingToApp": "Przekierowanie do aplikacji...", - "sshAuthenticationRequired": "Wymagane uwierzytelnienie SSH", - "sshNoKeyboardInteractive": "Interaktywne uwierzytelnianie klawiatury niedostępne", - "sshAuthenticationFailed": "Uwierzytelnianie nie powiodło się", - "sshAuthenticationTimeout": "Limit czasu uwierzytelniania", - "sshNoKeyboardInteractiveDescription": "Serwer nie obsługuje interaktywnego uwierzytelniania klawiatury. Proszę podać swoje hasło lub klucz SSH.", - "sshAuthFailedDescription": "Podane dane logowania są nieprawidłowe. Spróbuj ponownie z prawidłowymi danymi logowania.", - "sshTimeoutDescription": "Próba uwierzytelniania przekroczyła limit czasu. Spróbuj ponownie.", - "sshProvideCredentialsDescription": "Podaj swoje poświadczenia SSH aby połączyć się z tym serwerem.", - "sshPasswordDescription": "Wprowadź hasło dla tego połączenia SSH.", - "sshKeyPasswordDescription": "Jeśli twój klucz SSH jest zaszyfrowany, wprowadź hasło tutaj.", - "passphraseRequired": "Wymagane hasło", - "passphraseRequiredDescription": "Klucz SSH jest zaszyfrowany. Wprowadź hasło, aby go odblokować.", - "back": "Powrót", - "firstUser": "Pierwszy użytkownik", - "firstUserMessage": "Jesteś pierwszym użytkownikiem i zostaniesz administratorem. Możesz przeglądać ustawienia administratora na liście rozwijanej przez użytkownika paska bocznego. Jeśli uważasz, że to błąd, sprawdź logi dockera lub utwórz problem z GitHub.", - "external": "Zewnętrzne", - "loginWithExternal": "Zaloguj się z zewnętrznym dostawcą", - "loginWithExternalDesc": "Zaloguj się przy użyciu skonfigurowanego dostawcy zewnętrznej tożsamości", - "externalNotSupportedInElectron": "Uwierzytelnianie zewnętrzne nie jest jeszcze obsługiwane w aplikacji Electron. Użyj wersji internetowej dla logowania OIDC.", - "resetPasswordButton": "Resetuj hasło", - "sendResetCode": "Wyślij kod resetowania", - "resetCodeDesc": "Wprowadź swoją nazwę użytkownika, aby otrzymać kod resetowania hasła. Kod zostanie zalogowany w dziennikach kontenera.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Potwierdź kod", - "enterResetCode": "Wprowadź 6-cyfrowy kod z dziennika kontenera docker dla użytkownika:", - "newPassword": "Nowe hasło", - "confirmNewPassword": "Potwierdź hasło", - "enterNewPassword": "Wprowadź nowe hasło dla użytkownika:", - "signUp": "Zarejestruj", - "desktopApp": "Aplikacja pulpitowa", - "loggingInToDesktopApp": "Logowanie do aplikacji desktopowej", - "loadingServer": "Ładowanie serwera...", - "dataLossWarning": "Resetowanie hasła w ten sposób usunie wszystkie zapisane przez Ciebie hosty, dane logowania i inne zaszyfrowane dane. Tej akcji nie można cofnąć. Użyj tego tylko wtedy, gdy zapomniałeś hasła i nie jesteś zalogowany.", - "authenticationDisabled": "Uwierzytelnianie wyłączone", - "authenticationDisabledDesc": "Wszystkie metody uwierzytelniania są obecnie wyłączone. Skontaktuj się z administratorem.", - "attemptsRemaining": "Pozostało {{count}} prób" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Zweryfikuj klucz hosta SSH", - "keyChangedWarning": "Klucz hosta SSH został zmieniony", - "firstConnectionTitle": "Pierwsze połączenie z tym hostem", - "firstConnectionDescription": "Nie można ustalić autentyczności tego hosta. Sprawdź, czy odcisk palca odpowiada oczekiwaniom.", - "keyChangedDescription": "Klucz hosta dla tego serwera zmienił się od ostatniego połączenia. Może to wskazywać na problem bezpieczeństwa.", - "previousKey": "Poprzedni klucz", - "newFingerprint": "Nowy odcisk palca", - "fingerprint": "Odcisk palca", - "verifyInstructions": "Jeśli ufasz temu hostowi, kliknij przycisk Akceptuj, aby kontynuować i zapisać ten odcisk palca dla przyszłych połączeń.", - "securityWarning": "Ostrzeżenie bezpieczeństwa", - "acceptAndContinue": "Zaakceptuj i kontynuuj", - "acceptNewKey": "Akceptuj nowy klucz i kontynuuj" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Nie można połączyć się z bazą danych", - "unknownError": "Nieznany błąd", - "loginFailed": "Logowanie nie powiodło się", - "failedPasswordReset": "Nie udało się zainicjować resetowania hasła", - "failedVerifyCode": "Nie udało się zweryfikować kodu resetowania", - "failedCompleteReset": "Nie udało się zakończyć resetowania hasła", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Nie udało się uruchomić logowania OIDC", - "silentSigninOidcUnavailable": "Poproszono o cichy logowanie, ale login OIDC nie jest dostępny.", - "failedUserInfo": "Nie udało się uzyskać informacji o użytkowniku po zalogowaniu", - "oidcAuthFailed": "Uwierzytelnianie OIDC nie powiodło się", - "invalidAuthUrl": "Nieprawidłowy adres URL autoryzacji otrzymany z backendu", - "requiredField": "To pole jest wymagane", - "minLength": "Minimalna długość to {{min}}", - "passwordMismatch": "Hasła nie pasują", - "passwordLoginDisabled": "Logowanie użytkownika/hasła jest obecnie wyłączone", - "sessionExpired": "Sesja wygasła - zaloguj się ponownie", - "totpRateLimited": "Oceń limitowane: Zbyt wiele prób weryfikacji TOTP. Spróbuj ponownie później.", - "totpRateLimitedWithTime": "Ograniczenie szybkości: Zbyt wiele prób weryfikacji TOTP. Odczekaj {{time}} sekund przed ponowną próbą.", - "resetCodeRateLimited": "Oceń limit: Zbyt wiele prób weryfikacji. Spróbuj ponownie później.", - "resetCodeRateLimitedWithTime": "Ograniczenie szybkości: Zbyt wiele prób weryfikacji. Odczekaj {{time}} sekund przed ponowną próbą.", - "authTokenSaveFailed": "Nie udało się zapisać tokenu uwierzytelniania", - "failedToLoadServer": "Nie udało się załadować serwera" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Rejestracja nowego konta jest obecnie wyłączona przez administratora. Zaloguj się lub skontaktuj się z administratorem.", - "userNotAllowed": "Twoje konto nie jest autoryzowane do rejestracji. Skontaktuj się z administratorem.", - "databaseConnectionFailed": "Nie udało się połączyć z serwerem bazy danych", - "resetCodeSent": "Kod resetujący został wysłany do dzienników Docker", - "codeVerified": "Kod zweryfikowany pomyślnie", - "passwordResetSuccess": "Hasło zostało zresetowane", - "loginSuccess": "Logowanie zakończone powodzeniem", - "registrationSuccess": "Rejestracja powiodła się" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokalne tunele pulpitu ukierunkowane na skonfigurowane hosty SSH.", - "c2sTunnelPresets": "Ustawienia tunelu klienta", - "c2sTunnelPresetsDesc": "Zapisz lokalną listę tuneli tego klienta jako ustawienie serwera lub załaduj ustawienie z powrotem do tego klienta.", - "c2sTunnelPresetsUnavailable": "Ustawienia tunelu klienta są dostępne tylko w kliencie pulpitu.", - "c2sPresetName": "Nazwa ustawień wstępnych", - "c2sPresetNamePlaceholder": "Nazwa predefiniowana klienta", - "c2sPresetToLoad": "Ustawienie do załadowania", - "c2sNoPresetSelected": "Nie wybrano ustawień wstępnych", - "c2sNoPresets": "Nie zapisano ustawień wstępnych", - "c2sLoadPreset": "Załaduj", - "c2sCurrentLocalConfig": "{{count}} lokalne tunele klienta skonfigurowane na tym komputerze.", - "c2sPresetSyncNote": "Presety są wyraźnymi zrzutami migawek; ładowanie jednego zastępuje lokalną listę tuneli klienta tego komputera.", - "c2sPresetSaved": "Ustawienia tunelu klienta zapisane", - "c2sPresetLoaded": "Ustawienia tunelu klienta załadowane lokalnie", - "c2sPresetRenamed": "Zmieniono nazwę tunelu klienta", - "c2sPresetDeleted": "Usunięto ustawienie tunelu klienta", - "c2sPresetLoadError": "Nie udało się załadować ustawień tunelu klienta" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Język", - "keyPassword": "hasło klucza", - "pastePrivateKey": "Wklej klucz prywatny tutaj...", - "localListenerHost": "127.0.0.1 (słuchaj lokalnie)", - "localTargetHost": "127.0.0.1 (cel na tym komputerze)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Wprowadź hasło", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Pulpit", - "loading": "Ładowanie pulpitu nawigacyjnego...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Wsparcie", + "support": "Support", "discord": "Discord", - "serverOverview": "Przegląd serwera", - "version": "Wersja", - "upToDate": "Dotychczas", - "updateAvailable": "Dostępna aktualizacja", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Czas pracy", - "database": "Baza danych", - "healthy": "Zdrowe", - "error": "Błąd", - "totalHosts": "Całkowita liczba hostów", - "totalTunnels": "Całkowita liczba tuneli", - "totalCredentials": "Łącznie dane logowania", - "recentActivity": "Ostatnia aktywność", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Ładowanie ostatniej aktywności...", - "noRecentActivity": "Brak ostatniej aktywności", - "quickActions": "Szybkie akcje", - "addHost": "Dodaj hosta", - "addCredential": "Dodaj poświadczenia", - "adminSettings": "Ustawienia administratora", - "userProfile": "Profil użytkownika", - "serverStats": "Statystyki serwera", - "loadingServerStats": "Ładowanie statystyk serwera...", - "noServerData": "Brak dostępnych danych serwera", - "cpu": "Procesor", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Dostosuj panel", - "dashboardSettings": "Ustawienia panelu ustawień", - "enableDisableCards": "Włącz/Wyłącz karty", - "resetLayout": "Przywróć domyślne", - "serverOverviewCard": "Przegląd serwera", - "recentActivityCard": "Ostatnia aktywność", - "networkGraphCard": "Wykres sieci", - "networkGraph": "Wykres sieci", - "quickActionsCard": "Szybkie akcje", - "serverStatsCard": "Statystyki serwera", - "panelMain": "Główny", - "panelSide": "Bok", - "justNow": "właśnie teraz" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABILNY", - "hostsOnline": "Hosty Online", - "activeTunnels": "Aktywne tunele", - "registerNewServer": "Rejestracja nowego serwera", - "storeSshKeysOrPasswords": "Przechowuj klucze SSH lub hasła", - "manageUsersAndRoles": "Zarządzaj użytkownikami i rolami", - "manageYourAccount": "Zarządzaj kontem", - "hostStatus": "Status hosta", - "noHostsConfigured": "Brak skonfigurowanych hostów", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", - "onlineLower": "Dostępny", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Dodać:", - "commandPalette": "Paleta Poleceń", - "done": "Gotowe", - "editModeInstructions": "Przeciągnij karty aby zmienić kolejność · Przeciągnij rozdzielacz kolumn, aby zmienić rozmiar kolumn · Przeciągnij dolną krawędź karty, aby zmienić jej wysokość · Kosz aby usunąć", - "empty": "Pusty", - "clear": "Wyczyść" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Dodaj hosta", - "addGroup": "Dodaj grupę", - "addLink": "Dodaj link", - "zoomIn": "Powiększ", - "zoomOut": "Pomniejsz", - "resetView": "Resetuj widok", - "selectHost": "Wybierz hosta", - "chooseHost": "Wybierz host...", - "parentGroup": "Grupa nadrzędna", - "noGroup": "Brak grupy", - "groupName": "Nazwa grupy", - "color": "Kolor", - "source": "Źródło", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Przenieś do grupy", - "selectGroup": "Wybierz grupę...", - "addConnection": "Dodaj połączenie", - "hostDetails": "Szczegóły hosta", - "removeFromGroup": "Usuń z grupy", - "addHostHere": "Dodaj tutaj hosta", - "editGroup": "Edytuj grupę", - "delete": "Usuń", - "add": "Dodaj", - "create": "Utwórz", - "move": "Przenieś", - "connect": "Połącz", - "createGroup": "Utwórz grupę", - "selectSourcePlaceholder": "Wybierz źródło...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Nieprawidłowy plik", - "hostAlreadyExists": "Host jest już w topologii", - "connectionExists": "Połączenie już istnieje", - "unknown": "Nieznane", - "name": "Nazwisko", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", "status": "Status", - "failedToAddNode": "Nie udało się dodać węzła", - "sourceDifferentFromTarget": "Źródło i cel muszą być różne", - "exportJSON": "Eksportuj JSON", - "importJSON": "Importuj JSON", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Menedżer plików", - "tunnel": "Tunel", - "docker": "Dokujący", - "serverStats": "Statystyki serwera", - "noNodes": "Brak węzłów" + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker nie jest włączony dla tego hosta", - "validating": "Sprawdzanie poprawności Docker...", - "connecting": "Łączenie...", - "error": "Błąd", - "version": "Dokator {{version}}", - "connectionFailed": "Nie udało się połączyć z Dockerem", - "containerStarted": "Pojemnik {{name}} został uruchomiony", - "failedToStartContainer": "Nie udało się uruchomić kontenera {{name}}", - "containerStopped": "Pojemnik {{name}} zatrzymany", - "failedToStopContainer": "Nie udało się zatrzymać kontenera {{name}}", - "containerRestarted": "Pojemnik {{name}} został uruchomiony ponownie", - "failedToRestartContainer": "Nie udało się ponownie uruchomić kontenera {{name}}", - "containerPaused": "Kontener {{name}} wstrzymał", - "containerUnpaused": "Kontener {{name}} niewstrzymał", - "failedToTogglePauseContainer": "Nie udało się przełączyć stanu pauzy dla kontenera {{name}}", - "containerRemoved": "Usunięto pojemnik {{name}}", - "failedToRemoveContainer": "Nie udało się usunąć kontenera {{name}}", - "image": "Obraz", - "ports": "Porty", - "noPorts": "Brak portów", - "start": "Rozpocznij", - "confirmRemoveContainer": "Czy na pewno chcesz usunąć kontenera '{{name}}'? Tej akcji nie można cofnąć.", - "runningContainerWarning": "Ostrzeżenie: Ten kontener jest obecnie uruchomiony. Usunięcie go zatrzyma najpierw kontener", - "loadingContainers": "Ładowanie kontenerów...", - "manager": "Menedżer stacji dokującej", - "autoRefresh": "Automatyczne odświeżanie", - "timestamps": "Znaczniki czasu", - "lines": "Linie", - "filterLogs": "Filtruj logi...", - "refresh": "Odśwież", - "download": "Pobierz", - "clear": "Wyczyść", - "logsDownloaded": "Dzienniki pobrane pomyślnie", - "last50": "Ostatnie 50", - "last100": "Ostatnie 100", - "last500": "Ostatnie 500", - "last1000": "Ostatnie 1000", - "allLogs": "Wszystkie dzienniki", - "noLogsMatching": "Brak logów pasujących do \"{{query}}\"", - "noLogsAvailable": "Brak dostępnych logów", - "noContainersFound": "Nie znaleziono kontenerów", - "noContainersFoundHint": "Pojemniki dokujące nie są dostępne dla tego hosta", - "searchPlaceholder": "Szukaj kontenerów...", - "allStatuses": "Wszystkie statusy", - "stateRunning": "Uruchamianie", - "statePaused": "Zatrzymano", - "stateExited": "Zakończone", - "stateRestarting": "Ponowne uruchamianie", - "noContainersMatchFilters": "Żadne kontenery nie pasują do Twoich filtrów", - "noContainersMatchFiltersHint": "Spróbuj dostosować kryteria wyszukiwania lub filtrowania", - "failedToFetchStats": "Nie udało się pobrać statystyk kontenera", - "containerNotRunning": "Kontener nie działa", - "startContainerToViewStats": "Rozpocznij kontener aby wyświetlić statystyki", - "loadingStats": "Ładowanie statystyk...", - "errorLoadingStats": "Błąd ładowania statystyk", - "noStatsAvailable": "Brak dostępnych statystyk", - "cpuUsage": "Użycie procesora", - "current": "Bieżący", - "memoryUsage": "Użycie pamięci", - "networkIo": "Sieć I/O", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Wyjście", - "blockIo": "Blokuj I/O", - "read": "Czytaj", - "write": "Napisz", - "pids": "PID", - "containerInformation": "Informacje o kontenerze", - "name": "Nazwisko", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Stan", - "containerMustBeRunning": "Kontener musi być uruchomiony, aby uzyskać dostęp do konsoli", - "verificationCodePrompt": "Wprowadź kod weryfikacyjny", - "totpVerificationFailed": "Weryfikacja TOTP nie powiodła się. Spróbuj ponownie.", - "warpgateVerificationFailed": "Uwierzytelnianie Warpgate nie powiodło się. Spróbuj ponownie.", - "connectedTo": "Połączono z {{containerName}}", - "disconnected": "Rozłączony", - "consoleError": "Błąd konsoli", - "errorMessage": "Błąd: {{message}}", - "failedToConnect": "Nie udało się połączyć z kontenerem", - "console": "Konsola", - "selectShell": "Wybierz powłokę", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "pl", - "ash": "popiół", - "connect": "Połącz", - "disconnect": "Rozłącz", - "notConnected": "Nie połączono", - "clickToConnect": "Kliknij Połącz się, aby rozpocząć sesję powłoki", - "connectingTo": "Łączenie z {{containerName}}...", - "containerNotFound": "Nie znaleziono kontenera", - "backToList": "Powrót do listy", - "logs": "Logi", - "stats": "Statystyki", - "consoleTab": "Konsola", - "startContainerToAccess": "Uruchom kontener aby uzyskać dostęp do konsoli" + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Ogólny", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Użytkownicy", - "sectionSessions": "Sesje", - "sectionRoles": "Role", - "sectionDatabase": "Baza danych", - "sectionApiKeys": "Klucze API", - "allowRegistration": "Zezwalaj na rejestrację użytkownika", - "allowRegistrationDesc": "Pozwól nowym użytkownikom na samodzielną rejestrację", - "allowPasswordLogin": "Zezwalaj na logowanie do hasła", - "allowPasswordLoginDesc": "Login użytkownika/hasło", - "oidcAutoProvision": "Automatyczna dostawa OIDC", - "oidcAutoProvisionDesc": "Automatyczne tworzenie kont dla użytkowników OIDC nawet gdy rejestracja jest wyłączona", - "allowPasswordReset": "Zezwalaj na resetowanie hasła", - "allowPasswordResetDesc": "Zresetuj kod za pomocą dzienników Docker", - "sessionTimeout": "Limit czasu sesji", - "hours": "godziny", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Monitorowanie ustawień domyślnych", - "statusCheck": "Sprawdzanie statusu", - "metrics": "Metryki", - "sec": "sek.", - "logLevel": "Poziom logowania", - "enableGuacamole": "Włącz Guacamol", - "enableGuacamoleDesc": "Pulpit zdalny RDP/VNC", - "guacdUrl": "adres URL guacd", - "oidcDescription": "Konfiguracja OpenID Connect dla SSO. Pola oznaczone * są wymagane.", - "oidcClientId": "Identyfikator klienta", - "oidcClientSecret": "Klucz tajny klienta", - "oidcAuthUrl": "URL autoryzacji", - "oidcIssuerUrl": "Adres URL wystawcy", - "oidcTokenUrl": "Adres URL tokenu", - "oidcUserIdentifier": "Ścieżka identyfikatora użytkownika", - "oidcDisplayName": "Wyświetl ścieżkę nazwy", - "oidcScopes": "Zakresy", - "oidcUserinfoUrl": "Nadpisz adres URL Userinfo", - "oidcAllowedUsers": "Dozwoleni użytkownicy", - "oidcAllowedUsersDesc": "Jeden e-mail na wiersz. Pozostaw puste, aby zezwolić na wszystkie.", - "removeOidc": "Usuń", - "usersCount": "{{count}} użytkowników", - "createUser": "Utwórz", - "newRole": "Nowa rola", - "roleName": "Nazwisko", - "roleDisplayName": "Wyświetlana nazwa", - "roleDescription": "Opis", - "rolesCount": "Role {{count}}", - "createRole": "Utwórz", - "creating": "Tworzenie...", - "exportDatabase": "Eksportuj bazę danych", - "exportDatabaseDesc": "Pobierz kopię zapasową wszystkich hostów, danych logowania i ustawień", - "export": "Eksportuj", - "exporting": "Eksportowanie...", - "importDatabase": "Importuj bazę danych", - "importDatabaseDesc": "Przywróć z pliku backupu .sqlite", - "importDatabaseSelected": "Wybrane: {{name}}", - "selectFile": "Wybierz plik", - "changeFile": "Zmiana", - "import": "Importuj", - "importing": "Importowanie...", - "apiKeysCount": "{{count}} klucze", - "newApiKey": "Nowy klucz API", - "apiKeyCreatedWarning": "Klucz utworzony - skopiuj go teraz, nie będzie ponownie wyświetlany.", - "apiKeyName": "Nazwisko", - "apiKeyUser": "Użytkownik", - "apiKeySelectUser": "Wybierz użytkownika...", - "apiKeyExpiresAt": "Wygasa", - "createKey": "Utwórz klucz", - "apiKeyNoExpiry": "Brak terminu ważności", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokalny", + "authTypeLocal": "Local", "adminStatusAdministrator": "Administrator", - "adminStatusRegularUser": "Zwykły użytkownik", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "USTALENIE", - "youBadge": "Ty", - "sessionsActive": "{{count}} aktywny", - "sessionActive": "Aktywne: {{time}}", - "sessionExpires": "Termin ważności: {{time}}", - "revokeAll": "Wszystkie", - "revokeAllSessionsSuccess": "Wszystkie sesje dla użytkownika odwołane", - "revokeAllSessionsFailed": "Nie udało się odwołać sesji", - "revokeSessionFailed": "Nie udało się odwołać sesji", - "addRole": "Dodaj rolę", - "noCustomRoles": "Nie zdefiniowano roli niestandardowych", - "removeRoleFailed": "Nie udało się usunąć roli", - "assignRoleFailed": "Nie udało się przypisać roli", - "deleteRoleFailed": "Nie udało się usunąć roli", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Pełny dostęp do wszystkich ustawień administratora", - "userRoles": "Role", - "revokeAllUserSessions": "Unieważnij wszystkie sesje", - "revokeAllUserSessionsDesc": "Wymuś ponowne logowanie na wszystkich urządzeniach", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Usunięcie tego użytkownika jest trwałe.", - "deleteUser": "Usuń {{username}}", - "deleting": "Usuwanie...", - "deleteUserFailed": "Nie udało się usunąć użytkownika", - "deleteUserSuccess": "Użytkownik \"{{username}}\" został usunięty", - "deleteRoleSuccess": "Rola \"{{name}}\" usunięto", - "revokeKeySuccess": "Klucz \"{{name}}\" odwołany", - "revokeKeyFailed": "Nie udało się odwołać klucza", - "copiedToClipboard": "Skopiowano do schowka", - "done": "Gotowe", - "createUserTitle": "Utwórz użytkownika", - "createUserDesc": "Utwórz nowe konto lokalne.", - "createUserUsername": "Nazwa użytkownika", - "createUserPassword": "Hasło", - "createUserPasswordHint": "Co najmniej 6 znaków.", - "createUserEnterUsername": "Wprowadź nazwę użytkownika", - "createUserEnterPassword": "Wprowadź hasło", - "createUserSubmit": "Utwórz użytkownika", - "editUserTitle": "Zarządzaj użytkownikami: {{username}}", - "editUserDesc": "Edytuj role, status administratora, sesje i ustawienia konta.", - "editUserUsername": "Nazwa użytkownika", - "editUserAuthType": "Typ uwierzytelniania", - "editUserAdminStatus": "Status administratora", - "editUserUserId": "ID użytkownika", - "linkAccountTitle": "Połącz OIDC z kontem hasłem", - "linkAccountDesc": "Scal konto OIDC {{username}} z istniejącym kontem lokalnym.", - "linkAccountWarningTitle": "Będzie to obejmować:", - "linkAccountEffect1": "Usuń konto tylko OIDC", - "linkAccountEffect2": "Dodaj login OIDC do konta docelowego", - "linkAccountEffect3": "Zezwalaj na logowanie do OIDC i hasła", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Wprowadź nazwę użytkownika konta lokalnego, do której chcesz się połączyć", - "linkAccounts": "Połącz konta", - "linkAccountSuccess": "Konto OIDC powiązane z „{{username}}”", - "linkAccountFailed": "Nie udało się połączyć konta OIDC", - "linkAccountInProgress": "Łączenie...", - "saving": "Zapisywanie...", - "updateRegistrationFailed": "Nie udało się zaktualizować ustawień rejestracji", - "updatePasswordLoginFailed": "Nie udało się zaktualizować ustawienia logowania hasła", - "updateOidcAutoProvisionFailed": "Nie udało się zaktualizować ustawień automatycznego dostarczania OIDC", - "updatePasswordResetFailed": "Nie udało się zaktualizować ustawienia resetowania hasła", - "sessionTimeoutRange2": "Limit czasu sesji musi wynosić od 1 do 720 godzin", - "sessionTimeoutSaved": "Limit czasu sesji został zapisany", - "sessionTimeoutSaveFailed": "Nie udało się zapisać limitu czasu sesji", - "monitoringIntervalInvalid": "Nieprawidłowe wartości interwału", - "monitoringSaved": "Ustawienia monitorowania zapisane", - "monitoringSaveFailed": "Nie udało się zapisać ustawień monitorowania", - "guacamoleSaved": "Ustawienia Guacamole zapisane", - "guacamoleSaveFailed": "Nie udało się zapisać ustawień Guacamole", - "guacamoleUpdateFailed": "Nie udało się zaktualizować ustawienia Guacamole", - "logLevelUpdateFailed": "Nie udało się zaktualizować poziomu dziennika", - "oidcSaved": "Zapisano konfigurację OIDC", - "oidcSaveFailed": "Nie udało się zapisać konfiguracji OIDC", - "oidcRemoved": "Konfiguracja OIDC usunięta", - "oidcRemoveFailed": "Nie udało się usunąć konfiguracji OIDC", - "createUserRequired": "Nazwa użytkownika i hasło są wymagane", - "createUserPasswordTooShort": "Hasło musi mieć co najmniej 6 znaków", - "createUserSuccess": "Użytkownik \"{{username}}\" został utworzony", - "createUserFailed": "Nie udało się utworzyć użytkownika", - "updateAdminStatusFailed": "Nie udało się zaktualizować statusu administratora", - "allSessionsRevoked": "Wszystkie sesje zostały odwołane", - "revokeSessionsFailed": "Nie udało się odwołać sesji", - "createRoleRequired": "Nazwa i nazwa wyświetlana są wymagane", - "createRoleSuccess": "Rola \"{{name}}\" została utworzona", - "createRoleFailed": "Nie udało się utworzyć roli", - "apiKeyNameRequired": "Nazwa klucza jest wymagana", - "apiKeyUserRequired": "Identyfikator użytkownika jest wymagany", - "apiKeyCreatedSuccess": "Klucz API \"{{name}}\" został utworzony", - "apiKeyCreateFailed": "Nie udało się utworzyć klucza API", - "exportSuccess": "Baza danych wyeksportowana pomyślnie", - "exportFailed": "Eksport bazy danych nie powiódł się", - "importSelectFile": "Proszę najpierw wybrać plik", - "importCompleted": "Import zakończony: importowane elementy {{total}} , pominięto {{skipped}}", - "importFailed": "Importowanie nie powiodło się: {{error}}", - "importError": "Importowanie bazy danych nie powiodło się" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { "hostLabel": "Host", - "hostPlaceholder": "192.168.1.1 lub przykład.com", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nazwa użytkownika", - "usernamePlaceholder": "nazwa użytkownika", - "authLabel": "Autoryzacja", - "passwordLabel": "Hasło", - "passwordPlaceholder": "hasło", - "privateKeyLabel": "Klucz prywatny", - "privateKeyPlaceholder": "Wklej klucz prywatny...", - "credentialLabel": "Dane logowania", - "credentialPlaceholder": "Wybierz zapisane dane logowania", - "connectToTerminal": "Połącz z terminalem", - "connectToFiles": "Połącz z plikami" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nie wybrano terminala", - "noTerminalSelectedHint": "Otwórz kartę terminala SSH aby wyświetlić historię poleceń", - "searchPlaceholder": "Szukaj historii...", - "clearAll": "Wyczyść wszystko", - "noHistoryEntries": "Brak wpisów historii", - "trackingDisabled": "Śledzenie historii jest wyłączone", - "trackingDisabledHint": "Włącz to w ustawieniach profilu, aby nagrywać komendy." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Nagrywanie kluczy", - "recordToTerminals": "Nagrywanie w terminalach", - "selectAll": "Wszystkie", - "selectNone": "Brak", - "noTerminalTabsOpen": "Brak otwartych kart terminali", - "selectTerminalsAbove": "Wybierz terminale powyżej", - "broadcastInputPlaceholder": "Wpisz tutaj, aby nadawać przyciski klawiszowe...", - "stopRecording": "Zatrzymaj nagrywanie", - "startRecording": "Rozpocznij nagrywanie", - "settingsTitle": "Ustawienia", - "enableRightClickCopyPaste": "Włącz kopiowanie/wklejanie prawym przyciskiem myszy" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Układ", - "selectLayoutAbove": "Wybierz układ powyżej", - "selectLayoutHint": "Wybierz ile paneli chcesz wyświetlić", - "panesTitle": "Panele", - "openTabsTitle": "Otwórz karty", - "dragTabsHint": "Przeciągnij karty do paneli powyżej lub użyj funkcji Szybkie przypisywanie", - "dropHere": "Upuść tutaj", - "emptyPane": "Pusty", - "dashboard": "Pulpit", - "clearSplitScreen": "Wyczyść dzielony ekran", - "quickAssign": "Szybkie przypisanie", - "alreadyAssigned": "Panel {{index}}", - "splitTab": "Podziel kartę", - "addToSplit": "Dodaj do podziału", - "removeFromSplit": "Usuń z podziału", - "assignToPane": "Przypisz do panelu" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Utwórz Snippet", - "createSnippetDescription": "Utwórz nowy fragment komendy do szybkiego wykonania", - "nameLabel": "Nazwisko", - "namePlaceholder": "np. Uruchom ponownie Nginx", - "descriptionLabel": "Opis", - "descriptionPlaceholder": "Opcjonalny opis", - "optional": "Opcjonalnie", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", "folderLabel": "Folder", - "noFolder": "Brak folderu (brak kategorii)", - "commandLabel": "Polecenie", - "commandPlaceholder": "np. sudo systemowy restart nginx", - "cancel": "Anuluj", - "createSnippetButton": "Utwórz Snippet", - "createFolderTitle": "Utwórz folder", - "createFolderDescription": "Organizuj swoje fragmenty w folderach", - "folderNameLabel": "Nazwa folderu", - "folderNamePlaceholder": "np. Komendy systemowe, skrypty Docker", - "folderColorLabel": "Kolor folderu", - "folderIconLabel": "Ikona folderu", - "previewLabel": "Podgląd", - "folderNameFallback": "Nazwa folderu", - "createFolderButton": "Utwórz folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Wszystkie", - "selectNone": "Brak", - "noTerminalTabsOpen": "Brak otwartych kart terminali", - "searchPlaceholder": "Szukaj fragmentów...", - "newSnippet": "Nowy Snippet", - "newFolder": "Nowy folder", - "run": "Uruchom", - "noSnippetsInFolder": "Brak fragmentów w tym folderze", - "uncategorized": "Nieskategoryzowane", - "editSnippetTitle": "Edytuj Snippet", - "editSnippetDescription": "Aktualizuj ten fragment polecenia", - "saveSnippetButton": "Zapisz zmiany", - "createSuccess": "Snippet utworzony pomyślnie", - "createFailed": "Nie udało się utworzyć fragmentu", - "updateSuccess": "Snippet został pomyślnie zaktualizowany", - "updateFailed": "Nie udało się zaktualizować snippet", - "deleteFailed": "Nie udało się usunąć snippet", - "folderCreateSuccess": "Folder utworzony pomyślnie", - "folderCreateFailed": "Nie udało się utworzyć folderu", - "editFolderTitle": "Edytuj folder", - "editFolderDescription": "Zmień nazwę lub zmień wygląd tego folderu", - "saveFolderButton": "Zapisz zmiany", - "editFolder": "Edytuj folder", - "deleteFolder": "Usuń folder", - "folderDeleteSuccess": "Folder \"{{name}}\" usunięto", - "folderDeleteFailed": "Nie udało się usunąć folderu", - "folderEditSuccess": "Folder zaktualizowany pomyślnie", - "folderEditFailed": "Nie udało się zaktualizować folderu", - "confirmRunMessage": "Uruchomić \"{{name}}\"?", - "confirmRunButton": "Uruchomić", - "runSuccess": "Ran \"{{name}}\" w terminalach {{count}}", - "copySuccess": "Skopiowano \"{{name}}\" do schowka", - "shareTitle": "Udostępnij Snippet", - "shareUser": "Użytkownik", - "shareRole": "Rola", - "selectUser": "Wybierz użytkownika...", - "selectRole": "Wybierz rolę...", - "shareSuccess": "Snippet udostępniony pomyślnie", - "shareFailed": "Nie udało się udostępnić snippet", - "revokeSuccess": "Dostęp odwołany", - "revokeFailed": "Nie udało się odwołać dostępu", - "currentAccess": "Bieżący dostęp", - "shareLoadError": "Nie udało się załadować udostępnionych danych", - "loading": "Ładowanie...", - "close": "Zamknij", - "reorderFailed": "Nie udało się zapisać kolejności fragmentów" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Konto", - "sectionAppearance": "Wygląd", - "sectionSecurity": "Bezpieczeństwo", - "sectionApiKeys": "Klucze API", - "sectionC2sTunnels": "Tunele C2S", - "usernameLabel": "Nazwa użytkownika", - "roleLabel": "Rola", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", "roleAdministrator": "Administrator", - "authMethodLabel": "Metoda uwierzytelniania", - "authMethodLocal": "Lokalny", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Włączone", - "twoFaOff": "Wyłączony", - "versionLabel": "Wersja", - "deleteAccount": "Usuń konto", - "deleteAccountDescription": "Trwale usuń swoje konto", - "deleteButton": "Usuń", - "deleteAccountPermanent": "Ta akcja jest trwała i nie może zostać cofnięta.", - "deleteAccountWarning": "Wszystkie sesje, hosty, dane logowania i ustawienia zostaną trwale usunięte.", - "confirmPasswordDeletePlaceholder": "Wprowadź hasło, aby potwierdzić", - "languageLabel": "Język", - "themeLabel": "Motyw", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Kolor akcentu", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Autouzupełnianie polecenia", - "commandAutocompleteDesc": "Pokaż autouzupełnianie podczas pisania", - "historyTracking": "Śledzenie historii", - "historyTrackingDesc": "Śledź polecenia terminala", - "syntaxHighlighting": "Podświetlanie składni", - "syntaxHighlightingDesc": "Podświetl terminal wyjściowy", - "commandPalette": "Paleta Poleceń", - "commandPaletteDesc": "Włącz skrót klawiaturowy", - "reopenTabsOnLogin": "Ponowne otwieranie kart podczas logowania", - "reopenTabsOnLoginDesc": "Przywracaj otwarte karty po zalogowaniu się lub odświeżeniu strony, nawet z innego urządzenia", - "confirmTabClose": "Potwierdź zamknięcie karty", - "confirmTabCloseDesc": "Zapytaj przed zamknięciem kart terminali", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Pokaż tagi hosta", - "showHostTagsDesc": "Wyświetl tagi na liście hostów", - "hostTrayOnClick": "Kliknij, aby rozwinąć działania hosta", - "hostTrayOnClickDesc": "Zawsze pokazuj przyciski połączeń; kliknij, aby rozwinąć opcje zarządzania, zamiast najeżdżać kursorem", - "pinAppRail": "Aplikacja Pin Rail", - "pinAppRailDesc": "Utrzymuj zawsze rozwinięty lewy pasek boczny aplikacji zamiast rozwijać go po najechaniu kursorem", - "settingsSnippets": "Fragmenty", - "foldersCollapsed": "Foldery upadły", - "foldersCollapsedDesc": "Domyślnie zwiń foldery", - "confirmExecution": "Potwierdź wykonanie", - "confirmExecutionDesc": "Potwierdź przed uruchomieniem snippetów", - "settingsUpdates": "Aktualizacje", - "disableUpdateChecks": "Wyłącz sprawdzanie aktualizacji", - "disableUpdateChecksDesc": "Zatrzymaj sprawdzanie aktualizacji", - "totpAuthenticator": "Autoryzacja TOTP", - "totpEnabled": "2FA jest włączona", - "totpDisabled": "Dodaj dodatkowe bezpieczeństwo logowania", - "disable": "Wyłącz", - "enable": "Włącz", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Zeskanuj kod QR lub wprowadź sekret w swojej aplikacji uwierzytelniającej, a następnie wprowadź 6-cyfrowy kod", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Weryfikacja", - "changePassword": "Zmień hasło", - "currentPasswordLabel": "Bieżące hasło", - "currentPasswordPlaceholder": "Aktualne hasło", - "newPasswordLabel": "Nowe hasło", - "newPasswordPlaceholder": "Nowe hasło", - "confirmPasswordLabel": "Potwierdź nowe hasło", - "confirmPasswordPlaceholder": "Potwierdź nowe hasło", - "updatePassword": "Aktualizuj hasło", - "createApiKeyTitle": "Utwórz klucz API", - "createApiKeyDescription": "Wygeneruj nowy klucz API dla dostępu programowego.", - "apiKeyNameLabel": "Nazwisko", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "fakultatywne", - "cancel": "Anuluj", - "createKey": "Utwórz klucz", - "apiKeyCount": "{{count}} klucze", - "newKey": "Nowy klucz", - "noApiKeys": "Brak kluczy API.", - "apiKeyActive": "Aktywne", - "apiKeyUsageHint": "Dołącz swój klucz do", - "apiKeyUsageHintHeader": "nagłówek.", - "apiKeyPermissionsHint": "Klucze odziedziczą uprawnienia tworzącego użytkownika.", - "roleUser": "Użytkownik", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Nie udało się uruchomić konfiguracji TOTP", - "totpEnter6Digits": "Wprowadź 6-cyfrowy kod", - "totpEnabledSuccess": "Uwierzytelnianie dwuskładnikowe włączone", - "totpInvalidCode": "Nieprawidłowy kod, spróbuj ponownie", - "totpDisableInputRequired": "Wprowadź kod TOTP lub hasło", - "totpDisabledSuccess": "Uwierzytelnianie dwuskładnikowe wyłączone", - "totpDisableFailed": "Nie udało się wyłączyć 2FA", - "totpDisableTitle": "Wyłącz 2FA", - "totpDisablePlaceholder": "Wprowadź kod TOTP lub hasło", - "totpDisableConfirm": "Wyłącz 2FA", - "totpContinueVerify": "Kontynuuj weryfikację", - "totpVerifyTitle": "Potwierdź kod", - "totpBackupTitle": "Kody kopii zapasowych", - "totpDownloadBackup": "Pobierz kody kopii zapasowych", - "done": "Gotowe", - "secretCopied": "Sekretne skopiowane do schowka", - "apiKeyNameRequired": "Nazwa klucza jest wymagana", - "apiKeyCreated": "Klucz API \"{{name}}\" został utworzony", - "apiKeyCreateFailed": "Nie udało się utworzyć klucza API", - "apiKeyUser": "Użytkownik", - "apiKeyExpires": "Wygasa", - "apiKeyRevoked": "Klucz API \"{{name}}\" odwołany", - "apiKeyRevokeFailed": "Nie udało się odwołać klucza API", - "passwordFieldsRequired": "Wymagane są aktualne i nowe hasła", - "passwordMismatch": "Hasła nie pasują", - "passwordTooShort": "Hasło musi mieć co najmniej 6 znaków", - "passwordUpdated": "Hasło zaktualizowane pomyślnie", - "passwordUpdateFailed": "Nie udało się zaktualizować hasła", - "deletePasswordRequired": "Aby usunąć Twoje konto wymagane jest hasło.", - "deleteFailed": "Nie udało się usunąć konta", - "deleting": "Usuwanie...", - "colorPickerTooltip": "Otwórz selektor kolorów", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", "themeSystem": "System", - "themeLight": "Światło", - "themeDark": "Ciemny", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solaryzowane", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Jedno ciemne", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/pt_BR.json b/src/ui/locales/translated/pt_BR.json index 53afe286..9bc5af32 100644 --- a/src/ui/locales/translated/pt_BR.json +++ b/src/ui/locales/translated/pt_BR.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Pastas", - "folder": "pasta", - "password": "Palavra-passe", - "key": "Chave", - "sshPrivateKey": "Chave privada SSH", - "upload": "Transferir", - "keyPassword": "Senha da Chave", - "sshKey": "Chave SSH", - "uploadPrivateKeyFile": "Carregar arquivo de chave privada", - "searchCredentials": "Pesquisar credenciais...", - "addCredential": "Adicionar Credencial", - "caCertificate": "Certificado CA (-cert.pub)", - "caCertificateDescription": "Opcional: Carregar ou colar o arquivo de certificado assinado por CA (por exemplo, id_ed25519-cert.pub). Necessário quando o servidor SSH utiliza autorização baseada em certificados.", - "uploadCertFile": "Enviar arquivo -cert.pub", - "clearCert": "Limpar", - "certLoaded": "Certificado carregado", - "certPublicKeyLabel": "Certificado CA", - "certTypeLabel": "Tipo de certificado", - "pasteOrUploadCert": "Colar ou fazer upload de um certificado -cert.pub...", - "hasCaCert": "Possui certificado CA", - "noCaCert": "Nenhum certificado CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Falha ao carregar alertas", - "failedToDismissAlert": "Falha ao descartar alerta" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configuração Servidor", - "description": "Configure o URL do servidor do Termix para conectar aos seus serviços de backend", - "serverUrl": "URL do servidor", - "enterServerUrl": "Por favor, insira uma URL de servidor", - "saveFailed": "Falha ao salvar a configuração", - "saveError": "Erro ao salvar configuração", - "saving": "Salvando...", - "saveConfig": "Salvar configuração", - "helpText": "Digite a URL onde o servidor do seu Termix está executando (por exemplo, http://localhost:30001 ou https://seu-servidor.com)", - "changeServer": "Alterar Servidor", - "mustIncludeProtocol": "O URL do servidor deve começar com http:// ou https://", - "allowInvalidCertificate": "Permitir certificado inválido", - "allowInvalidCertificateDesc": "Utilize somente em servidores autohospedados confiáveis com certificados autoassinados ou de endereço IP.", - "useEmbedded": "Usar servidor local", - "embeddedDesc": "Executar Termix com o servidor local embutido (não é necessário um servidor remoto)", - "embeddedConnecting": "Conectando ao servidor local...", - "embeddedNotReady": "O servidor local ainda não está pronto. Por favor, aguarde um momento e tente novamente.", - "localServer": "Servidor local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Erro de verificação de versão", - "checkFailed": "Não foi possível verificar se há atualizações", - "upToDate": "O aplicativo está atualizado", - "currentVersion": "Você está executando a versão {{version}}", - "updateAvailable": "Atualização disponível", - "newVersionAvailable": "Uma nova versão está disponível! Você está executando {{current}}, mas {{latest}} está disponível.", - "betaVersion": "Versão Beta", - "betaVersionDesc": "Você está executando {{current}}, que é mais recente que a última versão estável {{latest}}.", - "releasedOn": "Lançado em {{date}}", - "downloadUpdate": "Baixar atualização", - "checking": "Verificando atualizações...", - "checkUpdates": "Procurar por atualizações", - "checkingUpdates": "Verificando atualizações...", - "updateRequired": "Atualização Necessária" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "FECHAR", + "close": "Close", "minimize": "Minimize", - "online": "Disponível", - "offline": "Desconectado", - "continue": "Continuar", - "maintenance": "Manutenção", - "degraded": "Degradado", - "error": "ERRO", - "warning": "ATENÇÃO", - "unsavedChanges": "Alterações não salvas", - "dismiss": "Descartar", - "loading": "Carregandochar@@0", - "optional": "Opcional", - "connect": "Conectar", - "copied": "Copiado", - "connecting": "Conectandochar@@0", - "updateAvailable": "Atualização disponível", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Abrir em nova aba", - "noReleases": "Sem lançamentos", - "updatesAndReleases": "Atualizações e Versões", - "newVersionAvailable": "Uma nova versão ({{version}}) está disponível.", - "failedToFetchUpdateInfo": "Falha ao buscar informações de atualização", - "preRelease": "Pré-lançamento", - "noReleasesFound": "Nenhuma versão encontrada.", - "cancel": "cancelar", - "username": "Usuário:", - "login": "Conectar-se", - "register": "Cadastrar", - "password": "Palavra-passe", - "confirmPassword": "Confirmar senha", - "back": "Anterior", - "save": "Guardar", - "saving": "Salvando...", - "delete": "excluir", - "rename": "Renomear", - "edit": "Alterar", - "add": "Adicionar", - "confirm": "Confirmar", - "no": "Não", - "or": "ou", - "next": "Próximo", - "previous": "Anterior", - "refresh": "atualizar", - "language": "IDIOMA", - "checking": "Verificandochar@@0", - "checkingDatabase": "Verificando conexão com o banco de dados...", - "checkingAuthentication": "Verificando autenticação...", - "backendReconnected": "Conexão do servidor restaurada", - "connectionDegraded": "Conexão do servidor perdida, recuperando…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Excluir", - "create": "Crio", - "update": "Atualização", - "copy": "copiar", - "copyFailed": "Falha ao copiar para área de transferência", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "RESTAURAR", - "of": "de" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Inicio", + "home": "Home", "terminal": "Terminal", - "docker": "Atracador", - "tunnels": "Túneis", - "fileManager": "Gerenciador de Arquivos", - "serverStats": "Estatísticas do servidor", - "admin": "Administrador", - "userProfile": "Informações do Perfil", - "splitScreen": "Dividir a tela", - "confirmClose": "Fechar esta sessão ativa?", - "close": "FECHAR", - "cancel": "cancelar", - "sshManager": "Gerenciador SSH", - "cannotSplitTab": "Não é possível dividir esta aba", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copiar Senha", - "copySudoPassword": "Copiar Senha do Sudo", - "passwordCopied": "Senha copiada para área de transferência", - "noPasswordAvailable": "Nenhuma senha disponível", - "failedToCopyPassword": "Falha ao copiar a senha", - "refreshTab": "Atualizar conexão", - "openFileManager": "Abrir Gerenciador de Arquivos", - "dashboard": "Painel", - "networkGraph": "Gráfico da Rede", - "quickConnect": "Conexão rápida", - "sshTools": "Ferramentas SSH", - "history": "Histórico", - "hosts": "Anfitriões", - "snippets": "Trechos", - "hostManager": "Gerenciador de Host", - "credentials": "Credenciais", - "connections": "Conexões", - "roleAdministrator": "Administrador", - "roleUser": "Usuário" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Anfitriões", - "noHosts": "Nenhum host SSH", - "retry": "Repetir", - "refresh": "atualizar", - "optional": "Opcional", - "downloadSample": "Baixar Exemplo", - "failedToDeleteHost": "Falha ao excluir {{name}}", - "importSkipExisting": "Importar (ignorar existente)", - "connectionDetails": "Detalhes da conexão", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Computador Remoto", - "port": "Porta", - "username": "Usuário:", - "folder": "pasta", - "tags": "Etiquetas", - "pin": "PIN", - "addHost": "Adicionar Host", - "editHost": "Editar Host", - "cloneHost": "Clonar Host", - "enableTerminal": "Ativar Terminal", - "enableTunnel": "Ativar Túnel", - "enableFileManager": "Ativar Gerenciador de Arquivos", - "enableDocker": "Ativar Docker", - "defaultPath": "Caminho Padrão", - "connection": "Ligação", - "upload": "Transferir", - "authentication": "Autenticação", - "password": "Palavra-passe", - "key": "Chave", - "credential": "Credencial", - "none": "Nenhuma", - "sshPrivateKey": "Chave privada SSH", - "keyType": "Tipo de chave", - "uploadFile": "Enviar Arquivo", - "tabGeneral": "Gerais", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Túneis", - "tabDocker": "Atracador", - "tabFiles": "arquivos", - "tabStats": "Estatísticas", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Compartilhando", - "tabAuthentication": "Autenticação", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Túnel", - "fileManager": "Gerenciador de Arquivos", - "serverStats": "Estatísticas do servidor", - "status": "SItuação", - "folderRenamed": "Pasta \"{{oldName}}\" renomeada para \"{{newName}}\" com sucesso", - "failedToRenameFolder": "Falha ao renomear pasta", - "movedToFolder": "Movido para \"{{folder}}\"", - "editHostTooltip": "Editar host", - "statusChecks": "Verificações de Status", - "metricsCollection": "Coleção de Métricas", - "metricsInterval": "Intervalo de Coleção de Métricas", - "metricsIntervalDesc": "Com que frequência coletar estatísticas do servidor (5s - 1h)", - "behavior": "Comportamento", - "themePreview": "Pré-visualização do tema", - "theme": "Tema", - "fontFamily": "Família de fonte", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Espaçamento das letras", - "lineHeight": "Altura da linha", - "cursorStyle": "Estilo do cursor", - "cursorBlink": "Pisca do Cursor", - "scrollbackBuffer": "Buffer de rolagem", - "bellStyle": "Estilo do sino", - "rightClickSelectsWord": "Clique com botão direito seleciona Palavra", - "fastScrollModifier": "Modificador de rolagem rápido", - "fastScrollSensitivity": "Sensibilidade de rolagem rápida", - "sshAgentForwarding": "Encaminhamento de agente SSH", - "backspaceMode": "Modo Backspace", - "startupSnippet": "Trecho de Inicialização", - "selectSnippet": "Selecionar snippet", - "forceKeyboardInteractive": "Forçar teclado interativo", - "overrideCredentialUsername": "Substituir o nome de usuário credencial", - "overrideCredentialUsernameDesc": "Use o nome de usuário especificado acima ao invés do nome de usuário da credencial", - "jumpHostChain": "Corrente de Host Salto", - "portKnocking": "Knocking de Porta", - "addKnock": "Adicionar Porta", - "addProxyNode": "Adicionar node", - "proxyNode": "Nó Proxy", - "proxyType": "Tipo de proxy", - "quickActions": "Ações rápidas", - "sudoPasswordAutoFill": "Auto-preenchimento de Senha Sudo", - "sudoPassword": "Senha Sudo", - "keepaliveInterval": "Intervalo de Mantícora (ms)", - "moshCommand": "Comando MOSH", - "environmentVariables": "Variáveis de Ambiente", - "addVariable": "Adicionar Variável", - "docker": "Atracador", - "copyTerminalUrl": "Copiar URL do Terminal", - "copyFileManagerUrl": "Copiar URL do Gerenciador de Arquivos", - "copyRemoteDesktopUrl": "Copiar URL do Desktop Remoto", - "failedToConnect": "Falha ao conectar ao console", - "connect": "Conectar", - "disconnect": "Desconectar", - "start": "Iniciar", - "enableStatusCheck": "Ativar verificação de status", - "enableMetrics": "Habilitar métricas", - "bulkUpdateFailed": "Atualização em massa falhou", - "selectAll": "Selecionar Todos", - "deselectAll": "Desmarcar Todos", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Projétil Seguro", - "virtualNetwork": "Rede Virtual", - "unencryptedShell": "shell não criptografado", - "addressIp": "Endereço / IP", - "friendlyName": "Nome Amigável", - "folderAndAdvanced": "Pasta & Avançada", - "privateNotes": "Notas do trabalho", - "privateNotesPlaceholder": "Detalhes sobre este servidor...", - "pinToTop": "Fixar no topo", - "pinToTopDesc": "Sempre mostrar este host no topo da lista", - "portKnockingSequence": "Sequência de Portas Knocking", - "addKnockBtn": "Adicionar toco", - "noPortKnocking": "Nenhuma porta ativa configurada.", - "knockPort": "Porta do Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Atraso após (ms)", - "useSocks5Proxy": "Usar Proxy SOCKS5", - "useSocks5ProxyDesc": "Conexão de rota através de um servidor proxy", - "proxyHost": "Servidor de Proxy", - "proxyPort": "Porta do Proxy", - "proxyUsername": "Usuário do Proxy", - "proxyPassword": "Senha do Proxy", - "proxySingleMode": "Proxy Único", - "proxyChainMode": "Corrente de Proxy", - "you": "Tu", - "jumpHostChainLabel": "Corrente de Host Salto", - "addJumpBtn": "Adicionar Salto", - "noJumpHosts": "Nenhum host de salto configurado.", - "selectAServer": "Selecione um servidor...", - "sshPort": "Porta SSH", - "authMethod": "Método de Autenticação", - "storedCredential": "Credenciais armazenadas", - "selectACredential": "Selecione uma credencial...", - "keyTypeLabel": "Tipo de chave", - "keyTypeAuto": "Detecção automática", - "keyPasteTab": "Colar", - "keyUploadTab": "Transferir", - "keyFileLoaded": "Arquivo de chave carregado", - "keyUploadClick": "Clique para enviar .pem / .key / .ppk", - "clearKey": "Limpar Chave", - "keySaved": "Chave SSH salva", - "keyReplaceNotice": "colar uma nova chave abaixo para substituí-la", - "keyPassphraseSaved": "Senha salva, digite a mudar", - "replaceKey": "Substituir chave", - "forceKeyboardInteractiveLabel": "Forçar teclado interativo", - "forceKeyboardInteractiveShortDesc": "Forçar entrada de senha manual, mesmo se chaves estiverem presentes", - "terminalAppearance": "Aparência do Terminal", - "colorTheme": "Tema de Cor", - "fontFamilyLabel": "Família de fonte", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Estilo do cursor", - "letterSpacingPx": "Espaçamento das letras (px)", - "lineHeightLabel": "Altura da linha", - "bellStyleLabel": "Estilo do sino", - "backspaceModeLabel": "Modo Backspace", - "cursorBlinking": "Piscar cursor", - "cursorBlinkingDesc": "Ativar animação piscante para o cursor do terminal", - "rightClickSelectsWordLabel": "Clique com botão direito seleciona Palavra", - "rightClickSelectsWordShortDesc": "Selecione a palavra no botão direito do mouse", - "behaviorAndAdvanced": "Comportamento & Avançado", - "scrollbackBufferLabel": "Buffer de rolagem", - "scrollbackMaxLines": "Número máximo de linhas mantidas no histórico", - "sshAgentForwardingLabel": "Encaminhamento de agente SSH", - "sshAgentForwardingShortDesc": "Passe suas chaves SSH locais para este host", - "enableAutoMosh": "Habilitar Mosh Automático", - "enableAutoMoshDesc": "Prefere Mosh por SSH se disponível", - "enableAutoTmux": "Ativar Tmux Automático", - "enableAutoTmuxDesc": "Iniciar ou anexar automaticamente à sessão tmux", - "sudoPasswordAutoFillLabel": "Auto-preenchimento de Senha Sudo", - "sudoPasswordAutoFillShortDesc": "Automaticamente fornecer senha sudo quando solicitado", - "sudoPasswordLabel": "Senha Sudo", - "environmentVariablesLabel": "Variáveis de Ambiente", - "addVariableBtn": "Adicionar Variável", - "noEnvVars": "Nenhuma variável de ambiente configurada.", - "fastScrollModifierLabel": "Modificador de rolagem rápido", - "fastScrollSensitivityLabel": "Sensibilidade de rolagem rápida", - "moshCommandLabel": "Comando Mosh", - "startupSnippetLabel": "Trecho de Inicialização", - "keepaliveIntervalLabel": "Intervalo de Keepalive (segundos)", - "maxKeepaliveMisses": "Máximo de Perdas Keepalive", - "tunnelSettings": "Configurações de túnel", - "enableTunneling": "Ativar Túnel", - "enableTunnelingDesc": "Ativar a funcionalidade de túnel SSH para esse host", - "serverTunnelsSection": "Túneis do servidor", - "addTunnelBtn": "Adicionar túnel", - "noTunnelsConfigured": "Nenhum túnel configurado.", - "tunnelLabel": "Túnel {{number}}", - "tunnelType": "Tipo de túnel", - "tunnelModeLocalDesc": "Encaminhar uma porta local para uma porta no servidor remoto (ou um host acessível a partir da).", - "tunnelModeRemoteDesc": "Encaminha uma porta no servidor remoto de volta para uma porta local na sua máquina.", - "tunnelModeDynamicDesc": "Crie um proxy SOCKS5 em uma porta local para encaminhamento de porta dinâmica.", - "sameHost": "Este host (túnel direto)", - "endpointHost": "Host de Endpoint", - "endpointPort": "Porta de Endpoint", - "bindHost": "Vincular Host", - "sourcePort": "Porta de origem", - "maxRetries": "Máximo de buscas", - "retryIntervalS": "Intervalo de Repetir", - "autoStartLabel": "Auto-iniciar", - "autoStartDesc": "Conectar automaticamente este túnel quando o host é carregado", - "tunnelConnecting": "Túnel conectando...", - "tunnelDisconnected": "Túnel desconectado", - "failedToConnectTunnel": "Falha ao conectar", - "failedToDisconnectTunnel": "Falha ao desconectar", - "dockerIntegration": "Integração Docker", - "enableDockerMonitor": "Ativar Docker", - "enableDockerMonitorDesc": "Monitorar e gerenciar contêineres neste host via Docker", - "enableFileManagerMonitor": "Ativar Gerenciador de Arquivos", - "enableFileManagerMonitorDesc": "Navegue e gerencie arquivos neste host através do SFTP", - "defaultPathLabel": "Caminho Padrão", - "fileManagerPathHint": "O diretório a abrir quando o gerenciador de arquivos é aberto para este host.", - "statusChecksLabel": "Verificações de Status", - "enableStatusChecks": "Habilitar Verificação de Status", - "enableStatusChecksDesc": "Periodicamente ping este host para verificar a disponibilidade", - "useGlobalInterval": "Usar Intervalo Global", - "useGlobalIntervalDesc": "Substituir com o intervalo de verificação de status de todo o servidor", - "checkIntervalS": "Intervalo de verificação", - "checkIntervalDesc": "Segundos entre cada ping de conectividade", - "metricsCollectionLabel": "Coleção de Métricas", - "enableMetricsLabel": "Habilitar métricas", - "enableMetricsDesc": "Coletar CPU, RAM, disco e uso de rede deste host", - "useGlobalMetrics": "Usar Intervalo Global", - "useGlobalMetricsDesc": "Sobrescrever com o intervalo de métricas para o servidor", - "metricsIntervalS": "Intervalo de Métricas", - "metricsIntervalDesc2": "Segundos entre snapshots métricos", - "visibleWidgets": "Widgets visíveis", - "cpuUsageLabel": "Uso da CPU", - "cpuUsageDesc": "CPU por cento, carregar médias de carga, gráfico de linha", - "memoryLabel": "Memória Utilizada", - "memoryDesc": "Uso de RAM, troque, em cache", - "storageLabel": "Uso do disco", - "storageDesc": "Uso do disco por ponto de montagem", - "networkLabel": "Interfaces de Rede", - "networkDesc": "Lista de interface e banda", - "uptimeLabel": "Tempo em atividade", - "uptimeDesc": "Tempo de atividade e inicialização do sistema", - "systemInfoLabel": "Informações do Sistema", - "systemInfoDesc": "OS, kernel, hostname, arquitetura", - "recentLoginsLabel": "Logins recentes", - "recentLoginsDesc": "Eventos bem-sucedidos e falhados de login", - "topProcessesLabel": "Processos mais Populares", - "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Porta de Escuta", - "listeningPortsDesc": "Abrir portas com processo e estado", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, status do SELinux", - "quickActionsLabel": "Ações rápidas", - "quickActionsToolbar": "Ações rápidas aparecem como botões na barra de estatísticas do servidor para execução de comando com um clique.", - "noQuickActions": "Nenhuma ação rápida ainda.", - "buttonLabel": "Rótulo do botão", - "selectSnippetPlaceholder": "Selecionar snippet...", - "addActionBtn": "Adicionar ação", - "hostSharedSuccessfully": "Hospedeiro compartilhado com sucesso", - "failedToShareHost": "Falha ao compartilhar host", - "accessRevoked": "Acesso revogado", - "failedToRevokeAccess": "Falha ao revogar acesso", - "cancelBtn": "cancelar", - "savingBtn": "Salvando...", - "addHostBtn": "Adicionar Host", - "hostUpdated": "Servidor atualizado", - "hostCreated": "Host criado", - "failedToSave": "Falha ao salvar o host", - "credentialUpdated": "Credencial atualizada", - "credentialCreated": "Credencial criada", - "failedToSaveCredential": "Falha ao salvar credencial", - "backToHosts": "Voltar aos hosts", - "backToCredentials": "Voltar às Credenciais", - "pinned": "Fixado", - "noHostsFound": "Nenhum host encontrado", - "tryDifferentTerm": "Tente um termo diferente", - "addFirstHost": "Adicione o seu primeiro host para começar", - "noCredentialsFound": "Nenhuma credencial encontrada", - "addCredentialBtn": "Adicionar Credencial", - "updateCredentialBtn": "Atualizar Credencial", - "features": "Funcionalidades", - "noFolder": "(Sem pasta)", - "deleteSelected": "excluir", - "exitSelection": "Sair da seleção", - "importSkip": "Importar (ignorar existente)", - "importOverwrite": "Importação (sobrescrever)", - "collapseBtn": "Recolher", - "importExportBtn": "Importar / Exportar", - "hostStatusesRefreshed": "Status do host atualizados", - "failedToRefreshHosts": "Falha ao atualizar hosts", - "movedHostTo": "Mudou {{host}} para \"{{folder}}\"", - "failedToMoveHost": "Falha ao mover host", - "folderRenamedTo": "Pasta renomeada para \"{{name}}\"", - "deletedFolder": "Pasta apagada \"{{name}}\"", - "failedToDeleteFolder": "Falha ao excluir pasta", - "deleteAllInFolder": "Excluir todos os hosts em \"{{name}}\"? Isso não pode ser desfeito.", - "deletedHost": "{{name}} Excluído", - "copiedToClipboard": "Copiado para o clipboard", - "terminalUrlCopied": "URL do terminal copiado", - "fileManagerUrlCopied": "URL do Gerenciador de Arquivos copiado", - "tunnelUrlCopied": "URL do túnel copiado", - "dockerUrlCopied": "URL Docker copiado", - "serverStatsUrlCopied": "URL das estatísticas do servidor copiado", - "rdpUrlCopied": "URL RDP copiada", - "vncUrlCopied": "URL VNC copiada", - "telnetUrlCopied": "URL da rede copiada", - "remoteDesktopUrlCopied": "URL da área de trabalho remoto copiada", - "expandActions": "Expandir ações", - "collapseActions": "Ações de recolhimento", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Pacote mágico enviado para {{name}}", - "wakeOnLanError": "Falha ao enviar o pacote mágico", - "cloneHostAction": "Clonar Host", - "copyAddress": "Copiar Endereço", - "copyLink": "Copiar link", - "copyTerminalUrlAction": "Copiar URL do Terminal", - "copyFileManagerUrlAction": "Copiar URL do Gerenciador de Arquivos", - "copyTunnelUrlAction": "Copiar URL do túnel", - "copyDockerUrlAction": "Copiar URL do Docker", - "copyServerStatsUrlAction": "Copiar URL das Estatísticas do Servidor", - "copyRdpUrlAction": "Copiar URL do RDP", - "copyVncUrlAction": "Copiar URL do VNC", - "copyTelnetUrlAction": "Copiar URL da Telnet", - "copyRemoteDesktopUrlAction": "Copiar URL do Desktop Remoto", - "deleteCredentialConfirm": "Excluir credencial \"{{name}}\"?", - "deletedCredential": "{{name}} Excluído", - "deploySSHKeyTitle": "Chave SSH de deploy", - "deployingBtn": "Implementando...", - "deployBtn": "Implantar", - "failedToDeployKey": "Falha ao publicar chave", - "deleteHostsConfirm": "Excluir o host {{count}}{{plural}}? Isso não pode ser desfeito.", - "movedToRoot": "Movido para a raiz", - "failedToMoveHosts": "Falha ao mover hosts", - "enableTerminalFeature": "Ativar Terminal", - "disableTerminalFeature": "Desativar Terminal", - "enableFilesFeature": "Habilitar arquivos", - "disableFilesFeature": "Desabilitar Arquivos", - "enableTunnelsFeature": "Ativar túneis", - "disableTunnelsFeature": "Desativar Túneis", - "enableDockerFeature": "Ativar Docker", - "disableDockerFeature": "Desativar Docker", - "addTagsPlaceholder": "Adicionar tags...", - "authDetails": "Detalhes de autenticação", - "credType": "tipo", - "generateKeyPairDesc": "Gerar um novo par de chaves, chaves privadas e públicas serão preenchidas automaticamente.", - "generatingKey": "Gerando...", - "generateLabel": "Gerar {{label}}", - "uploadFileBtn": "Upload de arquivo", - "keyPassphraseOptional": "Senha Chave (Opcional)", - "sshPublicKeyOptional": "Chave Pública SSH (Opcional)", - "publicKeyGenerated": "Chave pública gerada", - "failedToGeneratePublicKey": "Falha ao derivar chave pública", - "publicKeyCopied": "Chave pública copiada", - "keyPairGenerated": "Um par de chaves {{label}} gerado", - "failedToGenerateKeyPair": "Falha ao gerar o par de chaves", - "searchHostsPlaceholder": "Pesquisar hospedeiros, endereços, etiquetas…", - "searchCredentialsPlaceholder": "Pesquisar credenciais…", - "refreshBtn": "atualizar", - "addTag": "Adicionar tags...", - "deleteConfirmBtn": "excluir", - "tunnelRequirementsText": "O servidor SSH deve ter GatewayPorts sim, AllowTcpForwarding yes, e permitRootLogin yes definido em /etc/ssh/sshd_config.", - "deleteHostConfirm": "Excluir \"{{name}}\"?", - "enableAtLeastOneProtocol": "Ative pelo menos um protocolo acima para configurar as configurações de autenticação e conexão.", - "keyPassphrase": "Senha Chave", - "connectBtn": "Conectar", - "disconnectBtn": "Desconectar", - "basicInformation": "Informações Básicas", - "authDetailsSection": "Detalhes de autenticação", - "credTypeLabel": "tipo", - "hostsTab": "Anfitriões", - "credentialsTab": "Credenciais", - "selectMultiple": "Selecionar múltiplos", - "selectHosts": "Selecionar hosts", - "connectionLabel": "Ligação", - "authenticationLabel": "Autenticação", - "generateKeyPairTitle": "Gerar par de chaves", - "generateKeyPairDescription": "Gerar um novo par de chaves, chaves privadas e públicas serão preenchidas automaticamente.", - "generateFromPrivateKey": "Gerar a partir da Chave Privada", - "refreshBtn2": "atualizar", - "exitSelectionTitle": "Sair da seleção", - "exportAll": "Exportar tudo", - "addHostBtn2": "Adicionar Host", - "addCredentialBtn2": "Adicionar Credencial", - "checkingHostStatuses": "Verificando status do host...", - "pinnedSection": "Fixado", - "hostsExported": "Hosts exportados com sucesso", - "exportFailed": "Falha ao exportar hosts", - "sampleDownloaded": "Exemplo de arquivo baixado", - "failedToDeleteCredential2": "Falha ao excluir credencial", - "noFolderOption": "(Sem pasta)", - "nSelected": "{{count}} selecionado", - "featuresMenu": "Funcionalidades", - "moveMenu": "Mover-se", - "cancelSelection": "cancelar", - "deployDialogDesc": "Implantar {{name}} em um host authorized_keys.", - "targetHostLabel": "Host de destino", - "selectHostOption": "Selecione um host...", - "keyDeployedSuccess": "Chave implantada com sucesso", - "failedToDeployKey2": "Falha ao publicar chave", - "deletedCount": "Hosts {{count}} excluídos", - "failedToDeleteCount": "Falha ao excluir hosts {{count}}", - "duplicatedHost": "Duplicado \"{{name}}\"", - "failedToDuplicateHost": "Falha ao duplicar host", - "updatedCount": "Hosts {{count}} atualizados", - "friendlyNameLabel": "Nome Amigável", - "descriptionLabel": "Descrição:", - "loadingHost": "Carregando host...", - "loadingHosts": "Carregando hosts...", - "loadingCredentials": "Carregando credenciais...", - "noHostsYet": "Nenhum host ainda", - "noHostsMatchSearch": "Nenhum host corresponde à sua pesquisa", - "hostNotFound": "Host não encontrado", - "searchHosts": "Procurar hosts...", - "sortHosts": "Classificar hosts", - "sortDefault": "Ordem padrão", - "sortNameAsc": "Nome (A → Z)", - "sortNameDesc": "Nome (Z → A)", - "sortIpAsc": "Endereço IP (Asc)", - "sortIpDesc": "Endereço IP (Descrição)", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", "sortOnlineFirst": "Online First", - "sortOfflineFirst": "Primeiro offline", - "sortPinnedFirst": "Fixado primeiro", - "filterHosts": "Filtrar hosts", - "filterClearAll": "Limpar filtros", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", - "filterOnline": "On-line", + "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fixado", - "filterAuthGroup": "Tipo de autenticação", - "filterAuthPassword": "Senha", - "filterAuthKey": "Chave SSH", - "filterAuthCredential": "Credencial", - "filterAuthNone": "Nenhum", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protocolo", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Características", - "filterFeatureTerminal": "terminal", - "filterFeatureFileManager": "Gerenciador de arquivos", - "filterFeatureTunnel": "Túnel", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etiquetas", - "shareHost": "Host de compartilhamento", - "shareHostTitle": "Compartilhar: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Este host deve usar uma credencial para permitir o compartilhamento. Edite o host e atribua uma credencial primeiro." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Ligação", - "authentication": "Autenticação", - "connectionSettings": "Configurações de conexão", - "displaySettings": "Configurações de exibição", - "audioSettings": "Configurações de Áudio", - "rdpPerformance": "Desempenho RDP", - "deviceRedirection": "Redirecionamento do dispositivo", - "session": "Sessão", - "gateway": "Desvio", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Área", - "sessionRecording": "Gravação de Sessão", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Configurações do VNC", - "terminalSettings": "Configurações do Terminal", - "rdpPort": "Porta RDP", - "username": "Usuário:", - "password": "Palavra-passe", - "domain": "Domínio", - "securityMode": "Modo de Segurança", - "colorDepth": "Profundidade da Cor", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Altura", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Método de Redimensionar", - "clientName": "Nome do Cliente", - "initialProgram": "Programa inicial", - "serverLayout": "Layout do Servidor", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Porta do Gateway", - "gatewayUsername": "Usuário do Gateway", - "gatewayPassword": "Senha do Gateway", - "gatewayDomain": "Domínio de Gateway", - "remoteAppProgram": "Programa RemoteApp", - "workingDirectory": "Diretório de trabalho", - "arguments": "Parâmetros", - "normalizeLineEndings": "Normalizar Extensões de Linha", - "recordingPath": "Caminho de gravação", - "recordingName": "Nome da gravação", - "macAddress": "Endereço MAC", - "broadcastAddress": "Endereço de Transmissão", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Tempo de Espera", - "driveName": "Nome da unidade", - "drivePath": "Caminho do Drive", - "ignoreCertificate": "Ignorar Certificado", - "ignoreCertificateDesc": "Permitir conexões para hosts com certificados auto-assinados", - "forceLossless": "Forçar perda", - "forceLosslessDesc": "Forçar codificação de imagens sem perdas (maior qualidade, mais largura de banda)", - "disableAudio": "Desativar Áudio", - "disableAudioDesc": "Silenciar todo o áudio da sessão remota", - "enableAudioInput": "Habilitar Entrada de Áudio (Microfone)", - "enableAudioInputDesc": "Encaminhar o microfone local para a sessão remota", - "wallpaper": "Plano de fundo", - "wallpaperDesc": "Mostrar plano de fundo da área de trabalho (desabilitar melhora o desempenho)", - "theming": "Temas", - "themingDesc": "Ativar temas e estilos visuais", - "fontSmoothing": "Suavização da Fonte", - "fontSmoothingDesc": "Habilitar renderização de fonte ClearType", - "fullWindowDrag": "Arrastar a janela completa", - "fullWindowDragDesc": "Mostrar conteúdo da janela ao arrastar", - "desktopComposition": "Composição da mesa", - "desktopCompositionDesc": "Habilitar efeitos de vidro Aero", - "menuAnimations": "Animações do menu", - "menuAnimationsDesc": "Ativar animações de deslize e deslize do menu", - "disableBitmapCaching": "Desabilitar Cache Bitmap", - "disableBitmapCachingDesc": "Desativar o cache do bitmap (pode ajudar com glitches)", - "disableOffscreenCaching": "Desativar Cache Offscreen", - "disableOffscreenCachingDesc": "Desativar cache de tela desligada", - "disableGlyphCaching": "Desativar Cache Glyph", - "disableGlyphCachingDesc": "Desligar cache glifo", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Usar RemoteFX pipeline graphics", - "enablePrinting": "Ativar impressão", - "enablePrintingDesc": "Redirecionar impressoras locais para a sessão remota", - "enableDriveRedirection": "Habilitar redirecionamento de unidade", - "enableDriveRedirectionDesc": "Mapear uma pasta local como um drive na sessão remota", - "createDrivePath": "Criar caminho de unidade", - "createDrivePathDesc": "Criar automaticamente a pasta se ela não existir", - "disableDownload": "Desativar download", - "disableDownloadDesc": "Impedir o download de arquivos da sessão remota", - "disableUpload": "Desabilitar Upload", - "disableUploadDesc": "Evitar o envio de arquivos para a sessão remota", - "enableTouch": "Ativar Touch", - "enableTouchDesc": "Ativar encaminhamento de entrada de toque", - "consoleSession": "Sessão de Console", - "consoleSessionDesc": "Conectar ao console (sessão 0) em vez de uma nova sessão", - "sendWolPacket": "Enviar Pacote WOL", - "sendWolPacketDesc": "Envie um pacote mágico para acordar este host antes de conectar", - "disableCopy": "Desativar Cópia", - "disableCopyDesc": "Impedir a cópia de texto da sessão remota", - "disablePaste": "Desativar Colar", - "disablePasteDesc": "Impedir colar texto na sessão remota", - "createPathIfMissing": "Criar caminho se estiver faltando", - "createPathIfMissingDesc": "Criar automaticamente o diretório de gravação", - "excludeOutput": "Excluir saída", - "excludeOutputDesc": "Não gravar a saída da tela (apenas metadados)", - "excludeMouse": "Excluir mouse", - "excludeMouseDesc": "Não grave os movimentos do mouse", - "includeKeystrokes": "Incluir Teclas Pressionadas", - "includeKeystrokesDesc": "Gravar teclas rasteiras brutas além da saída da tela", - "vncPort": "Porta VNC", - "vncPassword": "Senha VNC", - "vncUsernameOptional": "Nome de usuário (opcional)", - "vncLeaveBlank": "Deixe em branco se não necessário", - "cursorMode": "Modo Cursor", - "swapRedBlue": "Trocar Vermelho/Azul", - "swapRedBlueDesc": "Troque os canais de cor vermelho e azul (corrige alguns problemas de cor)", - "readOnly": "Somente leitura", - "readOnlyDesc": "Ver a tela remota sem enviar qualquer entrada", - "telnetPort": "Porta de Telnet", - "terminalType": "Tipo de Terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Esquema de Cor", - "backspaceKey": "Chave de Backspace", - "saveHostFirst": "Salve o host primeiro.", - "sharingOptionsAfterSave": "Opções de compartilhamento estão disponíveis após o host ser salvo.", - "sharingLoadError": "Não foi possível carregar os dados de compartilhamento. Verifique sua conexão e tente novamente.", - "shareHostSection": "Host de compartilhamento", - "shareWithUser": "Compartilhar com Usuário", - "shareWithRole": "Compartilhar com Função", - "selectUser": "Selecionar usuário", - "selectRole": "Selecione a função", - "selectUserOption": "Selecione um usuário...", - "selectRoleOption": "Selecione uma função...", - "permissionLevel": "Nível de permissão", - "expiresInHours": "Expira em (horas)", - "noExpiryPlaceholder": "Deixe em branco para não expirar", - "shareBtn": "Compartilhar", - "currentAccess": "Acesso atual", - "typeHeader": "tipo", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Permisschar@@0o", - "grantedByHeader": "Concedido por", - "expiresHeader": "Expira", - "noAccessEntries": "Ainda não há entradas de acesso.", - "expiredLabel": "Expirado", - "neverLabel": "nunca", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "cancelar", - "savingBtn": "Salvando...", - "updateHostBtn": "Atualizar Host", - "addHostBtn": "Adicionar Host" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Procurar por hosts, comandos ou configurações...", - "quickActions": "Ações rápidas", - "hostManager": "Gerenciador de Host", - "hostManagerDesc": "Gerenciar, adicionar ou editar hosts", - "addNewHost": "Adicionar Novo Host", - "addNewHostDesc": "Registrar um novo host", - "adminSettings": "Configurações de administrador", - "adminSettingsDesc": "Configurar preferências do sistema e usuários", - "userProfile": "Informações do Perfil", - "userProfileDesc": "Gerenciar sua conta e preferências", - "addCredential": "Adicionar Credencial", - "addCredentialDesc": "Armazenar chaves SSH ou senhas", - "recentActivity": "Atividade recente", - "serversAndHosts": "Servidores e Hosts", - "noHostsFound": "Nenhum host encontrado \"{{search}}\"", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", "links": "Links", "navigate": "Navigate", - "select": "Selecionar", - "toggleWith": "Alternar com" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Nenhuma aba atribuída", - "focusedPane": "Painel ativo" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Sem conexões", - "noConnectionsDesc": "Abra um terminal, um gerenciador de arquivos ou uma área de trabalho remota para ver as conexões aqui.", - "connectedFor": "Conectado por {{duration}}", - "connected": "Conectado", - "disconnected": "Desconectado", - "closeTab": "Fechar aba", - "closeConnection": "Conexão próxima", - "forgetTab": "Esquecer", - "removeBackground": "Remover", - "reconnect": "Reconectar", - "reopenTab": "Reabrir", - "sectionOpen": "Abrir", - "sectionBackground": "Fundo", - "backgroundDesc": "As sessões permanecem ativas por 30 minutos após a desconexão e podem ser reconectadas.", - "persisted": "Persistiu em segundo plano", - "expiresIn": "Expira em {{duration}}", - "search": "Buscar conexões...", - "noSearchResults": "Nenhuma conexão corresponde à sua pesquisa." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Conectando à sessão {{type}}", - "connectionError": "Erro de conexão", - "connectionFailed": "Conexão falhou", - "failedToConnect": "Falha ao obter token de conexão", - "hostNotFound": "Host não encontrado", - "noHostSelected": "Nenhum host selecionado", - "reconnect": "Reconectar", - "retry": "Repetir", - "guacdUnavailable": "Serviço de desktop remoto (guacd) não disponível. Por favor certifique-se que o guacd está sendo executado e acessível e configurado corretamente nas configurações de administração.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Tela de bloqueio)", - "winKey": "Chave do Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Turno", - "win": "Vitória", - "stickyActive": "{{key}} (retido - clique para liberar)", - "stickyInactive": "{{key}} (clique para travar)", - "esc": "Prosseguir", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Residencial", - "end": "Término", - "pageUp": "Página Acima", - "pageDown": "Página abaixo", - "arrowUp": "Seta para cima", - "arrowDown": "Seta para baixo", - "arrowLeft": "Seta para esquerda", - "arrowRight": "Seta para direita", - "fnToggle": "Chaves de função", - "reconnect": "Reconectar Sessão", - "collapse": "Recolher barra de ferramentas", - "expand": "Expandir barra de ferramentas", - "dragHandle": "Arraste para reposicionar" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Conectar ao Host", - "clear": "Limpar", - "paste": "Colar", - "reconnect": "Reconectar", - "connectionLost": "Conexão perdida", - "connected": "Conectado", - "clipboardWriteFailed": "Falha ao copiar para a área de transferência. Certifique-se de que a página seja servida por HTTPS ou localhost.", - "clipboardReadFailed": "Falha ao ler da área de transferência. Certifique-se de que as permissões da área de transferência foram concedidas.", - "clipboardHttpWarning": "Colar requer HTTPS. Use Ctrl+Shift+V ou sirva Termix em HTTPS.", - "unknownError": "Ocorreu um erro desconhecido", - "websocketError": "Erro de conexão WebSocket", - "connecting": "Conectandochar@@0", - "noHostSelected": "Nenhum host selecionado", - "reconnecting": "Reconectando... ({{attempt}}/{{max}})", - "reconnected": "Reconectado com sucesso", - "tmuxSessionCreated": "sessão tmux criada: {{name}}", - "tmuxSessionAttached": "sessão tmux anexada: {{name}}", - "tmuxUnavailable": "tmux não está instalado no host remoto, voltando ao shell padrão", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", "tmuxSessionPickerTitle": "tmux Sessions", - "tmuxSessionPickerDesc": "Sessões tmux existentes encontradas neste host. Selecione uma para reanexar ou criar uma nova sessão.", - "tmuxWindows": "Janelas", - "tmuxWindowCount": "{{count}} janela", - "tmuxAttached": "Clientes anexados", - "tmuxAttachedCount": "{{count}} anexou", - "tmuxLastActivity": "Última atividade", - "tmuxTimeJustNow": "neste momento", - "tmuxTimeMinutes": "{{count}}m atrás", - "tmuxTimeHours": "{{count}}h atrás", - "tmuxTimeDays": "{{count}}há alguns dias", - "tmuxCreateNew": "Iniciar nova sessão", - "tmuxCopyHint": "Ajustar seleção e pressione Enter para copiar para área de transferência", - "tmuxDetach": "Desanexar da sessão do tmux", - "tmuxDetached": "Desanexado da sessão do tmux", - "maxReconnectAttemptsReached": "Máximo de tentativas de reconexão alcançadas", - "closeTab": "FECHAR", - "connectionTimeout": "Conexão expirada", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Executando {{command}} - {{host}}", - "totpRequired": "Autenticação dupla requerida", - "totpCodeLabel": "Código de verificação", - "totpVerify": "Verificar", - "warpgateAuthRequired": "Autenticação Warpgate Necessária", - "warpgateSecurityKey": "Chave de Segurança", - "warpgateAuthUrl": "URL de autenticação", - "warpgateOpenBrowser": "Abrir no Navegador", - "warpgateContinue": "Eu concluí a autenticação", - "opksshAuthRequired": "Autenticação OPKSSH necessária", - "opksshAuthDescription": "Conclua a autenticação no seu navegador para continuar. Essa sessão permanecerá válida por 24 horas.", - "opksshOpenBrowser": "Abra o navegador para autenticar", - "opksshWaitingForAuth": "Aguardando autenticação no navegador...", - "opksshAuthenticating": "Processando autenticação...", - "opksshTimeout": "Tempo de autenticação esgotado. Por favor, tente novamente.", - "opksshAuthFailed": "Falha na autenticação. Por favor, verifique suas credenciais e tente novamente.", - "opksshSignInWith": "Entrar com {{provider}}", - "sudoPasswordPopupTitle": "Inserir senha?", - "websocketAbnormalClose": "A conexão foi fechada inesperadamente. Isso pode ser devido a um problema de configuração do proxy reverso ou SSL. Por favor, verifique os logs do servidor.", - "connectionLogTitle": "Registro de conexão", - "connectionLogCopy": "Copiar logs para área de transferência", - "connectionLogEmpty": "Ainda não há registros de conexão", - "connectionLogWaiting": "Aguardando registros de conexão...", - "connectionLogCopied": "Logs de conexão copiados para área de transferência", - "connectionLogCopyFailed": "Falha ao copiar os logs para a área de transferência", - "connectionRejected": "Conexão rejeitada pelo servidor. Verifique sua autenticação e configuração de rede.", - "hostKeyRejected": "Verificação de chave do host SSH rejeitada. Conexão cancelada.", - "sessionTakenOver": "A sessão foi aberta em outra aba. Reconectando..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nenhum host selecionado", - "initializingEditor": "Inicializando o editor...", - "file": "Arquivo", - "folder": "pasta", - "uploadFile": "Enviar Arquivo", - "downloadFile": "BAIXAR", - "extractArchive": "Extrair arquivo", - "extractingArchive": "Extraindo {{name}}...", - "archiveExtractedSuccessfully": "{{name}} extraído com sucesso", - "extractFailed": "Falha ao extrair", - "compressFile": "Compactar arquivo", - "compressFiles": "Compactar arquivos", - "compressFilesDesc": "Comprimir itens {{count}} em um arquivo", - "archiveName": "Nome do Arquivo", - "enterArchiveName": "Informe o nome do arquivo...", - "compressionFormat": "Formato de compressão", - "selectedFiles": "Arquivos selecionados", - "andMoreFiles": "e mais {{count}}...", - "compress": "Compactar", - "compressingFiles": "Comprimindo {{count}} itens em {{name}}...", - "filesCompressedSuccessfully": "{{name}} criado com sucesso", - "compressFailed": "Compressão falhou", - "edit": "Alterar", - "preview": "Pré-visualizar", - "previous": "Anterior", - "next": "Próximo", - "pageXOfY": "Página {{current}} de {{total}}", - "zoomOut": "Diminuir o zoom", - "zoomIn": "Aumentar zoom", - "newFile": "Novo arquivo", - "newFolder": "Adicionar uma pasta", - "rename": "Renomear", - "uploading": "Enviando...", - "uploadingFile": "Enviando {{name}}...", - "fileName": "Nome do arquivo", - "folderName": "Nome da pasta", - "fileUploadedSuccessfully": "Arquivo \"{{name}}\" carregado com sucesso", - "failedToUploadFile": "Falha ao carregar arquivo", - "fileDownloadedSuccessfully": "Arquivo \"{{name}}\" baixado com sucesso", - "failedToDownloadFile": "Não foi possível baixar o arquivo", - "fileCreatedSuccessfully": "Arquivo \"{{name}}\" criado com sucesso", - "folderCreatedSuccessfully": "Pasta \"{{name}}\" criada com sucesso", - "failedToCreateItem": "Falha ao criar item", - "operationFailed": "A operação {{operation}} falhou para {{name}}: {{error}}", - "failedToResolveSymlink": "Falha ao resolver link simbólico", - "itemsDeletedSuccessfully": "Itens {{count}} excluídos com sucesso", - "failedToDeleteItems": "Falha ao excluir itens", - "sudoPasswordRequired": "Senha do Administrador Necessária", - "enterSudoPassword": "Digite a senha sudo para continuar esta operação", - "sudoPassword": "Senha sudo", - "sudoOperationFailed": "Operação sudo falhou", - "sudoAuthFailed": "Falha na autenticação Sudo", - "dragFilesToUpload": "Solte os arquivos aqui para enviar", - "emptyFolder": "Esta pasta está vazia", - "searchFiles": "Pesquisar arquivos...", - "upload": "Transferir", - "selectHostToStart": "Selecione um host para iniciar o gerenciamento de arquivos", - "sshRequiredForFileManager": "O gerenciador de arquivos requer SSH. Este host não tem o SSH habilitado.", - "failedToConnect": "Falha ao conectar com SSH", - "failedToLoadDirectory": "Falha ao carregar diretório", - "noSSHConnection": "Nenhuma conexão SSH disponível", - "copy": "copiar", - "cut": "Recortar", - "paste": "Colar", - "copyPath": "Copiar Caminho", - "copyPaths": "Copiar caminhos", - "delete": "excluir", - "properties": "propriedades", - "refresh": "atualizar", - "downloadFiles": "Baixar arquivos {{count}} para o Navegador", - "copyFiles": "Copiar itens {{count}}", - "cutFiles": "Cortar {{count}} itens", - "deleteFiles": "Excluir {{count}} itens", - "filesCopiedToClipboard": "{{count}} itens copiados para a área de transferência", - "filesCutToClipboard": "{{count}} itens cortados na área de transferência", - "pathCopiedToClipboard": "Caminho copiado para área de transferência", - "pathsCopiedToClipboard": "{{count}} caminhos copiados para a área de transferência", - "failedToCopyPath": "Falha ao copiar caminho para área de transferência", - "movedItems": "Itens de {{count}} movidos", - "failedToDeleteItem": "Falha ao excluir item", - "itemRenamedSuccessfully": "{{type}} renomeado com sucesso", - "failedToRenameItem": "Falha ao renomear item", - "download": "BAIXAR", - "permissions": "Permissões", - "size": "Tamanho", - "modified": "Modificado", - "path": "Caminho", - "confirmDelete": "Tem certeza que deseja excluir {{name}}?", - "permissionDenied": "Permissão negada", - "serverError": "Erro no Servidor", - "fileSavedSuccessfully": "Arquivo salvo com sucesso", - "failedToSaveFile": "Falha ao salvar arquivo", - "confirmDeleteSingleItem": "Tem certeza de que quer apagar permanentemente \"{{name}}\"?", - "confirmDeleteMultipleItems": "Tem certeza de que deseja excluir permanentemente itens {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Tem certeza de que deseja excluir permanentemente itens {{count}} ? Isso inclui pastas e seu conteúdo.", - "confirmDeleteFolder": "Tem certeza de que deseja excluir permanentemente a pasta \"{{name}}\" e todo o seu conteúdo?", - "permanentDeleteWarning": "Esta ação não pode ser desfeita. O(s) item(ns) serão excluídos permanentemente do servidor.", - "recent": "Recente", - "pinned": "Fixado", - "folderShortcuts": "Atalhos da pasta", - "failedToReconnectSSH": "Falha ao reconectar a sessão SSH", - "openTerminalHere": "Abrir Terminal Aqui", - "run": "Executar", - "openTerminalInFolder": "Abrir Terminal nesta Pasta", - "openTerminalInFileLocation": "Abrir Terminal no Local do Arquivo", - "runningFile": "Executando - {{file}}", - "onlyRunExecutableFiles": "Só é possível executar arquivos executáveis", - "directories": "Diretórios", - "removedFromRecentFiles": "Removido \"{{name}}\" dos arquivos recentes", - "removeFailed": "Falha ao remover", - "unpinnedSuccessfully": "\"{{name}}desafixado\" com sucesso", - "unpinFailed": "Desafixar falhou", - "removedShortcut": "Atalho \"{{name}} removido \"", - "removeShortcutFailed": "Falha ao remover atalho", - "clearedAllRecentFiles": "Todos os arquivos recentes foram removidos", - "clearFailed": "Falha ao apagar", - "removeFromRecentFiles": "Remover dos arquivos recentes", - "clearAllRecentFiles": "Limpar todos os arquivos recentes", - "unpinFile": "Desafixar arquivo", - "removeShortcut": "Remover atalho", - "pinFile": "Fixar arquivo", - "addToShortcuts": "Adicionar a atalhos", - "pasteFailed": "A colagem falhou", - "noUndoableActions": "Nenhuma ação irreversível", - "undoCopySuccess": "Operação de cópia desfeita: {{count}} excluídos arquivos copiados", - "undoCopyFailedDelete": "Desfazer falhou: Não foi possível excluir nenhum arquivo copiado", - "undoCopyFailedNoInfo": "Desfazer falhou: Não foi possível encontrar informações do arquivo copiado", - "undoMoveSuccess": "Operação movida desfeita: arquivos {{count}} movidos de volta para o local original", - "undoMoveFailedMove": "Desfazer falhou: Não foi possível mover nenhum arquivo de volta", - "undoMoveFailedNoInfo": "Desfazer falhou: Não foi possível encontrar informação de arquivo movido", - "undoDeleteNotSupported": "Operação de exclusão não pode ser desfeita: Os arquivos foram excluídos permanentemente do servidor", - "undoTypeNotSupported": "Tipo de operação desfazer não suportado", - "undoOperationFailed": "Falha na operação", - "unknownError": "Erro desconhecido", - "confirm": "Confirmar", - "find": "Localizar...", - "replace": "Substituir", - "downloadInstead": "Em vez disso, baixar", - "keyboardShortcuts": "Atalhos do teclado", - "searchAndReplace": "Pesquisar e substituir", - "editing": "Editando", - "search": "Pesquisa", - "findNext": "Localizar Próximo", - "findPrevious": "Localizar Anterior", - "save": "Guardar", - "selectAll": "Selecionar Todos", - "undo": "Desfazer", - "redo": "Refazer", - "moveLineUp": "Mover Linha para Cima", - "moveLineDown": "Mover Linha para Baixo", - "toggleComment": "Alternar comentário", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Não foi possível carregar a imagem", - "startTyping": "Comece a digitar...", - "unknownSize": "Tamanho desconhecido", - "fileIsEmpty": "O arquivo está vazio", - "largeFileWarning": "Aviso de arquivo grande", - "largeFileWarningDesc": "Este arquivo tem o tamanho {{size}} , o que pode causar problemas de desempenho quando aberto como texto.", - "fileNotFoundAndRemoved": "O arquivo \"{{name}}\" não foi encontrado e foi removido dos arquivos recentes/fixados", - "failedToLoadFile": "Falha ao carregar arquivo: {{error}}", - "serverErrorOccurred": "Ocorreu um erro no servidor. Tente novamente mais tarde.", - "autoSaveFailed": "Auto-salvamento falhou", - "fileAutoSaved": "Arquivo salvo automaticamente", - "moveFileFailed": "Falha ao mover {{name}}", - "moveOperationFailed": "Falha ao mover", - "canOnlyCompareFiles": "Só é possível comparar dois arquivos", - "comparingFiles": "Comparando arquivos: {{file1}} e {{file2}}", - "dragFailed": "Falha ao arrastar", - "filePinnedSuccessfully": "Arquivo \"{{name}}\" fixado com sucesso", - "pinFileFailed": "Falha ao fixar arquivo", - "fileUnpinnedSuccessfully": "Arquivo \"{{name}}\" desafixado com sucesso", - "unpinFileFailed": "Falha ao desafixar arquivo", - "shortcutAddedSuccessfully": "Atalho para a pasta \"{{name}}\" adicionado com sucesso", - "addShortcutFailed": "Falha ao adicionar atalho", - "operationCompletedSuccessfully": "{{operation}} Itens {{count}} com sucesso", - "operationCompleted": "{{operation}} Itens em {{count}}", - "downloadFileSuccess": "Arquivo {{name}} baixado com sucesso", - "downloadFileFailed": "Download falhou", - "moveTo": "Mover para {{name}}", - "diffCompareWith": "Comparar diferenças com {{name}}", - "dragOutsideToDownload": "Arraste fora do janela para baixar (arquivos{{count}})", - "newFolderDefault": "Pasta", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Itens de {{count}} movidos com sucesso para {{target}}", - "move": "Mover-se", - "searchInFile": "Procurar no arquivo (Ctrl+F)", - "showKeyboardShortcuts": "Exibir atalhos de teclado", - "startWritingMarkdown": "Comece a escrever o seu conteúdo markdown...", - "loadingFileComparison": "Carregando comparação de arquivo...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Comparar", - "sideBySide": "Lado a lado", - "inline": "Embutido", - "fileComparison": "Comparação de arquivos: {{file1}} vs {{file2}}", - "fileTooLarge": "Arquivo muito grande: {{error}}", - "sshConnectionFailed": "Falha na conexão SSH. Por favor, verifique sua conexão com {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Falha ao carregar arquivo: {{error}}", - "connecting": "Conectandochar@@0", - "connectedSuccessfully": "Conectado com sucesso", - "totpVerificationFailed": "Falha na verificação TOTP", - "warpgateVerificationFailed": "Falha na autenticação do Warpgate", - "authenticationFailed": "Falha na autenticação", - "verificationCodePrompt": "Código de verificação:", - "changePermissions": "Alterar permissões", - "currentPermissions": "Permissões Atuais", - "owner": "Proprietário", - "group": "grupo", - "others": "Outros", - "read": "Lido", - "write": "Salvar", - "execute": "Executar", - "permissionsChangedSuccessfully": "Permissões alteradas com sucesso", - "failedToChangePermissions": "Falha ao alterar permissões", - "name": "Nome:", - "sortByName": "Nome:", - "sortByDate": "Data de Modificação", - "sortBySize": "Tamanho", - "ascending": "Ascendente", - "descending": "Descendente", - "root": "raiz", - "new": "Novidades", - "sortBy": "Classificar por", - "items": "itens", - "selected": "Selecionado", - "editor": "Editores", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", "octal": "Octal", - "storage": "Armazenamento", - "disk": "Disco", - "used": "Utilizado", - "of": "de", - "toggleSidebar": "Alternar barra lateral", - "cannotLoadPdf": "Não é possível carregar PDF", - "pdfLoadError": "Houve um erro ao carregar este arquivo PDF.", - "loadingPdf": "Carregando PDF...", - "loadingPage": "Carregando página..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copiar para o host…", - "moveToHost": "Mover para o host…", - "copyItemsToHost": "Copiar {{count}} itens para o host…", - "moveItemsToHost": "Mova {{count}} itens para o host…", - "noHostsConnected": "Não há outros servidores de gerenciamento de arquivos disponíveis.", - "noHostsConnectedHint": "Adicione outro host SSH com o Gerenciador de Arquivos habilitado no Gerenciador de Hosts.", - "selectDestinationHost": "Selecione o host de destino", - "destinationPath": "Caminho de destino", - "recentDestinations": "Destinos recentes", - "collapseRecentDestinations": "Recolher destinos recentes", - "expandRecentDestinations": "Expandir destinos recentes", - "browseFolders": "Procurar pastas de destino", - "browseDestination": "Navegue ou insira o caminho", - "confirmCopy": "Cópia", - "confirmMove": "Mover", - "transferring": "Transferindo…", - "compressing": "Comprimindo…", - "extracting": "Extraindo…", - "transferringItems": "Transferindo {{current}} de {{total}} itens…", - "transferSuccess": "Transferência concluída", - "transferError": "A transferência falhou", - "transferPartial": "Transferência concluída com {{count}} erros", - "transferPartialHint": "Não foi possível transferir: {{paths}}", - "itemsSummary": "{{count}} itens", - "destMustBeDirectory": "O destino deve ser um diretório para transferências de múltiplos itens.", - "selectThisFolder": "Selecione esta pasta", - "browsePathWillBeCreated": "Esta pasta ainda não existe. Ela será criada quando a transferência começar.", - "browsePathError": "Não foi possível abrir este caminho no host de destino.", - "goUp": "Subir", - "copyFolderToHost": "Copiar pasta para o host…", - "moveFolderToHost": "Mover pasta para o host…", - "hostReady": "Preparar", - "hostConnecting": "Conectando…", - "hostDisconnected": "Não conectado", - "hostAuthRequired": "Autenticação necessária — abra o Gerenciador de Arquivos neste host primeiro.", - "hostConnectionFailed": "Falha na conexão", - "metricsTitle": "Horários de transferência", - "metricsPrepare": "Preparar destino: {{duration}}", - "metricsCompress": "Comprimir na origem: {{duration}}", - "metricsHopSourceRead": "Origem → servidor: {{throughput}}", - "metricsHopDestSftpWrite": "Servidor → destino (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Servidor → destino (local): {{throughput}}", - "metricsTransfer": "De ponta a ponta: {{throughput}} ({{duration}})", - "metricsExtract": "Extrair no destino: {{duration}}", - "metricsSourceDelete": "Remover da fonte: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Comprimindo no host de origem…", - "progressExtracting": "Extraindo no destino…", - "progressTransferring": "Transferindo dados…", - "progressReconnecting": "Reconectando…", - "parallelSegmentsLabel": "Faixas de transferência paralelas", - "parallelSegmentsOption": "{{count}} faixas", - "parallelSegmentsHint": "Arquivos grandes são divididos em partes de 256 MB. Várias vias utilizam conexões separadas (como iniciar várias transferências) para obter uma taxa de transferência total maior.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} faixas)", - "progressTransferringItems": "Transferindo arquivos ({{current}} de {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} arquivos", - "sourceNotDeletedPartial": "Arquivos de origem mantidos (transferência parcial)", - "jumpHostLimitation": "Ambos os hosts devem ser acessíveis a partir do servidor Termix. O roteamento direto de host para host não é suportado.", - "cancel": "Cancelar", - "methodLabel": "Método de transferência", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Arquivo Tar", - "methodItemSftp": "SFTP por arquivo", - "methodAutoHint": "Seleciona o método de compactação (tar) ou SFTP por arquivo com base na quantidade de arquivos, tamanho e compressibilidade. Arquivos individuais sempre utilizam SFTP de streaming.", - "methodTarHint": "Comprima na origem, transfira um único arquivo compactado e extraia no destino. Requer o uso do tar em ambos os hosts Unix.", - "methodItemSftpHint": "Transfira cada arquivo individualmente via SFTP. Funciona em todos os hosts, incluindo Windows.", - "methodPreviewLoading": "Calculando o método de transferência…", - "methodPreviewError": "Não foi possível visualizar o método de transferência. O servidor ainda escolherá um método quando você iniciar o servidor.", - "methodPreviewWillUseTar": "Será utilizado: Arquivo tar", - "methodPreviewWillUseItemSftp": "Será utilizado: SFTP por arquivo", - "methodPreviewScanSummary": "{{fileCount}} arquivos, {{totalSize}} total (verificado no host de origem).", - "methodItemSftpLimitation": "Cada arquivo utiliza o mesmo fluxo SFTP como uma cópia de arquivo único, um após o outro. O progresso é combinado em todos os arquivos, portanto a barra se move lentamente durante a transferência de arquivos grandes.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Você escolheu SFTP por arquivo.", - "user_tar": "Você escolheu o arquivo tar.", - "tar_unavailable": "O arquivo tar não está disponível em um ou ambos os hosts — em vez disso, será usado o SFTP por arquivo.", - "windows_host": "O sistema operacional host é Windows — o arquivo tar não é utilizado.", - "auto_multi_large": "Automático: vários arquivos, incluindo um arquivo grande ({{largestSize}}) com dados compressíveis — pacotes tar em uma única transferência.", - "auto_single_large_in_archive": "Automático: um arquivo grande ({{largestSize}}) neste conjunto — SFTP por arquivo.", - "auto_many_incompressible": "Automático: dados em sua maioria não compressíveis — SFTP por arquivo.", - "auto_many_files": "Automático: muitos arquivos ({{fileCount}}) — tar reduz a sobrecarga por arquivo.", - "auto_default": "Automático: SFTP por arquivo para este conjunto." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Cancelar", - "progressCancelling": "Cancelando…", - "progressStalled": "Paralisado", - "resumedHint": "Reconectado a uma transferência ativa iniciada em outra janela.", - "transferCancelled": "Transferência cancelada", - "transferCancelledCopyHint": "Alguns arquivos podem permanecer no destino.", - "transferCancelledMoveHint": "É possível que alguns arquivos permaneçam no destino. Os arquivos de origem não foram removidos.", - "cleanupDestFiles": "destino de limpeza", - "cleanupDestFilesSuccess": "Arquivos parciais removidos do destino.", - "cleanupDestFilesPartial": "Alguns arquivos parciais não puderam ser removidos.", - "cleanupDestFilesNothing": "Nada para limpar no destino.", - "cleanupDestFilesError": "A limpeza falhou", - "retryTransfer": "Tentar novamente", - "retryTransferError": "A nova tentativa falhou.", - "transferFailedRetryHint": "Dados parciais foram mantidos no destino. A tentativa será retomada quando a conexão for restabelecida." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Sem Túneis SSH", - "createFirstTunnelMessage": "Você ainda não criou nenhum túnel SSH. Configure as conexões de túnel no Gerenciador de Host para começar.", - "connected": "Conectado", - "disconnected": "Desconectado", - "connecting": "Conectandochar@@0", - "error": "ERRO", - "canceling": "Cancelando...", - "connect": "Conectar", - "disconnect": "Desconectar", - "cancel": "cancelar", - "port": "Porta", - "localPort": "Porta local", - "remotePort": "Porta remota", - "currentHostPort": "Porta atual do Host", - "endpointPort": "Porta de Endpoint", - "bindIp": "IP local", - "endpointSshConfig": "Configuração do Endpoint SSH", - "endpointSshHost": "Host do Endpoint SSH", - "endpointSshHostPlaceholder": "Selecione um host configurado", - "endpointSshHostRequired": "Selecione um host SSH para cada túnel do cliente.", - "attempt": "Tentativa {{current}} de {{max}}", - "nextRetryIn": "Próxima repetição em {{seconds}} segundos", - "clientTunnels": "Túneis do cliente", - "clientTunnel": "Túnel Cliente", - "addClientTunnel": "Adicionar túnel de cliente", - "noClientTunnels": "Nenhum túnel do cliente configurado nesta área de trabalho.", - "tunnelName": "Nome do túnel", - "remoteHost": "Host Remoto", - "autoStart": "Início automático", - "clientAutoStartDesc": "Inicia quando este cliente desktop abre e permanece conectado.", - "clientManualStartDesc": "Use Iniciar e Parar dessa linha. O Termix não abrirá automaticamente.", - "clientRemoteServerNote": "O encaminhamento remoto pode exigir AllowTcpForwarding e GatewayPorts no servidor SSH do endpoint. A porta remota é fechada quando este desktop desconecta.", - "clientTunnelStarted": "Túnel de cliente iniciado", - "clientTunnelStopped": "Túnel de cliente parado", - "tunnelTestSucceeded": "Teste de túnel bem-sucedido", - "tunnelTestFailed": "Teste do túnel falhou", - "localSaved": "Túneis do cliente salvos", - "localSaveError": "Falha ao salvar túneis locais do cliente", - "invalidBindIp": "O IP local deve ter um endereço IPv4 válido.", - "invalidLocalTargetIp": "IP de destino local deve ser um endereço IPv4 válido.", - "invalidLocalPort": "A porta local deve estar entre 1 e 65535.", - "invalidRemotePort": "Porta remota deve estar entre 1 e 65535.", - "invalidLocalTargetPort": "Porta local de destino deve estar entre 1 e 65535.", - "invalidEndpointPort": "Porta de Endpoint deve estar entre 1 e 65535.", - "duplicateAutoStartBind": "Apenas um túnel de cliente iniciar automaticamente pode usar o {{bind}}.", - "manualControlError": "Falha ao atualizar o estado do túnel.", - "active": "ativo", - "start": "Iniciar", - "stop": "Interromper", - "test": "teste", - "type": "Tipo de túnel", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Remoto (-R)", - "typeDynamic": "Dinâmico (-D)", - "typeServerLocalDesc": "Host atual para endpoint.", - "typeServerRemoteDesc": "Endpoint de volta ao host atual.", - "typeClientLocalDesc": "Computador local para endpoint.", - "typeClientRemoteDesc": "Endpoint de volta ao computador local.", - "typeClientDynamicDesc": "SOCKS no computador local.", - "typeDynamicDesc": "Encaminhar tráfego SOCKS5 CONNECT através de SSH", - "forwardDescriptionServerLocal": "Host atual {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host atual {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS no host atual {{sourcePort}}.", - "forwardDescriptionClientLocal": "{{sourcePort}} local → remoto {{endpointPort}}.", - "forwardDescriptionClientRemote": "{{sourcePort}} remoto → {{endpointPort}} local.", - "forwardDescriptionClientDynamic": "SOCKS na porta local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "{{localPort}} local → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → {{localPort}} local", - "autoNameClientDynamic": "format@@0 SOCKS {{localPort}} via {{endpoint}}", - "route": "Itinerário:", - "lastStarted": "Último início", - "lastTested": "Último teste", - "lastError": "Último erro", - "maxRetries": "Máximo de buscas", - "maxRetriesDescription": "Quantidade máxima de tentativas de repetição.", - "retryInterval": "Intervalo de Repetir (segundos)", - "retryIntervalDescription": "Tempo de espera entre tentativas novamente.", - "local": "Localização", - "remote": "Remoto", - "destination": "Destino", - "host": "Servidor", - "mode": "Modo", - "noHostSelected": "Nenhum host selecionado", - "working": "Trabalhando..." + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "cpu", - "memory": "Memória", - "disk": "Disco", - "network": "Rede", - "uptime": "Tempo em atividade", - "processes": "processos", - "available": "Disponível", - "free": "Gratuito", - "connecting": "Conectandochar@@0", - "connectionFailed": "Falha ao conectar ao servidor", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "Núcleo {{count}}", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Uso da CPU", - "memoryUsage": "Memória Utilizada", - "diskUsage": "Uso do disco", - "failedToFetchHostConfig": "Falha ao buscar a configuração do host", - "serverOffline": "Servidor Offline", - "cannotFetchMetrics": "Não é possível buscar métricas do servidor offline", - "totpFailed": "Falha na verificação TOTP", - "noneAuthNotSupported": "As estatísticas do servidor não suportam tipo de autenticação 'nenhum'.", - "load": "Carregar", - "systemInfo": "Informação do Sistema", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Sistema operacional", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "segundos", - "networkInterfaces": "Interfaces de Rede", - "noInterfacesFound": "Nenhuma interface de rede encontrada", - "noProcessesFound": "Nenhum processo encontrado", - "loginStats": "Estatísticas de Login SSH", - "noRecentLoginData": "Nenhum dado de login recente", - "executingQuickAction": "Executando {{name}}...", - "quickActionSuccess": "{{name}} completado com sucesso", - "quickActionFailed": "{{name}} falhou", - "quickActionError": "Falha ao executar {{name}}", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Porta de Escuta", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Porta", - "address": "Endereço", - "process": "processo", - "noData": "Sem dados de portas de escuta" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inativo", - "policy": "Política", - "rules": "regras", - "noData": "Nenhum firewall disponível", - "action": "Acão", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Porta", - "source": "fonte", - "anywhere": "Em qualquer lugar" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Carga Média", - "swap": "Trocar", - "architecture": "Arquitetura", - "refresh": "atualizar", - "retry": "Repetir" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "SSH autohospedado e gerenciamento de desktop remoto", - "loginTitle": "Faça login para Termix", - "registerTitle": "Criar conta", - "forgotPassword": "Esqueceu a senha?", - "rememberMe": "Lembrar dispositivo por 30 dias (inclui TOTP)", - "noAccount": "Não possui uma conta?", - "hasAccount": "Já possui uma conta?", - "twoFactorAuth": "Autenticação dupla", - "enterCode": "Inserir código de verificação", - "backupCode": "Ou usar código de backup", - "verifyCode": "Verificar Código", - "redirectingToApp": "Redirecionando para o aplicativo...", - "sshAuthenticationRequired": "Autenticação SSH necessária", - "sshNoKeyboardInteractive": "Autenticação Interativa do Keyboard-Indisponível", - "sshAuthenticationFailed": "Falha na autenticação", - "sshAuthenticationTimeout": "Timeout de autenticação", - "sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação interativa de teclado. Por favor, forneça sua senha ou chave SSH.", - "sshAuthFailedDescription": "As credenciais fornecidas estavam incorretas. Por favor, tente novamente com credenciais válidas.", - "sshTimeoutDescription": "A tentativa de autenticação expirou. Tente novamente.", - "sshProvideCredentialsDescription": "Por favor, forneça suas credenciais SSH para conectar a este servidor.", - "sshPasswordDescription": "Digite a senha para esta conexão SSH.", - "sshKeyPasswordDescription": "Se a sua chave SSH estiver criptografada, digite a senha aqui.", - "passphraseRequired": "Senha Obrigatória", - "passphraseRequiredDescription": "A chave SSH está criptografada. Digite a senha para desbloqueá-la.", - "back": "Anterior", - "firstUser": "Primeiro usuário", - "firstUserMessage": "Você é o primeiro usuário e será feito um administrador. Você pode ver as configurações de administrador na lista de usuários da barra lateral. Se você acha que isso é um erro, verifique os logs do docker ou crie uma questão no GitHub.", - "external": "externo", - "loginWithExternal": "Entrar com o provedor externo", - "loginWithExternalDesc": "Fazer login usando seu provedor de identidade externo configurado", - "externalNotSupportedInElectron": "A autenticação externa ainda não é suportada no aplicativo Electron. Por favor, use a versão web para logar OIDC.", - "resetPasswordButton": "Redefinir a senha", - "sendResetCode": "Enviar Código de Redefinição", - "resetCodeDesc": "Digite seu nome de usuário para receber um código de redefinição de senha. O código será logado nos logs do contêiner docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verificar Código", - "enterResetCode": "Insira o código de 6 dígitos do contêiner docker para o usuário:", - "newPassword": "Nova Palavra-Passe", - "confirmNewPassword": "Confirmar senha", - "enterNewPassword": "Digite sua nova senha para o usuário:", - "signUp": "Criar conta", - "desktopApp": "Aplicativo para computador", - "loggingInToDesktopApp": "Fazendo login no aplicativo para computador", - "loadingServer": "Carregando servidor...", - "dataLossWarning": "Redefinir sua senha desta forma irá apagar todos os seus hosts, credenciais e outros dados criptografados salvos por SSH. Essa ação não pode ser desfeita. Apenas use isso se você esqueceu sua senha e não está logado.", - "authenticationDisabled": "Autenticação desabilitada", - "authenticationDisabledDesc": "Todos os métodos de autenticação estão desativados no momento. Entre em contato com o administrador.", - "attemptsRemaining": "Tentativas {{count}} restantes" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verificar chave do host SSH", - "keyChangedWarning": "Chave do host SSH alterada", - "firstConnectionTitle": "Primeira vez conectando-se a este host", - "firstConnectionDescription": "A autenticidade deste host não pode ser estabelecida. Verifique a impressão digital que você espera.", - "keyChangedDescription": "A chave do host para este servidor mudou desde a sua última conexão. Isso pode indicar um problema de segurança.", - "previousKey": "Chave anterior", - "newFingerprint": "Nova impressão digital", - "fingerprint": "Impressão digital", - "verifyInstructions": "Caso você confie neste host, clique em Aceitar para continuar e salvar esta impressão digital para conexões futuras.", - "securityWarning": "Aviso de segurança", - "acceptAndContinue": "Aceitar e Continuar", - "acceptNewKey": "Aceitar nova chave e continuar" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Não foi possível conectar ao banco de dados", - "unknownError": "Erro desconhecido", - "loginFailed": "Falha no login", - "failedPasswordReset": "Falha ao iniciar a redefinição de senha", - "failedVerifyCode": "Falha ao verificar código de redefinição", - "failedCompleteReset": "Falha ao concluir a redefinição de senha", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Falha ao iniciar o login OIDC", - "silentSigninOidcUnavailable": "O login silencioso foi solicitado, mas o login do OIDC não está disponível.", - "failedUserInfo": "Falha ao obter informações do usuário após o login", - "oidcAuthFailed": "Falha na autenticação OIDC", - "invalidAuthUrl": "URL de autorização inválida recebida do backend", - "requiredField": "Este campo é obrigatório", - "minLength": "Tamanho mínimo de {{min}}", - "passwordMismatch": "As senhas não coincidem", - "passwordLoginDisabled": "Nome de usuário/senha está desativado no momento", - "sessionExpired": "Sessão expirou - por favor faça o login novamente", - "totpRateLimited": "Taxa limitada: Muitas tentativas de verificação TOTP. Tente novamente mais tarde.", - "totpRateLimitedWithTime": "Taxa limitada: Muitas tentativas de verificação TOTP. Aguarde {{time}} segundos antes de tentar novamente.", - "resetCodeRateLimited": "Taxa limitada: Muitas tentativas de verificação. Por favor, tente novamente mais tarde.", - "resetCodeRateLimitedWithTime": "Taxa limitada: Muitas tentativas de verificação. Por favor, aguarde {{time}} segundos antes de tentar novamente.", - "authTokenSaveFailed": "Falha ao salvar token de autenticação", - "failedToLoadServer": "Falha ao carregar servidor" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "O registro da nova conta está desativado por um administrador. Por favor, entre em contato com um administrador.", - "userNotAllowed": "Sua conta não está autorizada a se registrar. Por favor, contate um administrador.", - "databaseConnectionFailed": "Falha ao conectar ao servidor do banco de dados", - "resetCodeSent": "Redefinir código enviado para os logs Docker", - "codeVerified": "Código verificado com sucesso", - "passwordResetSuccess": "Senha redefinida com sucesso", - "loginSuccess": "Login bem-sucedido", - "registrationSuccess": "Registrado com sucesso" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Túneis locais de desktop, direcionando hosts SSH configurados.", - "c2sTunnelPresets": "Predefinições do Túnel Cliente", - "c2sTunnelPresetsDesc": "Salvar a lista de túneis locais do cliente local como uma predefinição de servidor nomeada ou carregar uma predefinição de volta para este cliente.", - "c2sTunnelPresetsUnavailable": "Predefinições de túnel cliente só estão disponíveis no cliente desktop.", - "c2sPresetName": "Nome da predefinição", - "c2sPresetNamePlaceholder": "Nome da predefinição cliente", - "c2sPresetToLoad": "Predefinição para carregar", - "c2sNoPresetSelected": "Nenhuma predefinição selecionada", - "c2sNoPresets": "Nenhuma predefinição salva", - "c2sLoadPreset": "Carregar", - "c2sCurrentLocalConfig": "{{count}} túneis locais do cliente configurados nesta área de trabalho.", - "c2sPresetSyncNote": "As predefinições são imagens explícitas; carregar uma substitui a lista de túneis de cliente local desse cliente.", - "c2sPresetSaved": "Túnel de cliente predefinido salvo", - "c2sPresetLoaded": "Túnel cliente predefinido carregado localmente", - "c2sPresetRenamed": "Pré-ajuste do túnel cliente renomeado", - "c2sPresetDeleted": "Prédefinição de túnel de cliente excluído", - "c2sPresetLoadError": "Falha ao carregar as predefinições do túnel do cliente" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "IDIOMA", - "keyPassword": "Senha da chave", - "pastePrivateKey": "Cole sua chave privada aqui...", - "localListenerHost": "127.0.0.1 (ouvir localmente)", - "localTargetHost": "127.0.0.1 (alvo neste computador)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Digite sua senha", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Painel", - "loading": "Carregando painel...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "SUPORTE", + "support": "Support", "discord": "Discord", - "serverOverview": "Visão Geral do Servidor", - "version": "Versão", - "upToDate": "Até a data", - "updateAvailable": "Atualização disponível", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Tempo em atividade", - "database": "Banco", - "healthy": "Saudável", - "error": "ERRO", - "totalHosts": "Hosts totais", - "totalTunnels": "Total de túneis", - "totalCredentials": "Credenciais totais", - "recentActivity": "Atividade recente", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Carregando atividade recente...", - "noRecentActivity": "Nenhuma atividade recente", - "quickActions": "Ações rápidas", - "addHost": "Adicionar Host", - "addCredential": "Adicionar Credencial", - "adminSettings": "Configurações de administrador", - "userProfile": "Informações do Perfil", - "serverStats": "Estatísticas do servidor", - "loadingServerStats": "Carregando estatísticas do servidor...", - "noServerData": "Nenhum dado de servidor disponível", - "cpu": "cpu", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizar Painel", - "dashboardSettings": "Configurações do painel", - "enableDisableCards": "Ativar/Desativar cartões", - "resetLayout": "Restaurar ao Padrão", - "serverOverviewCard": "Visão Geral do Servidor", - "recentActivityCard": "Atividade recente", - "networkGraphCard": "Gráfico da Rede", - "networkGraph": "Gráfico da Rede", - "quickActionsCard": "Ações rápidas", - "serverStatsCard": "Estatísticas do servidor", - "panelMain": "Principal", - "panelSide": "Lado", - "justNow": "neste momento" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ESTÁVEL", - "hostsOnline": "Hosts online", - "activeTunnels": "Túneis ativos", - "registerNewServer": "Registrar um novo servidor", - "storeSshKeysOrPasswords": "Armazenar chaves SSH ou senhas", - "manageUsersAndRoles": "Gerenciar usuários e funções", - "manageYourAccount": "Gerencie sua conta", - "hostStatus": "Status do Host", - "noHostsConfigured": "Nenhum host configurado", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", - "offline": "DESLIGAR", - "onlineLower": "Disponível", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Adicionar:", - "commandPalette": "Paleta de comando", - "done": "Concluído", - "editModeInstructions": "Arraste cartões para reordenar · Arraste o divisor de coluna para redimensionar colunas · Arraste a borda inferior de um cartão para redimensionar sua altura · Lixeira para remover", - "empty": "Vazio", - "clear": "Limpar" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Adicionar Host", - "addGroup": "Adicionar Grupo", - "addLink": "Adicionar Link", - "zoomIn": "Aumentar zoom", - "zoomOut": "Diminuir o zoom", - "resetView": "Redefinir visualização", - "selectHost": "Selecione o Host", - "chooseHost": "Escolha um host...", - "parentGroup": "Grupo Pai", - "noGroup": "Nenhum grupo", - "groupName": "Nome do Grupo", - "color": "Cor", - "source": "fonte", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Mover para o grupo", - "selectGroup": "Selecionar grupo...", - "addConnection": "Adicionar conexão", - "hostDetails": "Detalhes do Host", - "removeFromGroup": "Remover do grupo", - "addHostHere": "Adicionar Host Aqui", - "editGroup": "Editar Grupo", - "delete": "excluir", - "add": "Adicionar", - "create": "Crio", - "move": "Mover-se", - "connect": "Conectar", - "createGroup": "Criar grupo", - "selectSourcePlaceholder": "Selecione a Fonte...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Arquivo inválido", - "hostAlreadyExists": "O host já está na topologia", - "connectionExists": "Conexão já existe", - "unknown": "Desconhecido", - "name": "Nome:", - "ip": "PI", - "status": "SItuação", - "failedToAddNode": "Falha ao adicionar nó", - "sourceDifferentFromTarget": "Origem e alvo devem ser diferentes", - "exportJSON": "Exportar JSON", - "importJSON": "Importar JSON", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Gerenciador de Arquivos", - "tunnel": "Túnel", - "docker": "Atracador", - "serverStats": "Estatísticas do servidor", - "noNodes": "Nenhum nó ainda" + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "O Docker não está habilitado para este host", - "validating": "Validando o Docker...", - "connecting": "Conectandochar@@0", - "error": "ERRO", - "version": "{{version}} Docker", - "connectionFailed": "Falha ao conectar ao Docker", - "containerStarted": "Contêiner {{name}} iniciado", - "failedToStartContainer": "Falha ao iniciar o contêiner {{name}}", - "containerStopped": "O contêiner {{name}} parou", - "failedToStopContainer": "Falha ao parar o contêiner {{name}}", - "containerRestarted": "Contêiner {{name}} reiniciado", - "failedToRestartContainer": "Falha ao reiniciar o contêiner {{name}}", - "containerPaused": "Recipiente {{name}} pausado", - "containerUnpaused": "Recipiente {{name}} despausado", - "failedToTogglePauseContainer": "Falha ao alternar estado de pausa para o contêiner {{name}}", - "containerRemoved": "{{name}} do contêiner removido", - "failedToRemoveContainer": "Falha ao remover contêiner {{name}}", - "image": "Imagem:", - "ports": "Portas", - "noPorts": "Nenhuma porta", - "start": "Iniciar", - "confirmRemoveContainer": "Você tem certeza que deseja remover o contêiner '{{name}}'? Esta ação não pode ser desfeita.", - "runningContainerWarning": "Aviso: Este contêiner está em execução no momento. Removê-lo irá parar o contêiner primeiro.", - "loadingContainers": "Carregando contêineres...", - "manager": "Gerenciador de Docker", - "autoRefresh": "Atualização Automática", - "timestamps": "Data", - "lines": "linhas", - "filterLogs": "Filtrar registros...", - "refresh": "atualizar", - "download": "BAIXAR", - "clear": "Limpar", - "logsDownloaded": "Registros baixados com sucesso", - "last50": "Últimos 50", - "last100": "Últimas 100", - "last500": "Últimas 500", - "last1000": "Últimas 1000", - "allLogs": "Todos os registros", - "noLogsMatching": "Nenhum registro correspondente \"{{query}}\"", - "noLogsAvailable": "Não há registros disponíveis", - "noContainersFound": "Nenhum contêiner encontrado", - "noContainersFoundHint": "Nenhum contêiner Docker está disponível neste host", - "searchPlaceholder": "Procurar contêineres...", - "allStatuses": "Todos os Status", - "stateRunning": "Executando", - "statePaused": "Pausado", - "stateExited": "Saída", - "stateRestarting": "Reiniciando", - "noContainersMatchFilters": "Nenhum contêiner corresponde aos seus filtros", - "noContainersMatchFiltersHint": "Tente ajustar sua pesquisa ou critério de filtro", - "failedToFetchStats": "Falha ao obter estatísticas do contêiner", - "containerNotRunning": "Contêiner não executando", - "startContainerToViewStats": "Inicie o contêiner para ver estatísticas", - "loadingStats": "Carregando estatísticas...", - "errorLoadingStats": "Erro ao carregar estatísticas", - "noStatsAvailable": "Não há estatísticas disponíveis", - "cpuUsage": "Uso da CPU", - "current": "Atual", - "memoryUsage": "Memória Utilizada", - "networkIo": "Entrada e Saída de rede", - "input": "Entrada", - "output": "Saída", - "blockIo": "Bloco de E/S", - "read": "Lido", - "write": "Salvar", - "pids": "Identificadores de Processos", - "containerInformation": "Informação do contêiner", - "name": "Nome:", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Estado:", - "containerMustBeRunning": "O contêiner precisa estar em execução para acessar o console", - "verificationCodePrompt": "Inserir código de verificação", - "totpVerificationFailed": "Falha na verificação TOTP. Tente novamente.", - "warpgateVerificationFailed": "A autenticação do Warpgate falhou. Por favor, tente novamente.", - "connectedTo": "Conectado a {{containerName}}", - "disconnected": "Desconectado", - "consoleError": "Erro de console", - "errorMessage": "Erro: {{message}}", - "failedToConnect": "Falha ao conectar ao contêiner", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", "console": "Console", - "selectShell": "Selecionar shell", - "bash": "Pancada", - "sh": "estrito", - "ash": "cinzas", - "connect": "Conectar", - "disconnect": "Desconectar", - "notConnected": "Não conectado", - "clickToConnect": "Clique em conectar para iniciar uma sessão shell", - "connectingTo": "Conectando a {{containerName}}...", - "containerNotFound": "Contêiner não encontrado", - "backToList": "Voltar para a Lista", - "logs": "Registros", - "stats": "Estatísticas", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Iniciar o contêiner para acessar o console" + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Gerais", - "sectionOidc": "OCIDADE", - "sectionUsers": "Utilizadores", - "sectionSessions": "Sessões", - "sectionRoles": "Papéis", - "sectionDatabase": "Banco", - "sectionApiKeys": "Chaves API", - "allowRegistration": "Permitir Registro de Usuário", - "allowRegistrationDesc": "Deixe novos usuários se registrarem", - "allowPasswordLogin": "Permitir acesso à senha", - "allowPasswordLoginDesc": "Usuário/senha para login", - "oidcAutoProvision": "Auto-Provisão OIDC", - "oidcAutoProvisionDesc": "Criar contas automaticamente para usuários OIDC, mesmo que o registro esteja desativado", - "allowPasswordReset": "Permitir redefinição de senha", - "allowPasswordResetDesc": "Redefinir código via logs do Docker", - "sessionTimeout": "Sessão expirada", - "hours": "horas", - "sessionTimeoutRange": "Mínimo de 1h · Máximo de 720h", - "monitoringDefaults": "Padrões de monitoramento", - "statusCheck": "Verificação de status", - "metrics": "Métricas", - "sec": "seg.", - "logLevel": "Nível do Registro", - "enableGuacamole": "Ativar Guacamole", - "enableGuacamoleDesc": "desktop remoto RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Configure o OpenID Connect para SSO. Campos marcados * são necessários.", - "oidcClientId": "ID do Cliente", - "oidcClientSecret": "Segredo do Cliente", - "oidcAuthUrl": "URL de autorização", - "oidcIssuerUrl": "URL do Emissor", - "oidcTokenUrl": "URL do token", - "oidcUserIdentifier": "Caminho do usuário", - "oidcDisplayName": "Exibir Caminho do Nome", - "oidcScopes": "Âmbitos", - "oidcUserinfoUrl": "Substituir URL do Userinfo", - "oidcAllowedUsers": "Usuários permitidos", - "oidcAllowedUsersDesc": "Um e-mail por linha. Deixe em branco para permitir que todos.", - "removeOidc": "Excluir", - "usersCount": "Usuários {{count}}", - "createUser": "Crio", - "newRole": "Nova Permissão", - "roleName": "Nome:", - "roleDisplayName": "Nome para exibição", - "roleDescription": "Descrição:", - "rolesCount": "Funções {{count}}", - "createRole": "Crio", - "creating": "Criando...", - "exportDatabase": "Exportar banco de dados", - "exportDatabaseDesc": "Baixar um backup de todos os hosts, credenciais e configurações", - "export": "Exportação", - "exporting": "Exportando...", - "importDatabase": "Importar base de dados", - "importDatabaseDesc": "Restaurar a partir de um arquivo de backup .sqlite", - "importDatabaseSelected": "Selecionado: {{name}}", - "selectFile": "Selecione o arquivo", - "changeFile": "Troca", - "import": "Importação", - "importing": "Importando...", - "apiKeysCount": "Chaves {{count}}", - "newApiKey": "Nova chave de API", - "apiKeyCreatedWarning": "Chave criada - copiá-la agora, ela não será exibida novamente.", - "apiKeyName": "Nome:", - "apiKeyUser": "Usuário", - "apiKeySelectUser": "Selecione um usuário...", - "apiKeyExpiresAt": "Expira em", - "createKey": "Criar chave", - "apiKeyNoExpiry": "Sem expiração", + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Dupla Autenticação", - "authTypeOidc": "OCIDADE", - "authTypeLocal": "Localização", - "adminStatusAdministrator": "Administrador", - "adminStatusRegularUser": "Usuário Normal", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "SIM", - "customBadge": "PERSÃO", - "youBadge": "VOCÊ", - "sessionsActive": "{{count}} ativo", - "sessionActive": "Ativo: {{time}}", - "sessionExpires": "Exx: {{time}}", - "revokeAll": "TODOS", - "revokeAllSessionsSuccess": "Todas as sessões do usuário revogadas", - "revokeAllSessionsFailed": "Falha ao revogar sessões", - "revokeSessionFailed": "Falha ao revogar sessão", - "addRole": "Adicionar função", - "noCustomRoles": "Nenhum cargo personalizado definido", - "removeRoleFailed": "Falha ao remover papel", - "assignRoleFailed": "Falha ao atribuir papel", - "deleteRoleFailed": "Falha ao excluir papel", - "userAdminAccess": "Administrador", - "userAdminAccessDesc": "Acesso completo a todas as configurações de administrador", - "userRoles": "Papéis", - "revokeAllUserSessions": "Revogar todas as sessões", - "revokeAllUserSessionsDesc": "Forçar o re-login em todos os dispositivos", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "A exclusão deste usuário é permanente.", - "deleteUser": "Excluir {{username}}", - "deleting": "Excluindo...", - "deleteUserFailed": "Falha ao excluir usuário", - "deleteUserSuccess": "Usuário \"{{username}}\" excluído", - "deleteRoleSuccess": "Função \"{{name}}\" excluído", - "revokeKeySuccess": "Chave \"{{name}}\" revogada", - "revokeKeyFailed": "Falha ao revogar a chave", - "copiedToClipboard": "Copiado para o clipboard", - "done": "Concluído", - "createUserTitle": "Criar Usuário", - "createUserDesc": "Crie uma nova conta local.", - "createUserUsername": "Usuário:", - "createUserPassword": "Palavra-passe", - "createUserPasswordHint": "Mínimo 6 caracteres.", - "createUserEnterUsername": "Digite o usuário", - "createUserEnterPassword": "Insira a senha", - "createUserSubmit": "Criar Usuário", - "editUserTitle": "Gerenciar Usuário: {{username}}", - "editUserDesc": "Editar funções, status de administrador, sessões e configurações da conta.", - "editUserUsername": "Usuário:", - "editUserAuthType": "Tipo de Autenticação", - "editUserAdminStatus": "Status do administrador", - "editUserUserId": "ID de usuário", - "linkAccountTitle": "Vincular OIDC à conta da senha", - "linkAccountDesc": "Mesclar a conta OIDC {{username}} com uma conta local existente.", - "linkAccountWarningTitle": "Isto irá:", - "linkAccountEffect1": "Excluir conta somente OIDC", - "linkAccountEffect2": "Adicione login OIDC à conta de destino", - "linkAccountEffect3": "Permitir tanto acesso OIDC quanto acesso à senha", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Digite o nome de usuário da conta local para vincular", - "linkAccounts": "Vincular Contas", - "linkAccountSuccess": "Conta OIDC vinculada a \"{{username}}\"", - "linkAccountFailed": "Falha ao vincular a conta OIDC", - "linkAccountInProgress": "Conectando...", - "saving": "Salvando...", - "updateRegistrationFailed": "Falha ao atualizar configuração de registro", - "updatePasswordLoginFailed": "Falha ao atualizar configuração de login de senha", - "updateOidcAutoProvisionFailed": "Falha ao atualizar a auto disposição OIDC", - "updatePasswordResetFailed": "Falha ao atualizar a configuração de redefinição de senha", - "sessionTimeoutRange2": "Tempo limite da sessão deve ser entre 1 e 720 horas", - "sessionTimeoutSaved": "Timeout da sessão salvo", - "sessionTimeoutSaveFailed": "Falha ao salvar tempo limite de sessão", - "monitoringIntervalInvalid": "Valores de intervalo inválidos", - "monitoringSaved": "Configurações de monitoramento salvas", - "monitoringSaveFailed": "Falha ao salvar configurações de monitoramento", - "guacamoleSaved": "Configurações de Guacamole salvas", - "guacamoleSaveFailed": "Falha ao salvar configurações de Guacamole", - "guacamoleUpdateFailed": "Falha ao atualizar a configuração de Guacamole", - "logLevelUpdateFailed": "Falha ao atualizar o nível de log", - "oidcSaved": "Configuração OIDC salva", - "oidcSaveFailed": "Falha ao salvar configuração OIDC", - "oidcRemoved": "Configuração OIDC removida", - "oidcRemoveFailed": "Falha ao remover a configuração OIDC", - "createUserRequired": "Nome de usuário e senha são obrigatórios", - "createUserPasswordTooShort": "A senha deve ter pelo menos 6 caracteres", - "createUserSuccess": "Usuário \"{{username}}\" criado", - "createUserFailed": "Falha ao criar usuário", - "updateAdminStatusFailed": "Falha ao atualizar status do administrador", - "allSessionsRevoked": "Todas as sessões revogadas", - "revokeSessionsFailed": "Falha ao revogar sessões", - "createRoleRequired": "Nome e nome de exibição são necessários", - "createRoleSuccess": "Papel \"{{name}}\" criado", - "createRoleFailed": "Falha ao criar papel", - "apiKeyNameRequired": "O nome da chave é obrigatório", - "apiKeyUserRequired": "O ID do usuário é necessário", - "apiKeyCreatedSuccess": "Chave da API \"{{name}}\" criado", - "apiKeyCreateFailed": "Falha ao criar chave de API", - "exportSuccess": "Banco de dados exportado com sucesso", - "exportFailed": "Exportação do banco de dados falhou", - "importSelectFile": "Por favor, selecione um arquivo primeiro", - "importCompleted": "Importação concluída: {{total}} itens importados, {{skipped}} ignorados", - "importFailed": "Importação falhou: {{error}}", - "importError": "Falha ao importar banco" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Servidor", - "hostPlaceholder": "192.168.1.1 ou exemplo.com", - "portLabel": "Porta", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Usuário:", - "usernamePlaceholder": "usuário", - "authLabel": "Autenticação", - "passwordLabel": "Palavra-passe", - "passwordPlaceholder": "Senha", - "privateKeyLabel": "Chave Privada", - "privateKeyPlaceholder": "Colar chave privada...", - "credentialLabel": "Credencial", - "credentialPlaceholder": "Selecione uma credencial salva", - "connectToTerminal": "Conectar ao Terminal", - "connectToFiles": "Conectar aos arquivos" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nenhum terminal selecionado", - "noTerminalSelectedHint": "Abra uma aba de terminal SSH para ver seu histórico de comandos", - "searchPlaceholder": "Pesquisar histórico...", - "clearAll": "Limpar Tudo", - "noHistoryEntries": "Não há entradas no histórico", - "trackingDisabled": "Rastreamento do histórico está desativado", - "trackingDisabledHint": "Ative-o nas configurações do seu perfil para gravar comandos." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Gravação de Teclas", - "recordToTerminals": "Registro em terminais", - "selectAll": "TODOS", - "selectNone": "Nenhuma", - "noTerminalTabsOpen": "Nenhuma aba de terminal aberta", - "selectTerminalsAbove": "Selecione os terminais acima", - "broadcastInputPlaceholder": "Digite aqui para transmitir keystroks...", - "stopRecording": "Parar Gravação", - "startRecording": "Iniciar Gravação", - "settingsTitle": "Confirgurações", - "enableRightClickCopyPaste": "Habilitar copiar/colar com botão direito" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Disposição", - "selectLayoutAbove": "Selecione um layout acima", - "selectLayoutHint": "Escolha quantos painéis mostrar", - "panesTitle": "Painéis", - "openTabsTitle": "Abas abertas", - "dragTabsHint": "Arraste as guias para os painéis acima ou use a Atribuição Rápida.", - "dropHere": "Solte aqui", - "emptyPane": "Vazio", - "dashboard": "Painel", - "clearSplitScreen": "Limpar Tela dividida", - "quickAssign": "Atribuição rápida", - "alreadyAssigned": "Painel {{index}}", - "splitTab": "Aba dividida", - "addToSplit": "Adicionar à divisão", - "removeFromSplit": "Remover da divisão", - "assignToPane": "Atribuir ao painel" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Criar Snippet", - "createSnippetDescription": "Criar um novo snippet de comando para a execução rápida", - "nameLabel": "Nome:", - "namePlaceholder": "ex.: Reiniciar o Nginx", - "descriptionLabel": "Descrição:", - "descriptionPlaceholder": "Descrição opcional", - "optional": "Opcional", - "folderLabel": "pasta", - "noFolder": "Nenhuma pasta (Descategorizada)", - "commandLabel": "Comando", - "commandPlaceholder": "ex.: sudo systemctl reinicializa o nginx", - "cancel": "cancelar", - "createSnippetButton": "Criar Snippet", - "createFolderTitle": "Criar pasta", - "createFolderDescription": "Organize seus snippets em pastas", - "folderNameLabel": "Nome da pasta", - "folderNamePlaceholder": "ex.: Comandos do Sistema, Scripts Docker", - "folderColorLabel": "Cor da pasta", - "folderIconLabel": "Ícone da pasta", - "previewLabel": "Pré-visualizar", - "folderNameFallback": "Nome da pasta", - "createFolderButton": "Criar pasta", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "TODOS", - "selectNone": "Nenhuma", - "noTerminalTabsOpen": "Nenhuma aba de terminal aberta", - "searchPlaceholder": "Buscar snippets...", - "newSnippet": "Novo Trecho", - "newFolder": "Adicionar uma pasta", - "run": "Executar", - "noSnippetsInFolder": "Não há snippets nesta pasta", - "uncategorized": "Descategorizado", - "editSnippetTitle": "Editar Trecho", - "editSnippetDescription": "Atualizar este comando snippet", - "saveSnippetButton": "Salvar as alterações", - "createSuccess": "Snippet criado com sucesso", - "createFailed": "Falha ao criar snippet", - "updateSuccess": "Trecho atualizado com sucesso", - "updateFailed": "Falha ao atualizar snippet", - "deleteFailed": "Falha ao excluir snippet", - "folderCreateSuccess": "Pasta criada com sucesso", - "folderCreateFailed": "Falha ao criar pasta", - "editFolderTitle": "Editar Pasta", - "editFolderDescription": "Renomear ou alterar a aparência desta pasta", - "saveFolderButton": "Salvar as alterações", - "editFolder": "Editar Pasta", - "deleteFolder": "Excluir pasta", - "folderDeleteSuccess": "Pasta \"{{name}}excluída", - "folderDeleteFailed": "Falha ao excluir pasta", - "folderEditSuccess": "Pasta atualizada com sucesso", - "folderEditFailed": "Falha ao atualizar a pasta", - "confirmRunMessage": "Executar \"{{name}}\"?", - "confirmRunButton": "Correr", - "runSuccess": "Executado \"{{name}}\" em {{count}} terminal(is)", - "copySuccess": "Copiado \"{{name}}\" para área de transferência", - "shareTitle": "Compartilhar Snippet", - "shareUser": "Usuário", - "shareRole": "Funções", - "selectUser": "Selecione um usuário...", - "selectRole": "Selecione uma função...", - "shareSuccess": "Snippet compartilhado com sucesso", - "shareFailed": "Falha ao compartilhar snippet", - "revokeSuccess": "Acesso revogado", - "revokeFailed": "Falha ao revogar acesso", - "currentAccess": "Acesso atual", - "shareLoadError": "Não foi possível carregar os dados de compartilhamento", - "loading": "Carregandochar@@0", - "close": "FECHAR", - "reorderFailed": "Falha ao salvar a ordem do trecho de código" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "conta", - "sectionAppearance": "Aparência", - "sectionSecurity": "Segurança", - "sectionApiKeys": "Chaves API", - "sectionC2sTunnels": "Túneis C2S", - "usernameLabel": "Usuário:", - "roleLabel": "Funções", - "roleAdministrator": "Administrador", - "authMethodLabel": "Método de Autenticação", - "authMethodLocal": "Localização", - "twoFaLabel": "A2F", - "twoFaOn": "Ligado", - "twoFaOff": "Desligado", - "versionLabel": "Versão", - "deleteAccount": "Excluir Conta", - "deleteAccountDescription": "Excluir permanentemente sua conta", - "deleteButton": "excluir", - "deleteAccountPermanent": "Esta ação é permanente e não pode ser desfeita.", - "deleteAccountWarning": "Todas as sessões, hosts, credenciais e configurações serão excluídas permanentemente.", - "confirmPasswordDeletePlaceholder": "Digite sua senha para confirmar", - "languageLabel": "IDIOMA", - "themeLabel": "Tema", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Cor do acento", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Auto-completar comando", - "commandAutocompleteDesc": "Mostrar autocomplete durante a digitação", - "historyTracking": "Rastreamento do Histórico", - "historyTrackingDesc": "Rastrear comandos do terminal", - "syntaxHighlighting": "Realce de sintaxe", - "syntaxHighlightingDesc": "Destacar saída do terminal", - "commandPalette": "Paleta de comando", - "commandPaletteDesc": "Ativar atalho de teclado", - "reopenTabsOnLogin": "Reabrir abas ao fazer login", - "reopenTabsOnLoginDesc": "Restaure suas abas abertas ao fazer login ou atualizar a página, mesmo em outro dispositivo.", - "confirmTabClose": "Confirmar Fechar Aba", - "confirmTabCloseDesc": "Perguntar antes de fechar os guias dos terminais", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Exibir Tags do Host", - "showHostTagsDesc": "Exibir tags na lista de endereços", - "hostTrayOnClick": "Clique para expandir as ações do anfitrião", - "hostTrayOnClickDesc": "Sempre mostrar os botões de conexão; clicar para expandir as opções de gerenciamento em vez de passar o cursor sobre elas.", - "pinAppRail": "Aplicativo Pin Rail", - "pinAppRailDesc": "Mantenha a barra lateral esquerda do aplicativo sempre expandida, em vez de expandir ao passar o cursor sobre ela.", - "settingsSnippets": "Trechos", - "foldersCollapsed": "Pastas colapsadas", - "foldersCollapsedDesc": "Recolher pastas por padrão", - "confirmExecution": "Confirmar execução", - "confirmExecutionDesc": "Confirme antes de executar trechos", - "settingsUpdates": "Atualizações", - "disableUpdateChecks": "Desativar Verificações de Atualizações", - "disableUpdateChecksDesc": "Interromper a verificação de atualizações", - "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "2FA está habilitado", - "totpDisabled": "Adicionar segurança extra ao login", - "disable": "Desligado", - "enable": "Habilitado", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Escaneie o código QR ou insira o segredo no seu aplicativo de autenticação, e digite o código de 6 dígitos", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verificar", - "changePassword": "Mudar a senha", - "currentPasswordLabel": "Palavra-passe Atual", - "currentPasswordPlaceholder": "Senha atual", - "newPasswordLabel": "Nova Palavra-Passe", - "newPasswordPlaceholder": "Nova senha", - "confirmPasswordLabel": "Confirme a Nova Senha", - "confirmPasswordPlaceholder": "Confirme a nova senha", - "updatePassword": "Atualizar Senha", - "createApiKeyTitle": "Criar chave de API", - "createApiKeyDescription": "Gere uma nova chave de API para acesso programático.", - "apiKeyNameLabel": "Nome:", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "opcional", - "cancel": "cancelar", - "createKey": "Criar chave", - "apiKeyCount": "Chaves {{count}}", - "newKey": "Nova Chave", - "noApiKeys": "Ainda não há chaves de API.", - "apiKeyActive": "ativo", - "apiKeyUsageHint": "Inclua sua chave no", - "apiKeyUsageHintHeader": "cabeçalho.", - "apiKeyPermissionsHint": "Chaves herdam as permissões do usuário criador.", - "roleUser": "Usuário", - "authMethodDual": "Dupla Autenticação", - "authMethodOidc": "OCIDADE", - "totpSetupFailed": "Falha ao iniciar a configuração TOTP", - "totpEnter6Digits": "Insira um código de 6 dígitos", - "totpEnabledSuccess": "Autenticação de dois fatores ativada", - "totpInvalidCode": "Código inválido, por favor tente novamente", - "totpDisableInputRequired": "Digite seu código TOTP ou senha", - "totpDisabledSuccess": "Autenticação de dois fatores desativada", - "totpDisableFailed": "Falha ao desativar 2FA", - "totpDisableTitle": "Desativar 2FA", - "totpDisablePlaceholder": "Insira o código ou senha TOTP", - "totpDisableConfirm": "Desativar 2FA", - "totpContinueVerify": "Continuar a verificar", - "totpVerifyTitle": "Verificar Código", - "totpBackupTitle": "Códigos de recuperação", - "totpDownloadBackup": "Baixar códigos de recuperação", - "done": "Concluído", - "secretCopied": "Segredo copiado para área de transferência", - "apiKeyNameRequired": "O nome da chave é obrigatório", - "apiKeyCreated": "Chave da API \"{{name}}\" criado", - "apiKeyCreateFailed": "Falha ao criar chave de API", - "apiKeyUser": "Usuário", - "apiKeyExpires": "Expira", - "apiKeyRevoked": "Chave de API \"{{name}}\" revogada", - "apiKeyRevokeFailed": "Falha ao revogar chave de API", - "passwordFieldsRequired": "Senhas atuais e novas são necessárias", - "passwordMismatch": "As senhas não coincidem", - "passwordTooShort": "A senha deve ter pelo menos 6 caracteres", - "passwordUpdated": "Senha atualizada com sucesso", - "passwordUpdateFailed": "Falha ao atualizar a senha", - "deletePasswordRequired": "É necessário uma senha para excluir sua conta", - "deleteFailed": "Falha ao excluir conta", - "deleting": "Excluindo...", - "colorPickerTooltip": "Abrir seletor de cores", - "themeSystem": "SISTEMA", - "themeLight": "Fino", - "themeDark": "Escuro", - "themeDracula": "Drácula", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Ensolarado", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Um Escuro", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/pt_PT.json b/src/ui/locales/translated/pt_PT.json index 61baf774..9bc5af32 100644 --- a/src/ui/locales/translated/pt_PT.json +++ b/src/ui/locales/translated/pt_PT.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Pastas", - "folder": "pasta", - "password": "Palavra-passe", - "key": "Chave", - "sshPrivateKey": "Chave privada SSH", - "upload": "Transferir", - "keyPassword": "Senha da Chave", - "sshKey": "Chave SSH", - "uploadPrivateKeyFile": "Carregar arquivo de chave privada", - "searchCredentials": "Pesquisar credenciais...", - "addCredential": "Adicionar Credencial", - "caCertificate": "Certificado CA (-cert.pub)", - "caCertificateDescription": "Opcional: Carregar ou colar o arquivo de certificado assinado por CA (por exemplo, id_ed25519-cert.pub). Necessário quando o servidor SSH utiliza autorização baseada em certificados.", - "uploadCertFile": "Enviar arquivo -cert.pub", - "clearCert": "Limpar", - "certLoaded": "Certificado carregado", - "certPublicKeyLabel": "Certificado CA", - "certTypeLabel": "Tipo de certificado", - "pasteOrUploadCert": "Colar ou fazer upload de um certificado -cert.pub...", - "hasCaCert": "Possui certificado CA", - "noCaCert": "Nenhum certificado CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Falha ao carregar alertas", - "failedToDismissAlert": "Falha ao descartar alerta" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configuração Servidor", - "description": "Configure o URL do servidor do Termix para conectar aos seus serviços de backend", - "serverUrl": "URL do servidor", - "enterServerUrl": "Por favor, insira uma URL de servidor", - "saveFailed": "Falha ao salvar a configuração", - "saveError": "Erro ao salvar configuração", - "saving": "Salvando...", - "saveConfig": "Salvar configuração", - "helpText": "Digite a URL onde o servidor do seu Termix está executando (por exemplo, http://localhost:30001 ou https://seu-servidor.com)", - "changeServer": "Alterar Servidor", - "mustIncludeProtocol": "O URL do servidor deve começar com http:// ou https://", - "allowInvalidCertificate": "Permitir certificado inválido", - "allowInvalidCertificateDesc": "Utilize apenas em servidores auto-hospedados fidedignos com certificados autoassinados ou de endereço IP.", - "useEmbedded": "Usar servidor local", - "embeddedDesc": "Executar Termix com o servidor local embutido (não é necessário um servidor remoto)", - "embeddedConnecting": "Conectando ao servidor local...", - "embeddedNotReady": "O servidor local ainda não está pronto. Por favor, aguarde um momento e tente novamente.", - "localServer": "Servidor local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Erro de verificação de versão", - "checkFailed": "Não foi possível verificar se há atualizações", - "upToDate": "O aplicativo está atualizado", - "currentVersion": "Você está executando a versão {{version}}", - "updateAvailable": "Atualização disponível", - "newVersionAvailable": "Uma nova versão está disponível! Você está executando {{current}}, mas {{latest}} está disponível.", - "betaVersion": "Versão Beta", - "betaVersionDesc": "Você está executando {{current}}, que é mais recente que a última versão estável {{latest}}.", - "releasedOn": "Lançado em {{date}}", - "downloadUpdate": "Baixar atualização", - "checking": "Verificando atualizações...", - "checkUpdates": "Procurar por atualizações", - "checkingUpdates": "Verificando atualizações...", - "updateRequired": "Atualização Necessária" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "FECHAR", + "close": "Close", "minimize": "Minimize", - "online": "Disponível", - "offline": "Desconectado", - "continue": "Continuar", - "maintenance": "Manutenção", - "degraded": "Degradado", - "error": "ERRO", - "warning": "ATENÇÃO", - "unsavedChanges": "Alterações não salvas", - "dismiss": "Descartar", - "loading": "Carregandochar@@0", - "optional": "Opcional", - "connect": "Conectar", - "copied": "Copiado", - "connecting": "Conectandochar@@0", - "updateAvailable": "Atualização disponível", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Abrir em nova aba", - "noReleases": "Sem lançamentos", - "updatesAndReleases": "Atualizações e Versões", - "newVersionAvailable": "Uma nova versão ({{version}}) está disponível.", - "failedToFetchUpdateInfo": "Falha ao buscar informações de atualização", - "preRelease": "Pré-lançamento", - "noReleasesFound": "Nenhuma versão encontrada.", - "cancel": "cancelar", - "username": "Usuário:", - "login": "Conectar-se", - "register": "Cadastrar", - "password": "Palavra-passe", - "confirmPassword": "Confirmar senha", - "back": "Anterior", - "save": "Guardar", - "saving": "Salvando...", - "delete": "excluir", - "rename": "Renomear", - "edit": "Alterar", - "add": "Adicionar", - "confirm": "Confirmar", - "no": "Não", - "or": "ou", - "next": "Próximo", - "previous": "Anterior", - "refresh": "atualizar", - "language": "IDIOMA", - "checking": "Verificandochar@@0", - "checkingDatabase": "Verificando conexão com o banco de dados...", - "checkingAuthentication": "Verificando autenticação...", - "backendReconnected": "Conexão do servidor restaurada", - "connectionDegraded": "Conexão do servidor perdida, recuperando…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Excluir", - "create": "Crio", - "update": "Atualização", - "copy": "copiar", - "copyFailed": "Falha ao copiar para área de transferência", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "RESTAURAR", - "of": "de" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Residencial", + "home": "Home", "terminal": "Terminal", - "docker": "Atracador", - "tunnels": "Túneis", - "fileManager": "Gerenciador de Arquivos", - "serverStats": "Estatísticas do servidor", - "admin": "Administrador", - "userProfile": "Informações do Perfil", - "splitScreen": "Dividir a tela", - "confirmClose": "Fechar esta sessão ativa?", - "close": "FECHAR", - "cancel": "cancelar", - "sshManager": "Gerenciador SSH", - "cannotSplitTab": "Não é possível dividir esta aba", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copiar Senha", - "copySudoPassword": "Copiar Senha do Sudo", - "passwordCopied": "Senha copiada para área de transferência", - "noPasswordAvailable": "Nenhuma senha disponível", - "failedToCopyPassword": "Falha ao copiar a senha", - "refreshTab": "Atualizar conexão", - "openFileManager": "Abrir Gerenciador de Arquivos", - "dashboard": "Painel", - "networkGraph": "Gráfico da Rede", - "quickConnect": "Conexão rápida", - "sshTools": "Ferramentas SSH", - "history": "Histórico", - "hosts": "Anfitriões", - "snippets": "Trechos", - "hostManager": "Gerenciador de Host", - "credentials": "Credenciais", - "connections": "Conexões", - "roleAdministrator": "Administrador", - "roleUser": "Usuário" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Anfitriões", - "noHosts": "Nenhum host SSH", - "retry": "Repetir", - "refresh": "atualizar", - "optional": "Opcional", - "downloadSample": "Baixar Exemplo", - "failedToDeleteHost": "Falha ao excluir {{name}}", - "importSkipExisting": "Importar (ignorar existente)", - "connectionDetails": "Detalhes da conexão", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Computador Remoto", - "port": "Porta", - "username": "Usuário:", - "folder": "pasta", - "tags": "Etiquetas", - "pin": "PIN", - "addHost": "Adicionar Host", - "editHost": "Editar Host", - "cloneHost": "Clonar Host", - "enableTerminal": "Ativar Terminal", - "enableTunnel": "Ativar Túnel", - "enableFileManager": "Ativar Gerenciador de Arquivos", - "enableDocker": "Ativar Docker", - "defaultPath": "Caminho Padrão", - "connection": "Ligação", - "upload": "Transferir", - "authentication": "Autenticação", - "password": "Palavra-passe", - "key": "Chave", - "credential": "Credencial", - "none": "Nenhuma", - "sshPrivateKey": "Chave privada SSH", - "keyType": "Tipo de chave", - "uploadFile": "Enviar Arquivo", - "tabGeneral": "Gerais", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Túneis", - "tabDocker": "Atracador", - "tabFiles": "arquivos", - "tabStats": "Estatísticas", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Compartilhando", - "tabAuthentication": "Autenticação", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Túnel", - "fileManager": "Gerenciador de Arquivos", - "serverStats": "Estatísticas do servidor", - "status": "SItuação", - "folderRenamed": "Pasta \"{{oldName}}\" renomeada para \"{{newName}}\" com sucesso", - "failedToRenameFolder": "Falha ao renomear pasta", - "movedToFolder": "Movido para \"{{folder}}\"", - "editHostTooltip": "Editar host", - "statusChecks": "Verificações de Status", - "metricsCollection": "Coleção de Métricas", - "metricsInterval": "Intervalo de Coleção de Métricas", - "metricsIntervalDesc": "Com que frequência coletar estatísticas do servidor (5s - 1h)", - "behavior": "Comportamento", - "themePreview": "Pré-visualização do tema", - "theme": "Tema", - "fontFamily": "Família de fonte", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Espaçamento das letras", - "lineHeight": "Altura da linha", - "cursorStyle": "Estilo do cursor", - "cursorBlink": "Pisca do Cursor", - "scrollbackBuffer": "Buffer de rolagem", - "bellStyle": "Estilo do sino", - "rightClickSelectsWord": "Clique com botão direito seleciona Palavra", - "fastScrollModifier": "Modificador de rolagem rápido", - "fastScrollSensitivity": "Sensibilidade de rolagem rápida", - "sshAgentForwarding": "Encaminhamento de agente SSH", - "backspaceMode": "Modo Backspace", - "startupSnippet": "Trecho de Inicialização", - "selectSnippet": "Selecionar snippet", - "forceKeyboardInteractive": "Forçar teclado interativo", - "overrideCredentialUsername": "Substituir o nome de usuário credencial", - "overrideCredentialUsernameDesc": "Use o nome de usuário especificado acima ao invés do nome de usuário da credencial", - "jumpHostChain": "Corrente de Host Salto", - "portKnocking": "Knocking de Porta", - "addKnock": "Adicionar Porta", - "addProxyNode": "Adicionar node", - "proxyNode": "Nó Proxy", - "proxyType": "Tipo de proxy", - "quickActions": "Ações rápidas", - "sudoPasswordAutoFill": "Auto-preenchimento de Senha Sudo", - "sudoPassword": "Senha Sudo", - "keepaliveInterval": "Intervalo de Mantícora (ms)", - "moshCommand": "Comando MOSH", - "environmentVariables": "Variáveis de Ambiente", - "addVariable": "Adicionar Variável", - "docker": "Atracador", - "copyTerminalUrl": "Copiar URL do Terminal", - "copyFileManagerUrl": "Copiar URL do Gerenciador de Arquivos", - "copyRemoteDesktopUrl": "Copiar URL do Desktop Remoto", - "failedToConnect": "Falha ao conectar ao console", - "connect": "Conectar", - "disconnect": "Desconectar", - "start": "Iniciar", - "enableStatusCheck": "Ativar verificação de status", - "enableMetrics": "Habilitar métricas", - "bulkUpdateFailed": "Atualização em massa falhou", - "selectAll": "Selecionar Todos", - "deselectAll": "Desmarcar Todos", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Projétil Seguro", - "virtualNetwork": "Rede Virtual", - "unencryptedShell": "shell não criptografado", - "addressIp": "Endereço / IP", - "friendlyName": "Nome Amigável", - "folderAndAdvanced": "Pasta & Avançada", - "privateNotes": "Notas do trabalho", - "privateNotesPlaceholder": "Detalhes sobre este servidor...", - "pinToTop": "Fixar no topo", - "pinToTopDesc": "Sempre mostrar este host no topo da lista", - "portKnockingSequence": "Sequência de Portas Knocking", - "addKnockBtn": "Adicionar toco", - "noPortKnocking": "Nenhuma porta ativa configurada.", - "knockPort": "Porta do Knock", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Atraso após (ms)", - "useSocks5Proxy": "Usar Proxy SOCKS5", - "useSocks5ProxyDesc": "Conexão de rota através de um servidor proxy", - "proxyHost": "Servidor de Proxy", - "proxyPort": "Porta do Proxy", - "proxyUsername": "Usuário do Proxy", - "proxyPassword": "Senha do Proxy", - "proxySingleMode": "Proxy Único", - "proxyChainMode": "Corrente de Proxy", - "you": "Tu", - "jumpHostChainLabel": "Corrente de Host Salto", - "addJumpBtn": "Adicionar Salto", - "noJumpHosts": "Nenhum host de salto configurado.", - "selectAServer": "Selecione um servidor...", - "sshPort": "Porta SSH", - "authMethod": "Método de Autenticação", - "storedCredential": "Credenciais armazenadas", - "selectACredential": "Selecione uma credencial...", - "keyTypeLabel": "Tipo de chave", - "keyTypeAuto": "Detecção automática", - "keyPasteTab": "Colar", - "keyUploadTab": "Transferir", - "keyFileLoaded": "Arquivo de chave carregado", - "keyUploadClick": "Clique para enviar .pem / .key / .ppk", - "clearKey": "Limpar Chave", - "keySaved": "Chave SSH salva", - "keyReplaceNotice": "colar uma nova chave abaixo para substituí-la", - "keyPassphraseSaved": "Senha salva, digite a mudar", - "replaceKey": "Substituir chave", - "forceKeyboardInteractiveLabel": "Forçar teclado interativo", - "forceKeyboardInteractiveShortDesc": "Forçar entrada de senha manual, mesmo se chaves estiverem presentes", - "terminalAppearance": "Aparência do Terminal", - "colorTheme": "Tema de Cor", - "fontFamilyLabel": "Família de fonte", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Estilo do cursor", - "letterSpacingPx": "Espaçamento das letras (px)", - "lineHeightLabel": "Altura da linha", - "bellStyleLabel": "Estilo do sino", - "backspaceModeLabel": "Modo Backspace", - "cursorBlinking": "Piscar cursor", - "cursorBlinkingDesc": "Ativar animação piscante para o cursor do terminal", - "rightClickSelectsWordLabel": "Clique com botão direito seleciona Palavra", - "rightClickSelectsWordShortDesc": "Selecione a palavra no botão direito do mouse", - "behaviorAndAdvanced": "Comportamento & Avançado", - "scrollbackBufferLabel": "Buffer de rolagem", - "scrollbackMaxLines": "Número máximo de linhas mantidas no histórico", - "sshAgentForwardingLabel": "Encaminhamento de agente SSH", - "sshAgentForwardingShortDesc": "Passe suas chaves SSH locais para este host", - "enableAutoMosh": "Habilitar Mosh Automático", - "enableAutoMoshDesc": "Prefere Mosh por SSH se disponível", - "enableAutoTmux": "Ativar Tmux Automático", - "enableAutoTmuxDesc": "Iniciar ou anexar automaticamente à sessão tmux", - "sudoPasswordAutoFillLabel": "Auto-preenchimento de Senha Sudo", - "sudoPasswordAutoFillShortDesc": "Automaticamente fornecer senha sudo quando solicitado", - "sudoPasswordLabel": "Senha Sudo", - "environmentVariablesLabel": "Variáveis de Ambiente", - "addVariableBtn": "Adicionar Variável", - "noEnvVars": "Nenhuma variável de ambiente configurada.", - "fastScrollModifierLabel": "Modificador de rolagem rápido", - "fastScrollSensitivityLabel": "Sensibilidade de rolagem rápida", - "moshCommandLabel": "Comando Mosh", - "startupSnippetLabel": "Trecho de Inicialização", - "keepaliveIntervalLabel": "Intervalo de Keepalive (segundos)", - "maxKeepaliveMisses": "Máximo de Perdas Keepalive", - "tunnelSettings": "Configurações de túnel", - "enableTunneling": "Ativar Túnel", - "enableTunnelingDesc": "Ativar a funcionalidade de túnel SSH para esse host", - "serverTunnelsSection": "Túneis do servidor", - "addTunnelBtn": "Adicionar túnel", - "noTunnelsConfigured": "Nenhum túnel configurado.", - "tunnelLabel": "Túnel {{number}}", - "tunnelType": "Tipo de túnel", - "tunnelModeLocalDesc": "Encaminhar uma porta local para uma porta no servidor remoto (ou um host acessível a partir da).", - "tunnelModeRemoteDesc": "Encaminha uma porta no servidor remoto de volta para uma porta local na sua máquina.", - "tunnelModeDynamicDesc": "Crie um proxy SOCKS5 em uma porta local para encaminhamento de porta dinâmica.", - "sameHost": "Este host (túnel direto)", - "endpointHost": "Host de Endpoint", - "endpointPort": "Porta de Endpoint", - "bindHost": "Vincular Host", - "sourcePort": "Porta de origem", - "maxRetries": "Máximo de buscas", - "retryIntervalS": "Intervalo de Repetir", - "autoStartLabel": "Auto-iniciar", - "autoStartDesc": "Conectar automaticamente este túnel quando o host é carregado", - "tunnelConnecting": "Túnel conectando...", - "tunnelDisconnected": "Túnel desconectado", - "failedToConnectTunnel": "Falha ao conectar", - "failedToDisconnectTunnel": "Falha ao desconectar", - "dockerIntegration": "Integração Docker", - "enableDockerMonitor": "Ativar Docker", - "enableDockerMonitorDesc": "Monitorar e gerenciar contêineres neste host via Docker", - "enableFileManagerMonitor": "Ativar Gerenciador de Arquivos", - "enableFileManagerMonitorDesc": "Navegue e gerencie arquivos neste host através do SFTP", - "defaultPathLabel": "Caminho Padrão", - "fileManagerPathHint": "O diretório a abrir quando o gerenciador de arquivos é aberto para este host.", - "statusChecksLabel": "Verificações de Status", - "enableStatusChecks": "Habilitar Verificação de Status", - "enableStatusChecksDesc": "Periodicamente ping este host para verificar a disponibilidade", - "useGlobalInterval": "Usar Intervalo Global", - "useGlobalIntervalDesc": "Substituir com o intervalo de verificação de status de todo o servidor", - "checkIntervalS": "Intervalo de verificação", - "checkIntervalDesc": "Segundos entre cada ping de conectividade", - "metricsCollectionLabel": "Coleção de Métricas", - "enableMetricsLabel": "Habilitar métricas", - "enableMetricsDesc": "Coletar CPU, RAM, disco e uso de rede deste host", - "useGlobalMetrics": "Usar Intervalo Global", - "useGlobalMetricsDesc": "Sobrescrever com o intervalo de métricas para o servidor", - "metricsIntervalS": "Intervalo de Métricas", - "metricsIntervalDesc2": "Segundos entre snapshots métricos", - "visibleWidgets": "Widgets visíveis", - "cpuUsageLabel": "Uso da CPU", - "cpuUsageDesc": "CPU por cento, carregar médias de carga, gráfico de linha", - "memoryLabel": "Memória Utilizada", - "memoryDesc": "Uso de RAM, troque, em cache", - "storageLabel": "Uso do disco", - "storageDesc": "Uso do disco por ponto de montagem", - "networkLabel": "Interfaces de Rede", - "networkDesc": "Lista de interface e banda", - "uptimeLabel": "Tempo em atividade", - "uptimeDesc": "Tempo de atividade e inicialização do sistema", - "systemInfoLabel": "Informações do Sistema", - "systemInfoDesc": "OS, kernel, hostname, arquitetura", - "recentLoginsLabel": "Logins recentes", - "recentLoginsDesc": "Eventos bem-sucedidos e falhados de login", - "topProcessesLabel": "Processos mais Populares", - "topProcessesDesc": "PID, CPU%, MEM%, comando", - "listeningPortsLabel": "Porta de Escuta", - "listeningPortsDesc": "Abrir portas com processo e estado", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, status do SELinux", - "quickActionsLabel": "Ações rápidas", - "quickActionsToolbar": "Ações rápidas aparecem como botões na barra de estatísticas do servidor para execução de comando com um clique.", - "noQuickActions": "Nenhuma ação rápida ainda.", - "buttonLabel": "Rótulo do botão", - "selectSnippetPlaceholder": "Selecionar snippet...", - "addActionBtn": "Adicionar ação", - "hostSharedSuccessfully": "Hospedeiro compartilhado com sucesso", - "failedToShareHost": "Falha ao compartilhar host", - "accessRevoked": "Acesso revogado", - "failedToRevokeAccess": "Falha ao revogar acesso", - "cancelBtn": "cancelar", - "savingBtn": "Salvando...", - "addHostBtn": "Adicionar Host", - "hostUpdated": "Servidor atualizado", - "hostCreated": "Host criado", - "failedToSave": "Falha ao salvar o host", - "credentialUpdated": "Credencial atualizada", - "credentialCreated": "Credencial criada", - "failedToSaveCredential": "Falha ao salvar credencial", - "backToHosts": "Voltar aos hosts", - "backToCredentials": "Voltar às Credenciais", - "pinned": "Fixado", - "noHostsFound": "Nenhum host encontrado", - "tryDifferentTerm": "Tente um termo diferente", - "addFirstHost": "Adicione o seu primeiro host para começar", - "noCredentialsFound": "Nenhuma credencial encontrada", - "addCredentialBtn": "Adicionar Credencial", - "updateCredentialBtn": "Atualizar Credencial", - "features": "Funcionalidades", - "noFolder": "(Sem pasta)", - "deleteSelected": "excluir", - "exitSelection": "Sair da seleção", - "importSkip": "Importar (ignorar existente)", - "importOverwrite": "Importação (sobrescrever)", - "collapseBtn": "Recolher", - "importExportBtn": "Importar / Exportar", - "hostStatusesRefreshed": "Status do host atualizados", - "failedToRefreshHosts": "Falha ao atualizar hosts", - "movedHostTo": "Mudou {{host}} para \"{{folder}}\"", - "failedToMoveHost": "Falha ao mover host", - "folderRenamedTo": "Pasta renomeada para \"{{name}}\"", - "deletedFolder": "Pasta apagada \"{{name}}\"", - "failedToDeleteFolder": "Falha ao excluir pasta", - "deleteAllInFolder": "Excluir todos os hosts em \"{{name}}\"? Isso não pode ser desfeito.", - "deletedHost": "{{name}} Excluído", - "copiedToClipboard": "Copiado para o clipboard", - "terminalUrlCopied": "URL do terminal copiado", - "fileManagerUrlCopied": "URL do Gerenciador de Arquivos copiado", - "tunnelUrlCopied": "URL do túnel copiado", - "dockerUrlCopied": "URL Docker copiado", - "serverStatsUrlCopied": "URL das estatísticas do servidor copiado", - "rdpUrlCopied": "URL RDP copiada", - "vncUrlCopied": "URL VNC copiada", - "telnetUrlCopied": "URL da rede copiada", - "remoteDesktopUrlCopied": "URL da área de trabalho remoto copiada", - "expandActions": "Expandir ações", - "collapseActions": "Ações de cobrança", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Pacote mágico enviado para {{name}}", - "wakeOnLanError": "Falha ao enviar o pacote mágico", - "cloneHostAction": "Clonar Host", - "copyAddress": "Copiar Endereço", - "copyLink": "Copiar link", - "copyTerminalUrlAction": "Copiar URL do Terminal", - "copyFileManagerUrlAction": "Copiar URL do Gerenciador de Arquivos", - "copyTunnelUrlAction": "Copiar URL do túnel", - "copyDockerUrlAction": "Copiar URL do Docker", - "copyServerStatsUrlAction": "Copiar URL das Estatísticas do Servidor", - "copyRdpUrlAction": "Copiar URL do RDP", - "copyVncUrlAction": "Copiar URL do VNC", - "copyTelnetUrlAction": "Copiar URL da Telnet", - "copyRemoteDesktopUrlAction": "Copiar URL do Desktop Remoto", - "deleteCredentialConfirm": "Excluir credencial \"{{name}}\"?", - "deletedCredential": "{{name}} Excluído", - "deploySSHKeyTitle": "Chave SSH de deploy", - "deployingBtn": "Implementando...", - "deployBtn": "Implantar", - "failedToDeployKey": "Falha ao publicar chave", - "deleteHostsConfirm": "Excluir o host {{count}}{{plural}}? Isso não pode ser desfeito.", - "movedToRoot": "Movido para a raiz", - "failedToMoveHosts": "Falha ao mover hosts", - "enableTerminalFeature": "Ativar Terminal", - "disableTerminalFeature": "Desativar Terminal", - "enableFilesFeature": "Habilitar arquivos", - "disableFilesFeature": "Desabilitar Arquivos", - "enableTunnelsFeature": "Ativar túneis", - "disableTunnelsFeature": "Desativar Túneis", - "enableDockerFeature": "Ativar Docker", - "disableDockerFeature": "Desativar Docker", - "addTagsPlaceholder": "Adicionar tags...", - "authDetails": "Detalhes de autenticação", - "credType": "tipo", - "generateKeyPairDesc": "Gerar um novo par de chaves, chaves privadas e públicas serão preenchidas automaticamente.", - "generatingKey": "Gerando...", - "generateLabel": "Gerar {{label}}", - "uploadFileBtn": "Upload de arquivo", - "keyPassphraseOptional": "Senha Chave (Opcional)", - "sshPublicKeyOptional": "Chave Pública SSH (Opcional)", - "publicKeyGenerated": "Chave pública gerada", - "failedToGeneratePublicKey": "Falha ao derivar chave pública", - "publicKeyCopied": "Chave pública copiada", - "keyPairGenerated": "Um par de chaves {{label}} gerado", - "failedToGenerateKeyPair": "Falha ao gerar o par de chaves", - "searchHostsPlaceholder": "Pesquisar hospedeiros, endereços, etiquetas…", - "searchCredentialsPlaceholder": "Pesquisar credenciais…", - "refreshBtn": "atualizar", - "addTag": "Adicionar tags...", - "deleteConfirmBtn": "excluir", - "tunnelRequirementsText": "O servidor SSH deve ter GatewayPorts sim, AllowTcpForwarding yes, e permitRootLogin yes definido em /etc/ssh/sshd_config.", - "deleteHostConfirm": "Excluir \"{{name}}\"?", - "enableAtLeastOneProtocol": "Ative pelo menos um protocolo acima para configurar as configurações de autenticação e conexão.", - "keyPassphrase": "Senha Chave", - "connectBtn": "Conectar", - "disconnectBtn": "Desconectar", - "basicInformation": "Informações Básicas", - "authDetailsSection": "Detalhes de autenticação", - "credTypeLabel": "tipo", - "hostsTab": "Anfitriões", - "credentialsTab": "Credenciais", - "selectMultiple": "Selecionar múltiplos", - "selectHosts": "Selecionar hosts", - "connectionLabel": "Ligação", - "authenticationLabel": "Autenticação", - "generateKeyPairTitle": "Gerar par de chaves", - "generateKeyPairDescription": "Gerar um novo par de chaves, chaves privadas e públicas serão preenchidas automaticamente.", - "generateFromPrivateKey": "Gerar a partir da Chave Privada", - "refreshBtn2": "atualizar", - "exitSelectionTitle": "Sair da seleção", - "exportAll": "Exportar tudo", - "addHostBtn2": "Adicionar Host", - "addCredentialBtn2": "Adicionar Credencial", - "checkingHostStatuses": "Verificando status do host...", - "pinnedSection": "Fixado", - "hostsExported": "Hosts exportados com sucesso", - "exportFailed": "Falha ao exportar hosts", - "sampleDownloaded": "Exemplo de arquivo baixado", - "failedToDeleteCredential2": "Falha ao excluir credencial", - "noFolderOption": "(Sem pasta)", - "nSelected": "{{count}} selecionado", - "featuresMenu": "Funcionalidades", - "moveMenu": "Mover-se", - "cancelSelection": "cancelar", - "deployDialogDesc": "Implantar {{name}} em um host authorized_keys.", - "targetHostLabel": "Host de destino", - "selectHostOption": "Selecione um host...", - "keyDeployedSuccess": "Chave implantada com sucesso", - "failedToDeployKey2": "Falha ao publicar chave", - "deletedCount": "Hosts {{count}} excluídos", - "failedToDeleteCount": "Falha ao excluir hosts {{count}}", - "duplicatedHost": "Duplicado \"{{name}}\"", - "failedToDuplicateHost": "Falha ao duplicar host", - "updatedCount": "Hosts {{count}} atualizados", - "friendlyNameLabel": "Nome Amigável", - "descriptionLabel": "Descrição:", - "loadingHost": "Carregando host...", - "loadingHosts": "Carregando hosts...", - "loadingCredentials": "Carregando credenciais...", - "noHostsYet": "Nenhum host ainda", - "noHostsMatchSearch": "Nenhum host corresponde à sua pesquisa", - "hostNotFound": "Host não encontrado", - "searchHosts": "Procurar hosts...", - "sortHosts": "Classificar hosts", - "sortDefault": "Ordem padrão", - "sortNameAsc": "Nome (A → Z)", - "sortNameDesc": "Nome (Z → A)", - "sortIpAsc": "Endereço IP (Asc)", - "sortIpDesc": "Endereço IP (Descrição)", - "sortOnlineFirst": "Online Primeiro", - "sortOfflineFirst": "Primeiro offline", - "sortPinnedFirst": "Fixado primeiro", - "filterHosts": "Filtrar hosts", - "filterClearAll": "Limpar filtros", - "filterStatusGroup": "Estatuto", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fixado", - "filterAuthGroup": "Tipo de autenticação", - "filterAuthPassword": "Palavra-passe", - "filterAuthKey": "Chave SSH", - "filterAuthCredential": "Credencial", - "filterAuthNone": "Nenhum", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protocolo", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Funcionalidades", - "filterFeatureTerminal": "terminal", - "filterFeatureFileManager": "Gestor de arquivos", - "filterFeatureTunnel": "Túnel", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Etiquetas", - "shareHost": "Host de compartilhamento", - "shareHostTitle": "Compartilhar: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Este host deve usar uma credencial para permitir o compartilhamento. Edite o host e atribua uma credencial primeiro." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Ligação", - "authentication": "Autenticação", - "connectionSettings": "Configurações de conexão", - "displaySettings": "Configurações de exibição", - "audioSettings": "Configurações de Áudio", - "rdpPerformance": "Desempenho RDP", - "deviceRedirection": "Redirecionamento do dispositivo", - "session": "Sessão", - "gateway": "Desvio", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Área", - "sessionRecording": "Gravação de Sessão", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Configurações do VNC", - "terminalSettings": "Configurações do Terminal", - "rdpPort": "Porta RDP", - "username": "Usuário:", - "password": "Palavra-passe", - "domain": "Domínio", - "securityMode": "Modo de Segurança", - "colorDepth": "Profundidade da Cor", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Altura", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Método de Redimensionar", - "clientName": "Nome do Cliente", - "initialProgram": "Programa inicial", - "serverLayout": "Layout do Servidor", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Porta do Gateway", - "gatewayUsername": "Usuário do Gateway", - "gatewayPassword": "Senha do Gateway", - "gatewayDomain": "Domínio de Gateway", - "remoteAppProgram": "Programa RemoteApp", - "workingDirectory": "Diretório de trabalho", - "arguments": "Parâmetros", - "normalizeLineEndings": "Normalizar Extensões de Linha", - "recordingPath": "Caminho de gravação", - "recordingName": "Nome da gravação", - "macAddress": "Endereço MAC", - "broadcastAddress": "Endereço de Transmissão", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Tempo de Espera", - "driveName": "Nome da unidade", - "drivePath": "Caminho do Drive", - "ignoreCertificate": "Ignorar Certificado", - "ignoreCertificateDesc": "Permitir conexões para hosts com certificados auto-assinados", - "forceLossless": "Forçar perda", - "forceLosslessDesc": "Forçar codificação de imagens sem perdas (maior qualidade, mais largura de banda)", - "disableAudio": "Desativar Áudio", - "disableAudioDesc": "Silenciar todo o áudio da sessão remota", - "enableAudioInput": "Habilitar Entrada de Áudio (Microfone)", - "enableAudioInputDesc": "Encaminhar o microfone local para a sessão remota", - "wallpaper": "Plano de fundo", - "wallpaperDesc": "Mostrar plano de fundo da área de trabalho (desabilitar melhora o desempenho)", - "theming": "Temas", - "themingDesc": "Ativar temas e estilos visuais", - "fontSmoothing": "Suavização da Fonte", - "fontSmoothingDesc": "Habilitar renderização de fonte ClearType", - "fullWindowDrag": "Arrastar a janela completa", - "fullWindowDragDesc": "Mostrar conteúdo da janela ao arrastar", - "desktopComposition": "Composição da mesa", - "desktopCompositionDesc": "Habilitar efeitos de vidro Aero", - "menuAnimations": "Animações do menu", - "menuAnimationsDesc": "Ativar animações de deslize e deslize do menu", - "disableBitmapCaching": "Desabilitar Cache Bitmap", - "disableBitmapCachingDesc": "Desativar o cache do bitmap (pode ajudar com glitches)", - "disableOffscreenCaching": "Desativar Cache Offscreen", - "disableOffscreenCachingDesc": "Desativar cache de tela desligada", - "disableGlyphCaching": "Desativar Cache Glyph", - "disableGlyphCachingDesc": "Desligar cache glifo", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Usar RemoteFX pipeline graphics", - "enablePrinting": "Ativar impressão", - "enablePrintingDesc": "Redirecionar impressoras locais para a sessão remota", - "enableDriveRedirection": "Habilitar redirecionamento de unidade", - "enableDriveRedirectionDesc": "Mapear uma pasta local como um drive na sessão remota", - "createDrivePath": "Criar caminho de unidade", - "createDrivePathDesc": "Criar automaticamente a pasta se ela não existir", - "disableDownload": "Desativar download", - "disableDownloadDesc": "Impedir o download de arquivos da sessão remota", - "disableUpload": "Desabilitar Upload", - "disableUploadDesc": "Evitar o envio de arquivos para a sessão remota", - "enableTouch": "Ativar Touch", - "enableTouchDesc": "Ativar encaminhamento de entrada de toque", - "consoleSession": "Sessão de Console", - "consoleSessionDesc": "Conectar ao console (sessão 0) em vez de uma nova sessão", - "sendWolPacket": "Enviar Pacote WOL", - "sendWolPacketDesc": "Envie um pacote mágico para acordar este host antes de conectar", - "disableCopy": "Desativar Cópia", - "disableCopyDesc": "Impedir a cópia de texto da sessão remota", - "disablePaste": "Desativar Colar", - "disablePasteDesc": "Impedir colar texto na sessão remota", - "createPathIfMissing": "Criar caminho se estiver faltando", - "createPathIfMissingDesc": "Criar automaticamente o diretório de gravação", - "excludeOutput": "Excluir saída", - "excludeOutputDesc": "Não gravar a saída da tela (apenas metadados)", - "excludeMouse": "Excluir mouse", - "excludeMouseDesc": "Não grave os movimentos do mouse", - "includeKeystrokes": "Incluir Teclas Pressionadas", - "includeKeystrokesDesc": "Gravar teclas rasteiras brutas além da saída da tela", - "vncPort": "Porta VNC", - "vncPassword": "Senha VNC", - "vncUsernameOptional": "Nome de usuário (opcional)", - "vncLeaveBlank": "Deixe em branco se não necessário", - "cursorMode": "Modo Cursor", - "swapRedBlue": "Trocar Vermelho/Azul", - "swapRedBlueDesc": "Troque os canais de cor vermelho e azul (corrige alguns problemas de cor)", - "readOnly": "Somente leitura", - "readOnlyDesc": "Ver a tela remota sem enviar qualquer entrada", - "telnetPort": "Porta de Telnet", - "terminalType": "Tipo de Terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Esquema de Cor", - "backspaceKey": "Chave de Backspace", - "saveHostFirst": "Salve o host primeiro.", - "sharingOptionsAfterSave": "Opções de compartilhamento estão disponíveis após o host ser salvo.", - "sharingLoadError": "Não foi possível carregar os dados de compartilhamento. Verifique sua conexão e tente novamente.", - "shareHostSection": "Host de compartilhamento", - "shareWithUser": "Compartilhar com Usuário", - "shareWithRole": "Compartilhar com Função", - "selectUser": "Selecionar usuário", - "selectRole": "Selecione a função", - "selectUserOption": "Selecione um usuário...", - "selectRoleOption": "Selecione uma função...", - "permissionLevel": "Nível de permissão", - "expiresInHours": "Expira em (horas)", - "noExpiryPlaceholder": "Deixe em branco para não expirar", - "shareBtn": "Compartilhar", - "currentAccess": "Acesso atual", - "typeHeader": "tipo", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Permisschar@@0o", - "grantedByHeader": "Concedido por", - "expiresHeader": "Expira", - "noAccessEntries": "Ainda não há entradas de acesso.", - "expiredLabel": "Expirado", - "neverLabel": "nunca", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "cancelar", - "savingBtn": "Salvando...", - "updateHostBtn": "Atualizar Host", - "addHostBtn": "Adicionar Host" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Procurar por hosts, comandos ou configurações...", - "quickActions": "Ações rápidas", - "hostManager": "Gerenciador de Host", - "hostManagerDesc": "Gerenciar, adicionar ou editar hosts", - "addNewHost": "Adicionar Novo Host", - "addNewHostDesc": "Registrar um novo host", - "adminSettings": "Configurações de administrador", - "adminSettingsDesc": "Configurar preferências do sistema e usuários", - "userProfile": "Informações do Perfil", - "userProfileDesc": "Gerenciar sua conta e preferências", - "addCredential": "Adicionar Credencial", - "addCredentialDesc": "Armazenar chaves SSH ou senhas", - "recentActivity": "Atividade recente", - "serversAndHosts": "Servidores e Hosts", - "noHostsFound": "Nenhum host encontrado \"{{search}}\"", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", "links": "Links", "navigate": "Navigate", - "select": "Selecionar", - "toggleWith": "Alternar com" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Nenhuma aba atribuída", - "focusedPane": "Painel ativo" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Sem ligações", - "noConnectionsDesc": "Abra um terminal, um gestor de ficheiros ou uma área de trabalho remota para ver aqui as ligações.", - "connectedFor": "Ligado por {{duration}}", - "connected": "Conectado", - "disconnected": "Desconectado", - "closeTab": "Fechar aba", - "closeConnection": "Conexão próxima", - "forgetTab": "Esquecer-se", - "removeBackground": "Remover", - "reconnect": "Reconectar", - "reopenTab": "Reabrir", - "sectionOpen": "Aberto", - "sectionBackground": "Fundo", - "backgroundDesc": "As sessões permanecem ativas durante 30 minutos após a desconexão e podem ser reconectadas.", - "persisted": "Persistiu em segundo plano", - "expiresIn": "Expira em {{duration}}", - "search": "Procurar ligações...", - "noSearchResults": "Nenhuma ligação corresponde à sua pesquisa." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Conectando à sessão {{type}}", - "connectionError": "Erro de conexão", - "connectionFailed": "Conexão falhou", - "failedToConnect": "Falha ao obter token de conexão", - "hostNotFound": "Host não encontrado", - "noHostSelected": "Nenhum host selecionado", - "reconnect": "Reconectar", - "retry": "Repetir", - "guacdUnavailable": "Serviço de desktop remoto (guacd) não disponível. Por favor certifique-se que o guacd está sendo executado e acessível e configurado corretamente nas configurações de administração.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Tela de bloqueio)", - "winKey": "Chave do Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Turno", - "win": "Vitória", - "stickyActive": "{{key}} (retido - clique para liberar)", - "stickyInactive": "{{key}} (clique para travar)", - "esc": "Prosseguir", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Residencial", - "end": "Término", - "pageUp": "Página Acima", - "pageDown": "Página abaixo", - "arrowUp": "Seta para cima", - "arrowDown": "Seta para baixo", - "arrowLeft": "Seta para esquerda", - "arrowRight": "Seta para direita", - "fnToggle": "Chaves de função", - "reconnect": "Reconectar Sessão", - "collapse": "Recolher barra de ferramentas", - "expand": "Expandir barra de ferramentas", - "dragHandle": "Arraste para reposicionar" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Conectar ao Host", - "clear": "Limpar", - "paste": "Colar", - "reconnect": "Reconectar", - "connectionLost": "Conexão perdida", - "connected": "Conectado", - "clipboardWriteFailed": "Falha ao copiar para a área de transferência. Certifique-se de que a página seja servida por HTTPS ou localhost.", - "clipboardReadFailed": "Falha ao ler da área de transferência. Certifique-se de que as permissões da área de transferência foram concedidas.", - "clipboardHttpWarning": "Colar requer HTTPS. Use Ctrl+Shift+V ou sirva Termix em HTTPS.", - "unknownError": "Ocorreu um erro desconhecido", - "websocketError": "Erro de conexão WebSocket", - "connecting": "Conectandochar@@0", - "noHostSelected": "Nenhum host selecionado", - "reconnecting": "Reconectando... ({{attempt}}/{{max}})", - "reconnected": "Reconectado com sucesso", - "tmuxSessionCreated": "sessão tmux criada: {{name}}", - "tmuxSessionAttached": "sessão tmux anexada: {{name}}", - "tmuxUnavailable": "tmux não está instalado no host remoto, voltando ao shell padrão", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", "tmuxSessionPickerTitle": "tmux Sessions", - "tmuxSessionPickerDesc": "Sessões tmux existentes encontradas neste host. Selecione uma para reanexar ou criar uma nova sessão.", - "tmuxWindows": "Janelas", - "tmuxWindowCount": "{{count}} janela", - "tmuxAttached": "Clientes anexados", - "tmuxAttachedCount": "{{count}} anexou", - "tmuxLastActivity": "Última atividade", - "tmuxTimeJustNow": "neste momento", - "tmuxTimeMinutes": "{{count}}m atrás", - "tmuxTimeHours": "{{count}}há h", - "tmuxTimeDays": "{{count}}há alguns dias", - "tmuxCreateNew": "Iniciar nova sessão", - "tmuxCopyHint": "Ajustar seleção e pressione Enter para copiar para área de transferência", - "tmuxDetach": "Desanexar da sessão do tmux", - "tmuxDetached": "Desanexado da sessão do tmux", - "maxReconnectAttemptsReached": "Máximo de tentativas de reconexão alcançadas", - "closeTab": "FECHAR", - "connectionTimeout": "Conexão expirada", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Executando {{command}} - {{host}}", - "totpRequired": "Autenticação dupla requerida", - "totpCodeLabel": "Código de verificação", - "totpVerify": "Verificar", - "warpgateAuthRequired": "Autenticação Warpgate Necessária", - "warpgateSecurityKey": "Chave de Segurança", - "warpgateAuthUrl": "URL de autenticação", - "warpgateOpenBrowser": "Abrir no Navegador", - "warpgateContinue": "Eu concluí a autenticação", - "opksshAuthRequired": "Autenticação OPKSSH necessária", - "opksshAuthDescription": "Conclua a autenticação no seu navegador para continuar. Essa sessão permanecerá válida por 24 horas.", - "opksshOpenBrowser": "Abra o navegador para autenticar", - "opksshWaitingForAuth": "Aguardando autenticação no navegador...", - "opksshAuthenticating": "Processando autenticação...", - "opksshTimeout": "Tempo de autenticação esgotado. Por favor, tente novamente.", - "opksshAuthFailed": "Falha na autenticação. Por favor, verifique suas credenciais e tente novamente.", - "opksshSignInWith": "Entrar com {{provider}}", - "sudoPasswordPopupTitle": "Inserir senha?", - "websocketAbnormalClose": "A conexão foi fechada inesperadamente. Isso pode ser devido a um problema de configuração do proxy reverso ou SSL. Por favor, verifique os logs do servidor.", - "connectionLogTitle": "Registro de conexão", - "connectionLogCopy": "Copiar logs para área de transferência", - "connectionLogEmpty": "Ainda não há registros de conexão", - "connectionLogWaiting": "Aguardando registros de conexão...", - "connectionLogCopied": "Logs de conexão copiados para área de transferência", - "connectionLogCopyFailed": "Falha ao copiar os logs para a área de transferência", - "connectionRejected": "Conexão rejeitada pelo servidor. Verifique sua autenticação e configuração de rede.", - "hostKeyRejected": "Verificação de chave do host SSH rejeitada. Conexão cancelada.", - "sessionTakenOver": "A sessão foi aberta em outra aba. Reconectando..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Nenhum host selecionado", - "initializingEditor": "Inicializando o editor...", - "file": "Arquivo", - "folder": "pasta", - "uploadFile": "Enviar Arquivo", - "downloadFile": "BAIXAR", - "extractArchive": "Extrair arquivo", - "extractingArchive": "Extraindo {{name}}...", - "archiveExtractedSuccessfully": "{{name}} extraído com sucesso", - "extractFailed": "Falha ao extrair", - "compressFile": "Compactar arquivo", - "compressFiles": "Compactar arquivos", - "compressFilesDesc": "Comprimir itens {{count}} em um arquivo", - "archiveName": "Nome do Arquivo", - "enterArchiveName": "Informe o nome do arquivo...", - "compressionFormat": "Formato de compressão", - "selectedFiles": "Arquivos selecionados", - "andMoreFiles": "e mais {{count}}...", - "compress": "Compactar", - "compressingFiles": "Comprimindo {{count}} itens em {{name}}...", - "filesCompressedSuccessfully": "{{name}} criado com sucesso", - "compressFailed": "Compressão falhou", - "edit": "Alterar", - "preview": "Pré-visualizar", - "previous": "Anterior", - "next": "Próximo", - "pageXOfY": "Página {{current}} de {{total}}", - "zoomOut": "Diminuir o zoom", - "zoomIn": "Aumentar zoom", - "newFile": "Novo arquivo", - "newFolder": "Adicionar uma pasta", - "rename": "Renomear", - "uploading": "Enviando...", - "uploadingFile": "Enviando {{name}}...", - "fileName": "Nome do arquivo", - "folderName": "Nome da pasta", - "fileUploadedSuccessfully": "Arquivo \"{{name}}\" carregado com sucesso", - "failedToUploadFile": "Falha ao carregar arquivo", - "fileDownloadedSuccessfully": "Arquivo \"{{name}}\" baixado com sucesso", - "failedToDownloadFile": "Não foi possível baixar o arquivo", - "fileCreatedSuccessfully": "Arquivo \"{{name}}\" criado com sucesso", - "folderCreatedSuccessfully": "Pasta \"{{name}}\" criada com sucesso", - "failedToCreateItem": "Falha ao criar item", - "operationFailed": "A operação {{operation}} falhou para {{name}}: {{error}}", - "failedToResolveSymlink": "Falha ao resolver link simbólico", - "itemsDeletedSuccessfully": "Itens {{count}} excluídos com sucesso", - "failedToDeleteItems": "Falha ao excluir itens", - "sudoPasswordRequired": "Senha do Administrador Necessária", - "enterSudoPassword": "Digite a senha sudo para continuar esta operação", - "sudoPassword": "Senha sudo", - "sudoOperationFailed": "Operação sudo falhou", - "sudoAuthFailed": "Falha na autenticação Sudo", - "dragFilesToUpload": "Solte os arquivos aqui para enviar", - "emptyFolder": "Esta pasta está vazia", - "searchFiles": "Pesquisar arquivos...", - "upload": "Transferir", - "selectHostToStart": "Selecione um host para iniciar o gerenciamento de arquivos", - "sshRequiredForFileManager": "O gerenciador de arquivos requer SSH. Este host não tem o SSH habilitado.", - "failedToConnect": "Falha ao conectar com SSH", - "failedToLoadDirectory": "Falha ao carregar diretório", - "noSSHConnection": "Nenhuma conexão SSH disponível", - "copy": "copiar", - "cut": "Recortar", - "paste": "Colar", - "copyPath": "Copiar Caminho", - "copyPaths": "Copiar caminhos", - "delete": "excluir", - "properties": "propriedades", - "refresh": "atualizar", - "downloadFiles": "Baixar arquivos {{count}} para o Navegador", - "copyFiles": "Copiar itens {{count}}", - "cutFiles": "Cortar {{count}} itens", - "deleteFiles": "Excluir {{count}} itens", - "filesCopiedToClipboard": "{{count}} itens copiados para a área de transferência", - "filesCutToClipboard": "{{count}} itens cortados na área de transferência", - "pathCopiedToClipboard": "Caminho copiado para área de transferência", - "pathsCopiedToClipboard": "{{count}} caminhos copiados para a área de transferência", - "failedToCopyPath": "Falha ao copiar caminho para área de transferência", - "movedItems": "Itens de {{count}} movidos", - "failedToDeleteItem": "Falha ao excluir item", - "itemRenamedSuccessfully": "{{type}} renomeado com sucesso", - "failedToRenameItem": "Falha ao renomear item", - "download": "BAIXAR", - "permissions": "Permissões", - "size": "Tamanho", - "modified": "Modificado", - "path": "Caminho", - "confirmDelete": "Tem certeza que deseja excluir {{name}}?", - "permissionDenied": "Permissão negada", - "serverError": "Erro no Servidor", - "fileSavedSuccessfully": "Arquivo salvo com sucesso", - "failedToSaveFile": "Falha ao salvar arquivo", - "confirmDeleteSingleItem": "Tem certeza de que quer apagar permanentemente \"{{name}}\"?", - "confirmDeleteMultipleItems": "Tem certeza de que deseja excluir permanentemente itens {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Tem certeza de que deseja excluir permanentemente itens {{count}} ? Isso inclui pastas e seu conteúdo.", - "confirmDeleteFolder": "Tem a certeza de que pretende eliminar permanentemente a pasta \"{{name}}\" e todo o seu conteúdo?", - "permanentDeleteWarning": "Esta ação não pode ser desfeita. O(s) item(ns) serão excluídos permanentemente do servidor.", - "recent": "Recente", - "pinned": "Fixado", - "folderShortcuts": "Atalhos da pasta", - "failedToReconnectSSH": "Falha ao reconectar a sessão SSH", - "openTerminalHere": "Abrir Terminal Aqui", - "run": "Executar", - "openTerminalInFolder": "Abrir Terminal nesta Pasta", - "openTerminalInFileLocation": "Abrir Terminal no Local do Arquivo", - "runningFile": "Executando - {{file}}", - "onlyRunExecutableFiles": "Só é possível executar arquivos executáveis", - "directories": "Diretórios", - "removedFromRecentFiles": "Removido \"{{name}}\" dos arquivos recentes", - "removeFailed": "Falha ao remover", - "unpinnedSuccessfully": "\"{{name}}desafixado\" com sucesso", - "unpinFailed": "Desafixar falhou", - "removedShortcut": "Atalho \"{{name}} removido \"", - "removeShortcutFailed": "Falha ao remover atalho", - "clearedAllRecentFiles": "Todos os arquivos recentes foram removidos", - "clearFailed": "Falha ao apagar", - "removeFromRecentFiles": "Remover dos arquivos recentes", - "clearAllRecentFiles": "Limpar todos os arquivos recentes", - "unpinFile": "Desafixar arquivo", - "removeShortcut": "Remover atalho", - "pinFile": "Fixar arquivo", - "addToShortcuts": "Adicionar a atalhos", - "pasteFailed": "A colagem falhou", - "noUndoableActions": "Nenhuma ação irreversível", - "undoCopySuccess": "Operação de cópia desfeita: {{count}} excluídos arquivos copiados", - "undoCopyFailedDelete": "Desfazer falhou: Não foi possível excluir nenhum arquivo copiado", - "undoCopyFailedNoInfo": "Desfazer falhou: Não foi possível encontrar informações do arquivo copiado", - "undoMoveSuccess": "Operação movida desfeita: arquivos {{count}} movidos de volta para o local original", - "undoMoveFailedMove": "Desfazer falhou: Não foi possível mover nenhum arquivo de volta", - "undoMoveFailedNoInfo": "Desfazer falhou: Não foi possível encontrar informação de arquivo movido", - "undoDeleteNotSupported": "Operação de exclusão não pode ser desfeita: Os arquivos foram excluídos permanentemente do servidor", - "undoTypeNotSupported": "Tipo de operação desfazer não suportado", - "undoOperationFailed": "Falha na operação", - "unknownError": "Erro desconhecido", - "confirm": "Confirmar", - "find": "Localizar...", - "replace": "Substituir", - "downloadInstead": "Em vez disso, baixar", - "keyboardShortcuts": "Atalhos do teclado", - "searchAndReplace": "Pesquisar e substituir", - "editing": "Editando", - "search": "Pesquisa", - "findNext": "Localizar Próximo", - "findPrevious": "Localizar Anterior", - "save": "Guardar", - "selectAll": "Selecionar Todos", - "undo": "Desfazer", - "redo": "Refazer", - "moveLineUp": "Mover Linha para Cima", - "moveLineDown": "Mover Linha para Baixo", - "toggleComment": "Alternar comentário", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Não foi possível carregar a imagem", - "startTyping": "Comece a digitar...", - "unknownSize": "Tamanho desconhecido", - "fileIsEmpty": "O arquivo está vazio", - "largeFileWarning": "Aviso de arquivo grande", - "largeFileWarningDesc": "Este arquivo tem o tamanho {{size}} , o que pode causar problemas de desempenho quando aberto como texto.", - "fileNotFoundAndRemoved": "O arquivo \"{{name}}\" não foi encontrado e foi removido dos arquivos recentes/fixados", - "failedToLoadFile": "Falha ao carregar arquivo: {{error}}", - "serverErrorOccurred": "Ocorreu um erro no servidor. Tente novamente mais tarde.", - "autoSaveFailed": "Auto-salvamento falhou", - "fileAutoSaved": "Arquivo salvo automaticamente", - "moveFileFailed": "Falha ao mover {{name}}", - "moveOperationFailed": "Falha ao mover", - "canOnlyCompareFiles": "Só é possível comparar dois arquivos", - "comparingFiles": "Comparando arquivos: {{file1}} e {{file2}}", - "dragFailed": "Falha ao arrastar", - "filePinnedSuccessfully": "Arquivo \"{{name}}\" fixado com sucesso", - "pinFileFailed": "Falha ao fixar arquivo", - "fileUnpinnedSuccessfully": "Arquivo \"{{name}}\" desafixado com sucesso", - "unpinFileFailed": "Falha ao desafixar arquivo", - "shortcutAddedSuccessfully": "Atalho para a pasta \"{{name}}\" adicionado com sucesso", - "addShortcutFailed": "Falha ao adicionar atalho", - "operationCompletedSuccessfully": "{{operation}} Itens {{count}} com sucesso", - "operationCompleted": "{{operation}} Itens em {{count}}", - "downloadFileSuccess": "Arquivo {{name}} baixado com sucesso", - "downloadFileFailed": "Download falhou", - "moveTo": "Mover para {{name}}", - "diffCompareWith": "Comparar diferenças com {{name}}", - "dragOutsideToDownload": "Arraste fora do janela para baixar (arquivos{{count}})", - "newFolderDefault": "Pasta", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Itens de {{count}} movidos com sucesso para {{target}}", - "move": "Mover-se", - "searchInFile": "Procurar no arquivo (Ctrl+F)", - "showKeyboardShortcuts": "Exibir atalhos de teclado", - "startWritingMarkdown": "Comece a escrever o seu conteúdo markdown...", - "loadingFileComparison": "Carregando comparação de arquivo...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Comparar", - "sideBySide": "Lado a lado", - "inline": "Embutido", - "fileComparison": "Comparação de arquivos: {{file1}} vs {{file2}}", - "fileTooLarge": "Arquivo muito grande: {{error}}", - "sshConnectionFailed": "Falha na conexão SSH. Por favor, verifique sua conexão com {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Falha ao carregar arquivo: {{error}}", - "connecting": "Conectandochar@@0", - "connectedSuccessfully": "Conectado com sucesso", - "totpVerificationFailed": "Falha na verificação TOTP", - "warpgateVerificationFailed": "Falha na autenticação do Warpgate", - "authenticationFailed": "Falha na autenticação", - "verificationCodePrompt": "Código de verificação:", - "changePermissions": "Alterar permissões", - "currentPermissions": "Permissões Atuais", - "owner": "Proprietário", - "group": "grupo", - "others": "Outros", - "read": "Lido", - "write": "Salvar", - "execute": "Executar", - "permissionsChangedSuccessfully": "Permissões alteradas com sucesso", - "failedToChangePermissions": "Falha ao alterar permissões", - "name": "Nome:", - "sortByName": "Nome:", - "sortByDate": "Data de Modificação", - "sortBySize": "Tamanho", - "ascending": "Ascendente", - "descending": "Descendente", - "root": "raiz", - "new": "Novidades", - "sortBy": "Classificar por", - "items": "itens", - "selected": "Selecionado", - "editor": "Editores", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", "octal": "Octal", - "storage": "Armazenamento", - "disk": "Disco", - "used": "Utilizado", - "of": "de", - "toggleSidebar": "Alternar barra lateral", - "cannotLoadPdf": "Não é possível carregar PDF", - "pdfLoadError": "Houve um erro ao carregar este arquivo PDF.", - "loadingPdf": "Carregando PDF...", - "loadingPage": "Carregando página..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copiar para o host…", - "moveToHost": "Mover para o host…", - "copyItemsToHost": "Copiar {{count}} itens para o host…", - "moveItemsToHost": "Mover {{count}} itens para o host…", - "noHostsConnected": "Não existem outros servidores de gestão de ficheiros disponíveis.", - "noHostsConnectedHint": "Adicione outro host SSH com o Gestor de Ficheiros ativado no Gestor de Hosts.", - "selectDestinationHost": "Selecione o host de destino", - "destinationPath": "Caminho de destino", - "recentDestinations": "Destinos recentes", - "collapseRecentDestinations": "Recolher destinos recentes", - "expandRecentDestinations": "Expandir destinos recentes", - "browseFolders": "Procurar pastas de destino", - "browseDestination": "Navegue ou introduza o caminho", - "confirmCopy": "Cópia", - "confirmMove": "Mover", - "transferring": "Transferindo…", - "compressing": "Comprimindo…", - "extracting": "Extração…", - "transferringItems": "Transferir {{current}} de {{total}} itens…", - "transferSuccess": "Transferência concluída", - "transferError": "A transferência falhou", - "transferPartial": "Transferência concluída com {{count}} erros", - "transferPartialHint": "Não foi possível transferir: {{paths}}", - "itemsSummary": "{{count}} artigos", - "destMustBeDirectory": "O destino deve ser um diretório para transferências de múltiplos itens.", - "selectThisFolder": "Selecione esta pasta", - "browsePathWillBeCreated": "Esta pasta ainda não existe. Será criada quando a transferência começar.", - "browsePathError": "Não foi possível abrir este caminho no host de destino.", - "goUp": "Subir", - "copyFolderToHost": "Copiar pasta para o host…", - "moveFolderToHost": "Mover pasta para o host…", - "hostReady": "Pronto", - "hostConnecting": "Ligar…", - "hostDisconnected": "Não conectado", - "hostAuthRequired": "Autenticação necessária — abra primeiro o Gestor de Ficheiros neste host.", - "hostConnectionFailed": "Falha na ligação", - "metricsTitle": "Horários de transferência", - "metricsPrepare": "Preparar destino: {{duration}}", - "metricsCompress": "Comprimir na origem: {{duration}}", - "metricsHopSourceRead": "Origem → servidor: {{throughput}}", - "metricsHopDestSftpWrite": "Servidor → destino (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Servidor → destino (local): {{throughput}}", - "metricsTransfer": "De uma ponta à outra: {{throughput}} ({{duration}})", - "metricsExtract": "Extrair no destino: {{duration}}", - "metricsSourceDelete": "Remover da fonte: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Compressão no host de origem…", - "progressExtracting": "Extração no destino…", - "progressTransferring": "Transferir dados…", - "progressReconnecting": "Reconectando…", - "parallelSegmentsLabel": "Faixas de transferência paralelas", - "parallelSegmentsOption": "{{count}} faixas", - "parallelSegmentsHint": "Os ficheiros grandes estão divididos em partes de 256 MB. Várias vias utilizam ligações separadas (como iniciar várias transferências) para obter uma taxa de transferência total mais elevada.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} faixas)", - "progressTransferringItems": "Transferir ficheiros ({{current}} de {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} ficheiros", - "sourceNotDeletedPartial": "Ficheiros de origem mantidos (transferência parcial)", - "jumpHostLimitation": "Ambos os hosts devem ser acessíveis a partir do servidor Termix. O encaminhamento direto de host para host não é suportado.", - "cancel": "Cancelar", - "methodLabel": "Método de transferência", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Arquivo Tar", - "methodItemSftp": "SFTP por ficheiro", - "methodAutoHint": "Seleciona o método de compressão (tar) ou SFTP por ficheiro com base na quantidade de ficheiros, tamanho e compressibilidade. Os ficheiros individuais utilizam sempre SFTP de streaming.", - "methodTarHint": "Comprima na origem, transfira um único ficheiro comprimido e extraia no destino. Requer a utilização do tar em ambos os hosts Unix.", - "methodItemSftpHint": "Transfira cada ficheiro individualmente via SFTP. Funciona em todos os hosts, incluindo o Windows.", - "methodPreviewLoading": "Cálculo do método de transferência…", - "methodPreviewError": "Não foi possível visualizar o método de transferência. O servidor ainda escolherá um método quando iniciar o servidor.", - "methodPreviewWillUseTar": "Será utilizado: Ficheiro tar", - "methodPreviewWillUseItemSftp": "Será utilizado: SFTP por ficheiro", - "methodPreviewScanSummary": "{{fileCount}} ficheiros, {{totalSize}} total (verificado no host de origem).", - "methodItemSftpLimitation": "Cada ficheiro utiliza o mesmo fluxo SFTP como uma cópia de ficheiro único, um após o outro. O progresso é combinado em todos os ficheiros, pelo que a barra se move lentamente durante a transferência de ficheiros grandes.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Escolheu SFTP por ficheiro.", - "user_tar": "Escolheu o ficheiro tar.", - "tar_unavailable": "O ficheiro tar não está disponível num ou em ambos os hosts — em vez disso, será utilizado o SFTP por ficheiro.", - "windows_host": "O sistema operativo host é o Windows — o ficheiro tar não é utilizado.", - "auto_multi_large": "Automático: vários ficheiros, incluindo um ficheiro grande ({{largestSize}}) com dados compressíveis — pacotes tar numa única transferência.", - "auto_single_large_in_archive": "Automático: um ficheiro grande ({{largestSize}}) neste conjunto — SFTP por ficheiro.", - "auto_many_incompressible": "Automático: dados maioritariamente não compressíveis — SFTP por ficheiro.", - "auto_many_files": "Automático: demasiados ficheiros ({{fileCount}}) — o tar reduz a sobrecarga por ficheiro.", - "auto_default": "Automático: SFTP por ficheiro para este conjunto." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Cancelar", - "progressCancelling": "Cancelando…", - "progressStalled": "Paralisado", - "resumedHint": "Religado a uma transferência ativa iniciada noutra janela.", - "transferCancelled": "Transferência cancelada", - "transferCancelledCopyHint": "Alguns ficheiros podem permanecer no destino.", - "transferCancelledMoveHint": "É possível que alguns ficheiros permaneçam no destino. Os ficheiros de origem não foram removidos.", - "cleanupDestFiles": "destino de limpeza", - "cleanupDestFilesSuccess": "Ficheiros parciais removidos do destino.", - "cleanupDestFilesPartial": "Alguns ficheiros parciais não puderam ser removidos.", - "cleanupDestFilesNothing": "Nada a limpar no destino.", - "cleanupDestFilesError": "A limpeza falhou", - "retryTransfer": "Tentar novamente", - "retryTransferError": "A nova tentativa falhou.", - "transferFailedRetryHint": "Os dados parciais foram mantidos no destino. A tentativa será retomada quando a ligação for restabelecida." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Sem Túneis SSH", - "createFirstTunnelMessage": "Você ainda não criou nenhum túnel SSH. Configure as conexões de túnel no Gerenciador de Host para começar.", - "connected": "Conectado", - "disconnected": "Desconectado", - "connecting": "Conectandochar@@0", - "error": "ERRO", - "canceling": "Cancelando...", - "connect": "Conectar", - "disconnect": "Desconectar", - "cancel": "cancelar", - "port": "Porta", - "localPort": "Porta local", - "remotePort": "Porta remota", - "currentHostPort": "Porta atual do Host", - "endpointPort": "Porta de Endpoint", - "bindIp": "IP local", - "endpointSshConfig": "Configuração do Endpoint SSH", - "endpointSshHost": "Host do Endpoint SSH", - "endpointSshHostPlaceholder": "Selecione um host configurado", - "endpointSshHostRequired": "Selecione um host SSH para cada túnel do cliente.", - "attempt": "Tentativa {{current}} de {{max}}", - "nextRetryIn": "Próxima repetição em {{seconds}} segundos", - "clientTunnels": "Túneis do cliente", - "clientTunnel": "Túnel Cliente", - "addClientTunnel": "Adicionar túnel de cliente", - "noClientTunnels": "Nenhum túnel do cliente configurado nesta área de trabalho.", - "tunnelName": "Nome do túnel", - "remoteHost": "Host Remoto", - "autoStart": "Início automático", - "clientAutoStartDesc": "Inicia quando este cliente desktop abre e permanece conectado.", - "clientManualStartDesc": "Use Iniciar e Parar dessa linha. O Termix não abrirá automaticamente.", - "clientRemoteServerNote": "O encaminhamento remoto pode exigir AllowTcpForwarding e GatewayPorts no servidor SSH do endpoint. A porta remota é fechada quando este desktop desconecta.", - "clientTunnelStarted": "Túnel de cliente iniciado", - "clientTunnelStopped": "Túnel de cliente parado", - "tunnelTestSucceeded": "Teste de túnel bem-sucedido", - "tunnelTestFailed": "Teste do túnel falhou", - "localSaved": "Túneis do cliente salvos", - "localSaveError": "Falha ao salvar túneis locais do cliente", - "invalidBindIp": "O IP local deve ter um endereço IPv4 válido.", - "invalidLocalTargetIp": "IP de destino local deve ser um endereço IPv4 válido.", - "invalidLocalPort": "A porta local deve estar entre 1 e 65535.", - "invalidRemotePort": "Porta remota deve estar entre 1 e 65535.", - "invalidLocalTargetPort": "Porta local de destino deve estar entre 1 e 65535.", - "invalidEndpointPort": "Porta de Endpoint deve estar entre 1 e 65535.", - "duplicateAutoStartBind": "Apenas um túnel de cliente iniciar automaticamente pode usar o {{bind}}.", - "manualControlError": "Falha ao atualizar o estado do túnel.", - "active": "ativo", - "start": "Iniciar", - "stop": "Interromper", - "test": "teste", - "type": "Tipo de túnel", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Remoto (-R)", - "typeDynamic": "Dinâmico (-D)", - "typeServerLocalDesc": "Host atual para endpoint.", - "typeServerRemoteDesc": "Endpoint de volta ao host atual.", - "typeClientLocalDesc": "Computador local para endpoint.", - "typeClientRemoteDesc": "Endpoint de volta ao computador local.", - "typeClientDynamicDesc": "SOCKS no computador local.", - "typeDynamicDesc": "Encaminhar tráfego SOCKS5 CONNECT através de SSH", - "forwardDescriptionServerLocal": "Host atual {{sourcePort}} → endpoint {{endpointPort}}.", - "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → host atual {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS no host atual {{sourcePort}}.", - "forwardDescriptionClientLocal": "{{sourcePort}} local → remoto {{endpointPort}}.", - "forwardDescriptionClientRemote": "{{sourcePort}} remoto → {{endpointPort}} local.", - "forwardDescriptionClientDynamic": "SOCKS na porta local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "{{localPort}} local → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → {{localPort}} local", - "autoNameClientDynamic": "format@@0 SOCKS {{localPort}} via {{endpoint}}", - "route": "Itinerário:", - "lastStarted": "Último início", - "lastTested": "Último teste", - "lastError": "Último erro", - "maxRetries": "Máximo de buscas", - "maxRetriesDescription": "Quantidade máxima de tentativas de repetição.", - "retryInterval": "Intervalo de Repetir (segundos)", - "retryIntervalDescription": "Tempo de espera entre tentativas novamente.", - "local": "Localização", - "remote": "Remoto", - "destination": "Destino", - "host": "Servidor", - "mode": "Modo", - "noHostSelected": "Nenhum host selecionado", - "working": "Trabalhando..." + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "cpu", - "memory": "Memória", - "disk": "Disco", - "network": "Rede", - "uptime": "Tempo em atividade", - "processes": "processos", - "available": "Disponível", - "free": "Gratuito", - "connecting": "Conectandochar@@0", - "connectionFailed": "Falha ao conectar ao servidor", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", "naCpus": "N/A CPU(s)", - "cpuCores_one": "Núcleo {{count}}", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Uso da CPU", - "memoryUsage": "Memória Utilizada", - "diskUsage": "Uso do disco", - "failedToFetchHostConfig": "Falha ao buscar a configuração do host", - "serverOffline": "Servidor Offline", - "cannotFetchMetrics": "Não é possível buscar métricas do servidor offline", - "totpFailed": "Falha na verificação TOTP", - "noneAuthNotSupported": "As estatísticas do servidor não suportam tipo de autenticação 'nenhum'.", - "load": "Carregar", - "systemInfo": "Informação do Sistema", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Sistema operacional", + "operatingSystem": "Operating System", "kernel": "Kernel", - "seconds": "segundos", - "networkInterfaces": "Interfaces de Rede", - "noInterfacesFound": "Nenhuma interface de rede encontrada", - "noProcessesFound": "Nenhum processo encontrado", - "loginStats": "Estatísticas de Login SSH", - "noRecentLoginData": "Nenhum dado de login recente", - "executingQuickAction": "Executando {{name}}...", - "quickActionSuccess": "{{name}} completado com sucesso", - "quickActionFailed": "{{name}} falhou", - "quickActionError": "Falha ao executar {{name}}", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Porta de Escuta", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Porta", - "address": "Endereço", - "process": "processo", - "noData": "Sem dados de portas de escuta" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inativo", - "policy": "Política", - "rules": "regras", - "noData": "Nenhum firewall disponível", - "action": "Acão", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Porta", - "source": "fonte", - "anywhere": "Em qualquer lugar" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Carga Média", - "swap": "Trocar", - "architecture": "Arquitetura", - "refresh": "atualizar", - "retry": "Repetir" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "SSH auto-hospedado e gestão de desktop remoto", - "loginTitle": "Faça login para Termix", - "registerTitle": "Criar conta", - "forgotPassword": "Esqueceu a senha?", - "rememberMe": "Lembrar dispositivo por 30 dias (inclui TOTP)", - "noAccount": "Não possui uma conta?", - "hasAccount": "Já possui uma conta?", - "twoFactorAuth": "Autenticação dupla", - "enterCode": "Inserir código de verificação", - "backupCode": "Ou usar código de backup", - "verifyCode": "Verificar Código", - "redirectingToApp": "Redirecionando para o aplicativo...", - "sshAuthenticationRequired": "Autenticação SSH necessária", - "sshNoKeyboardInteractive": "Autenticação Interativa do Keyboard-Indisponível", - "sshAuthenticationFailed": "Falha na autenticação", - "sshAuthenticationTimeout": "Timeout de autenticação", - "sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação interativa de teclado. Por favor, forneça sua senha ou chave SSH.", - "sshAuthFailedDescription": "As credenciais fornecidas estavam incorretas. Por favor, tente novamente com credenciais válidas.", - "sshTimeoutDescription": "A tentativa de autenticação expirou. Tente novamente.", - "sshProvideCredentialsDescription": "Por favor, forneça suas credenciais SSH para conectar a este servidor.", - "sshPasswordDescription": "Digite a senha para esta conexão SSH.", - "sshKeyPasswordDescription": "Se a sua chave SSH estiver criptografada, digite a senha aqui.", - "passphraseRequired": "Senha Obrigatória", - "passphraseRequiredDescription": "A chave SSH está criptografada. Digite a senha para desbloqueá-la.", - "back": "Anterior", - "firstUser": "Primeiro usuário", - "firstUserMessage": "Você é o primeiro usuário e será feito um administrador. Você pode ver as configurações de administrador na lista de usuários da barra lateral. Se você acha que isso é um erro, verifique os logs do docker ou crie uma questão no GitHub.", - "external": "externo", - "loginWithExternal": "Entrar com o provedor externo", - "loginWithExternalDesc": "Fazer login usando seu provedor de identidade externo configurado", - "externalNotSupportedInElectron": "A autenticação externa ainda não é suportada no aplicativo Electron. Por favor, use a versão web para logar OIDC.", - "resetPasswordButton": "Redefinir a senha", - "sendResetCode": "Enviar Código de Redefinição", - "resetCodeDesc": "Digite seu nome de usuário para receber um código de redefinição de senha. O código será logado nos logs do contêiner docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verificar Código", - "enterResetCode": "Insira o código de 6 dígitos do contêiner docker para o usuário:", - "newPassword": "Nova Palavra-Passe", - "confirmNewPassword": "Confirmar senha", - "enterNewPassword": "Digite sua nova senha para o usuário:", - "signUp": "Criar conta", - "desktopApp": "Aplicativo para computador", - "loggingInToDesktopApp": "Fazendo login no aplicativo para computador", - "loadingServer": "Carregando servidor...", - "dataLossWarning": "Redefinir sua senha desta forma irá apagar todos os seus hosts, credenciais e outros dados criptografados salvos por SSH. Essa ação não pode ser desfeita. Apenas use isso se você esqueceu sua senha e não está logado.", - "authenticationDisabled": "Autenticação desabilitada", - "authenticationDisabledDesc": "Todos os métodos de autenticação estão desativados no momento. Entre em contato com o administrador.", - "attemptsRemaining": "Tentativas {{count}} restantes" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verificar chave do host SSH", - "keyChangedWarning": "Chave do host SSH alterada", - "firstConnectionTitle": "Primeira vez conectando-se a este host", - "firstConnectionDescription": "A autenticidade deste host não pode ser estabelecida. Verifique a impressão digital que você espera.", - "keyChangedDescription": "A chave do host para este servidor mudou desde a sua última conexão. Isso pode indicar um problema de segurança.", - "previousKey": "Chave anterior", - "newFingerprint": "Nova impressão digital", - "fingerprint": "Impressão digital", - "verifyInstructions": "Caso você confie neste host, clique em Aceitar para continuar e salvar esta impressão digital para conexões futuras.", - "securityWarning": "Aviso de segurança", - "acceptAndContinue": "Aceitar e Continuar", - "acceptNewKey": "Aceitar nova chave e continuar" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Não foi possível conectar ao banco de dados", - "unknownError": "Erro desconhecido", - "loginFailed": "Falha no login", - "failedPasswordReset": "Falha ao iniciar a redefinição de senha", - "failedVerifyCode": "Falha ao verificar código de redefinição", - "failedCompleteReset": "Falha ao concluir a redefinição de senha", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Falha ao iniciar o login OIDC", - "silentSigninOidcUnavailable": "O login silencioso foi solicitado, mas o login do OIDC não está disponível.", - "failedUserInfo": "Falha ao obter informações do usuário após o login", - "oidcAuthFailed": "Falha na autenticação OIDC", - "invalidAuthUrl": "URL de autorização inválida recebida do backend", - "requiredField": "Este campo é obrigatório", - "minLength": "Tamanho mínimo de {{min}}", - "passwordMismatch": "As senhas não coincidem", - "passwordLoginDisabled": "Nome de usuário/senha está desativado no momento", - "sessionExpired": "Sessão expirou - por favor faça o login novamente", - "totpRateLimited": "Taxa limitada: Muitas tentativas de verificação TOTP. Tente novamente mais tarde.", - "totpRateLimitedWithTime": "Taxa limitada: Muitas tentativas de verificação TOTP. Aguarde {{time}} segundos antes de tentar novamente.", - "resetCodeRateLimited": "Taxa limitada: Muitas tentativas de verificação. Por favor, tente novamente mais tarde.", - "resetCodeRateLimitedWithTime": "Taxa limitada: Muitas tentativas de verificação. Por favor, aguarde {{time}} segundos antes de tentar novamente.", - "authTokenSaveFailed": "Falha ao salvar token de autenticação", - "failedToLoadServer": "Falha ao carregar servidor" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "O registro da nova conta está desativado por um administrador. Por favor, entre em contato com um administrador.", - "userNotAllowed": "Sua conta não está autorizada a se registrar. Por favor, contate um administrador.", - "databaseConnectionFailed": "Falha ao conectar ao servidor do banco de dados", - "resetCodeSent": "Redefinir código enviado para os logs Docker", - "codeVerified": "Código verificado com sucesso", - "passwordResetSuccess": "Senha redefinida com sucesso", - "loginSuccess": "Login bem-sucedido", - "registrationSuccess": "Registrado com sucesso" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Túneis locais de desktop, direcionando hosts SSH configurados.", - "c2sTunnelPresets": "Predefinições do Túnel Cliente", - "c2sTunnelPresetsDesc": "Salvar a lista de túneis locais do cliente local como uma predefinição de servidor nomeada ou carregar uma predefinição de volta para este cliente.", - "c2sTunnelPresetsUnavailable": "Predefinições de túnel cliente só estão disponíveis no cliente desktop.", - "c2sPresetName": "Nome da predefinição", - "c2sPresetNamePlaceholder": "Nome da predefinição cliente", - "c2sPresetToLoad": "Predefinição para carregar", - "c2sNoPresetSelected": "Nenhuma predefinição selecionada", - "c2sNoPresets": "Nenhuma predefinição salva", - "c2sLoadPreset": "Carregar", - "c2sCurrentLocalConfig": "{{count}} túneis locais do cliente configurados nesta área de trabalho.", - "c2sPresetSyncNote": "As predefinições são imagens explícitas; carregar uma substitui a lista de túneis de cliente local desse cliente.", - "c2sPresetSaved": "Túnel de cliente predefinido salvo", - "c2sPresetLoaded": "Túnel cliente predefinido carregado localmente", - "c2sPresetRenamed": "Pré-ajuste do túnel cliente renomeado", - "c2sPresetDeleted": "Prédefinição de túnel de cliente excluído", - "c2sPresetLoadError": "Falha ao carregar as predefinições do túnel do cliente" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "IDIOMA", - "keyPassword": "Senha da chave", - "pastePrivateKey": "Cole sua chave privada aqui...", - "localListenerHost": "127.0.0.1 (ouvir localmente)", - "localTargetHost": "127.0.0.1 (alvo neste computador)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Digite sua senha", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Painel", - "loading": "Carregando painel...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "SUPORTE", + "support": "Support", "discord": "Discord", - "serverOverview": "Visão Geral do Servidor", - "version": "Versão", - "upToDate": "Até a data", - "updateAvailable": "Atualização disponível", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Tempo em atividade", - "database": "Banco", - "healthy": "Saudável", - "error": "ERRO", - "totalHosts": "Hosts totais", - "totalTunnels": "Total de túneis", - "totalCredentials": "Credenciais totais", - "recentActivity": "Atividade recente", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Carregando atividade recente...", - "noRecentActivity": "Nenhuma atividade recente", - "quickActions": "Ações rápidas", - "addHost": "Adicionar Host", - "addCredential": "Adicionar Credencial", - "adminSettings": "Configurações de administrador", - "userProfile": "Informações do Perfil", - "serverStats": "Estatísticas do servidor", - "loadingServerStats": "Carregando estatísticas do servidor...", - "noServerData": "Nenhum dado de servidor disponível", - "cpu": "cpu", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizar Painel", - "dashboardSettings": "Configurações do painel", - "enableDisableCards": "Ativar/Desativar cartões", - "resetLayout": "Restaurar ao Padrão", - "serverOverviewCard": "Visão Geral do Servidor", - "recentActivityCard": "Atividade recente", - "networkGraphCard": "Gráfico da Rede", - "networkGraph": "Gráfico da Rede", - "quickActionsCard": "Ações rápidas", - "serverStatsCard": "Estatísticas do servidor", - "panelMain": "Principal", - "panelSide": "Lado", - "justNow": "neste momento" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ESTÁVEL", - "hostsOnline": "Hosts online", - "activeTunnels": "Túneis ativos", - "registerNewServer": "Registrar um novo servidor", - "storeSshKeysOrPasswords": "Armazenar chaves SSH ou senhas", - "manageUsersAndRoles": "Gerenciar usuários e funções", - "manageYourAccount": "Gerencie sua conta", - "hostStatus": "Status do Host", - "noHostsConfigured": "Nenhum host configurado", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", - "offline": "DESLIGAR", - "onlineLower": "Disponível", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Adicionar:", - "commandPalette": "Paleta de comando", - "done": "Concluído", - "editModeInstructions": "Arraste cartões para reordenar · Arraste o divisor de coluna para redimensionar colunas · Arraste a borda inferior de um cartão para redimensionar sua altura · Lixeira para remover", - "empty": "Vazio", - "clear": "Limpar" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Adicionar Host", - "addGroup": "Adicionar Grupo", - "addLink": "Adicionar Link", - "zoomIn": "Aumentar zoom", - "zoomOut": "Diminuir o zoom", - "resetView": "Redefinir visualização", - "selectHost": "Selecione o Host", - "chooseHost": "Escolha um host...", - "parentGroup": "Grupo Pai", - "noGroup": "Nenhum grupo", - "groupName": "Nome do Grupo", - "color": "Cor", - "source": "fonte", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Mover para o grupo", - "selectGroup": "Selecionar grupo...", - "addConnection": "Adicionar conexão", - "hostDetails": "Detalhes do Host", - "removeFromGroup": "Remover do grupo", - "addHostHere": "Adicionar Host Aqui", - "editGroup": "Editar Grupo", - "delete": "excluir", - "add": "Adicionar", - "create": "Crio", - "move": "Mover-se", - "connect": "Conectar", - "createGroup": "Criar grupo", - "selectSourcePlaceholder": "Selecione a Fonte...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Arquivo inválido", - "hostAlreadyExists": "O host já está na topologia", - "connectionExists": "Conexão já existe", - "unknown": "Desconhecido", - "name": "Nome:", - "ip": "PI", - "status": "SItuação", - "failedToAddNode": "Falha ao adicionar nó", - "sourceDifferentFromTarget": "Origem e alvo devem ser diferentes", - "exportJSON": "Exportar JSON", - "importJSON": "Importar JSON", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Gerenciador de Arquivos", - "tunnel": "Túnel", - "docker": "Atracador", - "serverStats": "Estatísticas do servidor", - "noNodes": "Nenhum nó ainda" + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "O Docker não está habilitado para este host", - "validating": "Validando o Docker...", - "connecting": "Conectandochar@@0", - "error": "ERRO", - "version": "{{version}} Docker", - "connectionFailed": "Falha ao conectar ao Docker", - "containerStarted": "Contêiner {{name}} iniciado", - "failedToStartContainer": "Falha ao iniciar o contêiner {{name}}", - "containerStopped": "O contêiner {{name}} parou", - "failedToStopContainer": "Falha ao parar o contêiner {{name}}", - "containerRestarted": "Contêiner {{name}} reiniciado", - "failedToRestartContainer": "Falha ao reiniciar o contêiner {{name}}", - "containerPaused": "Recipiente {{name}} pausado", - "containerUnpaused": "Recipiente {{name}} despausado", - "failedToTogglePauseContainer": "Falha ao alternar estado de pausa para o contêiner {{name}}", - "containerRemoved": "{{name}} do contêiner removido", - "failedToRemoveContainer": "Falha ao remover contêiner {{name}}", - "image": "Imagem:", - "ports": "Portas", - "noPorts": "Nenhuma porta", - "start": "Iniciar", - "confirmRemoveContainer": "Você tem certeza que deseja remover o contêiner '{{name}}'? Esta ação não pode ser desfeita.", - "runningContainerWarning": "Aviso: Este contêiner está em execução no momento. Removê-lo irá parar o contêiner primeiro.", - "loadingContainers": "Carregando contêineres...", - "manager": "Gerenciador de Docker", - "autoRefresh": "Atualização Automática", - "timestamps": "Data", - "lines": "linhas", - "filterLogs": "Filtrar registros...", - "refresh": "atualizar", - "download": "BAIXAR", - "clear": "Limpar", - "logsDownloaded": "Registros baixados com sucesso", - "last50": "Últimos 50", - "last100": "Últimas 100", - "last500": "Últimas 500", - "last1000": "Últimas 1000", - "allLogs": "Todos os registros", - "noLogsMatching": "Nenhum registro correspondente \"{{query}}\"", - "noLogsAvailable": "Não há registros disponíveis", - "noContainersFound": "Nenhum contêiner encontrado", - "noContainersFoundHint": "Nenhum contêiner Docker está disponível neste host", - "searchPlaceholder": "Procurar contêineres...", - "allStatuses": "Todos os Status", - "stateRunning": "Executando", - "statePaused": "Pausado", - "stateExited": "Saída", - "stateRestarting": "Reiniciando", - "noContainersMatchFilters": "Nenhum contêiner corresponde aos seus filtros", - "noContainersMatchFiltersHint": "Tente ajustar sua pesquisa ou critério de filtro", - "failedToFetchStats": "Falha ao obter estatísticas do contêiner", - "containerNotRunning": "Contêiner não executando", - "startContainerToViewStats": "Inicie o contêiner para ver estatísticas", - "loadingStats": "Carregando estatísticas...", - "errorLoadingStats": "Erro ao carregar estatísticas", - "noStatsAvailable": "Não há estatísticas disponíveis", - "cpuUsage": "Uso da CPU", - "current": "Atual", - "memoryUsage": "Memória Utilizada", - "networkIo": "I/O de rede", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Saída", - "blockIo": "Bloco I/O", - "read": "Lido", - "write": "Salvar", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "Informação do contêiner", - "name": "Nome:", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Estado:", - "containerMustBeRunning": "O contêiner deve estar em execução para acessar o console", - "verificationCodePrompt": "Inserir código de verificação", - "totpVerificationFailed": "Falha na verificação TOTP. Tente novamente.", - "warpgateVerificationFailed": "A autenticação do Warpgate falhou. Por favor, tente novamente.", - "connectedTo": "Conectado a {{containerName}}", - "disconnected": "Desconectado", - "consoleError": "Erro de console", - "errorMessage": "Erro: {{message}}", - "failedToConnect": "Falha ao conectar ao contêiner", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", "console": "Console", - "selectShell": "Selecionar shell", - "bash": "Pancada", - "sh": "estrito", - "ash": "cinzas", - "connect": "Conectar", - "disconnect": "Desconectar", - "notConnected": "Não conectado", - "clickToConnect": "Clique em conectar para iniciar uma sessão shell", - "connectingTo": "Conectando a {{containerName}}...", - "containerNotFound": "Contêiner não encontrado", - "backToList": "Voltar para a Lista", - "logs": "Registros", - "stats": "Estatísticas", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", "consoleTab": "Console", - "startContainerToAccess": "Iniciar o contêiner para acessar o console" + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Gerais", - "sectionOidc": "OCIDADE", - "sectionUsers": "Utilizadores", - "sectionSessions": "Sessões", - "sectionRoles": "Papéis", - "sectionDatabase": "Banco", - "sectionApiKeys": "Chaves API", - "allowRegistration": "Permitir Registro de Usuário", - "allowRegistrationDesc": "Deixe novos usuários se registrarem", - "allowPasswordLogin": "Permitir acesso à senha", - "allowPasswordLoginDesc": "Usuário/senha para login", - "oidcAutoProvision": "Auto-Provisão OIDC", - "oidcAutoProvisionDesc": "Criar contas automaticamente para usuários OIDC, mesmo que o registro esteja desativado", - "allowPasswordReset": "Permitir redefinição de senha", - "allowPasswordResetDesc": "Redefinir código via logs do Docker", - "sessionTimeout": "Sessão expirada", - "hours": "horas", - "sessionTimeoutRange": "Mínimo de 1h · Máximo de 720h", - "monitoringDefaults": "Padrões de monitoramento", - "statusCheck": "Verificação de status", - "metrics": "Métricas", - "sec": "seg.", - "logLevel": "Nível do Registro", - "enableGuacamole": "Ativar Guacamole", - "enableGuacamoleDesc": "desktop remoto RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Configure o OpenID Connect para SSO. Campos marcados * são necessários.", - "oidcClientId": "ID do Cliente", - "oidcClientSecret": "Segredo do Cliente", - "oidcAuthUrl": "URL de autorização", - "oidcIssuerUrl": "URL do Emissor", - "oidcTokenUrl": "URL do token", - "oidcUserIdentifier": "Caminho do usuário", - "oidcDisplayName": "Exibir Caminho do Nome", - "oidcScopes": "Âmbitos", - "oidcUserinfoUrl": "Substituir URL do Userinfo", - "oidcAllowedUsers": "Usuários permitidos", - "oidcAllowedUsersDesc": "Um e-mail por linha. Deixe em branco para permitir que todos.", - "removeOidc": "Excluir", - "usersCount": "Usuários {{count}}", - "createUser": "Crio", - "newRole": "Nova Permissão", - "roleName": "Nome:", - "roleDisplayName": "Nome para exibição", - "roleDescription": "Descrição:", - "rolesCount": "Funções {{count}}", - "createRole": "Crio", - "creating": "Criando...", - "exportDatabase": "Exportar banco de dados", - "exportDatabaseDesc": "Baixar um backup de todos os hosts, credenciais e configurações", - "export": "Exportação", - "exporting": "Exportando...", - "importDatabase": "Importar base de dados", - "importDatabaseDesc": "Restaurar a partir de um arquivo de backup .sqlite", - "importDatabaseSelected": "Selecionado: {{name}}", - "selectFile": "Selecione o arquivo", - "changeFile": "Troca", - "import": "Importação", - "importing": "Importando...", - "apiKeysCount": "Chaves {{count}}", - "newApiKey": "Nova chave de API", - "apiKeyCreatedWarning": "Chave criada - copiá-la agora, ela não será exibida novamente.", - "apiKeyName": "Nome:", - "apiKeyUser": "Usuário", - "apiKeySelectUser": "Selecione um usuário...", - "apiKeyExpiresAt": "Expira em", - "createKey": "Criar chave", - "apiKeyNoExpiry": "Sem expiração", + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Dupla Autenticação", - "authTypeOidc": "OCIDADE", - "authTypeLocal": "Localização", - "adminStatusAdministrator": "Administrador", - "adminStatusRegularUser": "Usuário Normal", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "SIM", - "customBadge": "PERSÃO", - "youBadge": "VOCÊ", - "sessionsActive": "{{count}} ativo", - "sessionActive": "Ativo: {{time}}", - "sessionExpires": "Exx: {{time}}", - "revokeAll": "TODOS", - "revokeAllSessionsSuccess": "Todas as sessões do usuário revogadas", - "revokeAllSessionsFailed": "Falha ao revogar sessões", - "revokeSessionFailed": "Falha ao revogar sessão", - "addRole": "Adicionar função", - "noCustomRoles": "Nenhum cargo personalizado definido", - "removeRoleFailed": "Falha ao remover papel", - "assignRoleFailed": "Falha ao atribuir papel", - "deleteRoleFailed": "Falha ao excluir papel", - "userAdminAccess": "Administrador", - "userAdminAccessDesc": "Acesso completo a todas as configurações de administrador", - "userRoles": "Papéis", - "revokeAllUserSessions": "Revogar todas as sessões", - "revokeAllUserSessionsDesc": "Forçar o re-login em todos os dispositivos", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "A exclusão deste usuário é permanente.", - "deleteUser": "Excluir {{username}}", - "deleting": "Excluindo...", - "deleteUserFailed": "Falha ao excluir usuário", - "deleteUserSuccess": "Usuário \"{{username}}\" excluído", - "deleteRoleSuccess": "Função \"{{name}}\" excluído", - "revokeKeySuccess": "Chave \"{{name}}\" revogada", - "revokeKeyFailed": "Falha ao revogar a chave", - "copiedToClipboard": "Copiado para o clipboard", - "done": "Concluído", - "createUserTitle": "Criar Usuário", - "createUserDesc": "Crie uma nova conta local.", - "createUserUsername": "Usuário:", - "createUserPassword": "Palavra-passe", - "createUserPasswordHint": "Mínimo 6 caracteres.", - "createUserEnterUsername": "Digite o usuário", - "createUserEnterPassword": "Insira a senha", - "createUserSubmit": "Criar Usuário", - "editUserTitle": "Gerenciar Usuário: {{username}}", - "editUserDesc": "Editar funções, status de administrador, sessões e configurações da conta.", - "editUserUsername": "Usuário:", - "editUserAuthType": "Tipo de Autenticação", - "editUserAdminStatus": "Status do administrador", - "editUserUserId": "ID de usuário", - "linkAccountTitle": "Vincular OIDC à conta da senha", - "linkAccountDesc": "Mesclar a conta OIDC {{username}} com uma conta local existente.", - "linkAccountWarningTitle": "Isto irá:", - "linkAccountEffect1": "Excluir conta somente OIDC", - "linkAccountEffect2": "Adicione login OIDC à conta de destino", - "linkAccountEffect3": "Permitir tanto acesso OIDC quanto acesso à senha", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Digite o nome de usuário da conta local para vincular", - "linkAccounts": "Vincular Contas", - "linkAccountSuccess": "Conta OIDC ligada a \"{{username}}\"", - "linkAccountFailed": "Falha ao ligar a conta OIDC", - "linkAccountInProgress": "Ligando...", - "saving": "Salvando...", - "updateRegistrationFailed": "Falha ao atualizar configuração de registro", - "updatePasswordLoginFailed": "Falha ao atualizar configuração de login de senha", - "updateOidcAutoProvisionFailed": "Falha ao atualizar a auto disposição OIDC", - "updatePasswordResetFailed": "Falha ao atualizar a configuração de redefinição de senha", - "sessionTimeoutRange2": "Tempo limite da sessão deve ser entre 1 e 720 horas", - "sessionTimeoutSaved": "Timeout da sessão salvo", - "sessionTimeoutSaveFailed": "Falha ao salvar tempo limite de sessão", - "monitoringIntervalInvalid": "Valores de intervalo inválidos", - "monitoringSaved": "Configurações de monitoramento salvas", - "monitoringSaveFailed": "Falha ao salvar configurações de monitoramento", - "guacamoleSaved": "Configurações de Guacamole salvas", - "guacamoleSaveFailed": "Falha ao salvar configurações de Guacamole", - "guacamoleUpdateFailed": "Falha ao atualizar a configuração de Guacamole", - "logLevelUpdateFailed": "Falha ao atualizar o nível de log", - "oidcSaved": "Configuração OIDC salva", - "oidcSaveFailed": "Falha ao salvar configuração OIDC", - "oidcRemoved": "Configuração OIDC removida", - "oidcRemoveFailed": "Falha ao remover a configuração OIDC", - "createUserRequired": "Nome de usuário e senha são obrigatórios", - "createUserPasswordTooShort": "A senha deve ter pelo menos 6 caracteres", - "createUserSuccess": "Usuário \"{{username}}\" criado", - "createUserFailed": "Falha ao criar usuário", - "updateAdminStatusFailed": "Falha ao atualizar status do administrador", - "allSessionsRevoked": "Todas as sessões revogadas", - "revokeSessionsFailed": "Falha ao revogar sessões", - "createRoleRequired": "Nome e nome de exibição são necessários", - "createRoleSuccess": "Papel \"{{name}}\" criado", - "createRoleFailed": "Falha ao criar papel", - "apiKeyNameRequired": "O nome da chave é obrigatório", - "apiKeyUserRequired": "O ID do usuário é necessário", - "apiKeyCreatedSuccess": "Chave da API \"{{name}}\" criado", - "apiKeyCreateFailed": "Falha ao criar chave de API", - "exportSuccess": "Banco de dados exportado com sucesso", - "exportFailed": "Exportação do banco de dados falhou", - "importSelectFile": "Por favor, selecione um arquivo primeiro", - "importCompleted": "Importação concluída: {{total}} artigos importados, {{skipped}} ignorados", - "importFailed": "Importação falhou: {{error}}", - "importError": "Falha ao importar banco" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Servidor", - "hostPlaceholder": "192.168.1.1 ou exemplo.com", - "portLabel": "Porta", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Usuário:", - "usernamePlaceholder": "usuário", - "authLabel": "Autenticação", - "passwordLabel": "Palavra-passe", - "passwordPlaceholder": "Senha", - "privateKeyLabel": "Chave Privada", - "privateKeyPlaceholder": "Colar chave privada...", - "credentialLabel": "Credencial", - "credentialPlaceholder": "Selecione uma credencial salva", - "connectToTerminal": "Conectar ao Terminal", - "connectToFiles": "Conectar aos arquivos" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Nenhum terminal selecionado", - "noTerminalSelectedHint": "Abra uma aba de terminal SSH para ver seu histórico de comandos", - "searchPlaceholder": "Pesquisar histórico...", - "clearAll": "Limpar Tudo", - "noHistoryEntries": "Não há entradas no histórico", - "trackingDisabled": "Rastreamento do histórico está desativado", - "trackingDisabledHint": "Ative-o nas configurações do seu perfil para gravar comandos." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Gravação de Teclas", - "recordToTerminals": "Registro em terminais", - "selectAll": "TODOS", - "selectNone": "Nenhuma", - "noTerminalTabsOpen": "Nenhuma aba de terminal aberta", - "selectTerminalsAbove": "Selecione os terminais acima", - "broadcastInputPlaceholder": "Digite aqui para transmitir keystroks...", - "stopRecording": "Parar Gravação", - "startRecording": "Iniciar Gravação", - "settingsTitle": "Confirgurações", - "enableRightClickCopyPaste": "Habilitar copiar/colar com botão direito" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Disposição", - "selectLayoutAbove": "Selecione um layout acima", - "selectLayoutHint": "Escolha quantos painéis mostrar", - "panesTitle": "Painéis", - "openTabsTitle": "Abas abertas", - "dragTabsHint": "Arraste os separadores para os painéis acima ou utilize a Atribuição Rápida.", - "dropHere": "Solte aqui", - "emptyPane": "Vazio", - "dashboard": "Painel", - "clearSplitScreen": "Limpar Tela dividida", - "quickAssign": "Atribuição rápida", - "alreadyAssigned": "Painel {{index}}", - "splitTab": "Aba dividida", - "addToSplit": "Adicionar à divisão", - "removeFromSplit": "Remover da divisão", - "assignToPane": "Atribuir ao painel" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Criar Snippet", - "createSnippetDescription": "Criar um novo snippet de comando para a execução rápida", - "nameLabel": "Nome:", - "namePlaceholder": "ex.: Reiniciar o Nginx", - "descriptionLabel": "Descrição:", - "descriptionPlaceholder": "Descrição opcional", - "optional": "Opcional", - "folderLabel": "pasta", - "noFolder": "Nenhuma pasta (Descategorizada)", - "commandLabel": "Comando", - "commandPlaceholder": "ex.: sudo systemctl reinicializa o nginx", - "cancel": "cancelar", - "createSnippetButton": "Criar Snippet", - "createFolderTitle": "Criar pasta", - "createFolderDescription": "Organize seus snippets em pastas", - "folderNameLabel": "Nome da pasta", - "folderNamePlaceholder": "ex.: Comandos do Sistema, Scripts Docker", - "folderColorLabel": "Cor da pasta", - "folderIconLabel": "Ícone da pasta", - "previewLabel": "Pré-visualizar", - "folderNameFallback": "Nome da pasta", - "createFolderButton": "Criar pasta", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "TODOS", - "selectNone": "Nenhuma", - "noTerminalTabsOpen": "Nenhuma aba de terminal aberta", - "searchPlaceholder": "Buscar snippets...", - "newSnippet": "Novo Trecho", - "newFolder": "Adicionar uma pasta", - "run": "Executar", - "noSnippetsInFolder": "Não há snippets nesta pasta", - "uncategorized": "Descategorizado", - "editSnippetTitle": "Editar Trecho", - "editSnippetDescription": "Atualizar este comando snippet", - "saveSnippetButton": "Salvar as alterações", - "createSuccess": "Snippet criado com sucesso", - "createFailed": "Falha ao criar snippet", - "updateSuccess": "Trecho atualizado com sucesso", - "updateFailed": "Falha ao atualizar snippet", - "deleteFailed": "Falha ao excluir snippet", - "folderCreateSuccess": "Pasta criada com sucesso", - "folderCreateFailed": "Falha ao criar pasta", - "editFolderTitle": "Editar Pasta", - "editFolderDescription": "Renomear ou alterar a aparência desta pasta", - "saveFolderButton": "Salvar as alterações", - "editFolder": "Editar Pasta", - "deleteFolder": "Excluir pasta", - "folderDeleteSuccess": "Pasta \"{{name}}excluída", - "folderDeleteFailed": "Falha ao excluir pasta", - "folderEditSuccess": "Pasta atualizada com sucesso", - "folderEditFailed": "Falha ao atualizar a pasta", - "confirmRunMessage": "Executar \"{{name}}\"?", - "confirmRunButton": "Correr", - "runSuccess": "Executado \"{{name}}\" em {{count}} terminal(is)", - "copySuccess": "Copiado \"{{name}}\" para área de transferência", - "shareTitle": "Compartilhar Snippet", - "shareUser": "Usuário", - "shareRole": "Funções", - "selectUser": "Selecione um usuário...", - "selectRole": "Selecione uma função...", - "shareSuccess": "Snippet compartilhado com sucesso", - "shareFailed": "Falha ao compartilhar snippet", - "revokeSuccess": "Acesso revogado", - "revokeFailed": "Falha ao revogar acesso", - "currentAccess": "Acesso atual", - "shareLoadError": "Não foi possível carregar os dados de compartilhamento", - "loading": "Carregandochar@@0", - "close": "FECHAR", - "reorderFailed": "Falha ao guardar a ordem do trecho de código" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "conta", - "sectionAppearance": "Aparência", - "sectionSecurity": "Segurança", - "sectionApiKeys": "Chaves API", - "sectionC2sTunnels": "Túneis C2S", - "usernameLabel": "Usuário:", - "roleLabel": "Funções", - "roleAdministrator": "Administrador", - "authMethodLabel": "Método de Autenticação", - "authMethodLocal": "Localização", - "twoFaLabel": "A2F", - "twoFaOn": "Ligado", - "twoFaOff": "Desligado", - "versionLabel": "Versão", - "deleteAccount": "Excluir Conta", - "deleteAccountDescription": "Excluir permanentemente sua conta", - "deleteButton": "excluir", - "deleteAccountPermanent": "Esta ação é permanente e não pode ser desfeita.", - "deleteAccountWarning": "Todas as sessões, hosts, credenciais e configurações serão excluídas permanentemente.", - "confirmPasswordDeletePlaceholder": "Digite sua senha para confirmar", - "languageLabel": "IDIOMA", - "themeLabel": "Tema", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Cor do acento", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Auto-completar comando", - "commandAutocompleteDesc": "Mostrar autocomplete durante a digitação", - "historyTracking": "Rastreamento do Histórico", - "historyTrackingDesc": "Rastrear comandos do terminal", - "syntaxHighlighting": "Realce de sintaxe", - "syntaxHighlightingDesc": "Destacar saída do terminal", - "commandPalette": "Paleta de comando", - "commandPaletteDesc": "Ativar atalho de teclado", - "reopenTabsOnLogin": "Reabrir separadores ao fazer login", - "reopenTabsOnLoginDesc": "Restaure as suas abas abertas ao iniciar sessão ou atualizar a página, mesmo noutro dispositivo.", - "confirmTabClose": "Confirmar Fechar Aba", - "confirmTabCloseDesc": "Perguntar antes de fechar os guias dos terminais", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Exibir Tags do Host", - "showHostTagsDesc": "Exibir tags na lista de endereços", - "hostTrayOnClick": "Clique para expandir as ações do anfitrião", - "hostTrayOnClickDesc": "Mostrar sempre os botões de ligação; clicar para expandir as opções de gestão em vez de passar o cursor sobre as mesmas.", - "pinAppRail": "Aplicação Pin Rail", - "pinAppRailDesc": "Mantenha a barra lateral esquerda da aplicação sempre expandida, em vez de expandir ao passar o cursor sobre a mesma.", - "settingsSnippets": "Trechos", - "foldersCollapsed": "Pastas colapsadas", - "foldersCollapsedDesc": "Recolher pastas por padrão", - "confirmExecution": "Confirmar execução", - "confirmExecutionDesc": "Confirme antes de executar trechos", - "settingsUpdates": "Atualizações", - "disableUpdateChecks": "Desativar Verificações de Atualizações", - "disableUpdateChecksDesc": "Interromper a verificação de atualizações", - "totpAuthenticator": "Autenticador TOTP", - "totpEnabled": "2FA está habilitado", - "totpDisabled": "Adicionar segurança extra ao login", - "disable": "Desligado", - "enable": "Habilitado", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Escaneie o código QR ou insira o segredo no seu aplicativo de autenticação, e digite o código de 6 dígitos", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verificar", - "changePassword": "Mudar a senha", - "currentPasswordLabel": "Palavra-passe Atual", - "currentPasswordPlaceholder": "Senha atual", - "newPasswordLabel": "Nova Palavra-Passe", - "newPasswordPlaceholder": "Nova senha", - "confirmPasswordLabel": "Confirme a Nova Senha", - "confirmPasswordPlaceholder": "Confirme a nova senha", - "updatePassword": "Atualizar Senha", - "createApiKeyTitle": "Criar chave de API", - "createApiKeyDescription": "Gere uma nova chave de API para acesso programático.", - "apiKeyNameLabel": "Nome:", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "opcional", - "cancel": "cancelar", - "createKey": "Criar chave", - "apiKeyCount": "Chaves {{count}}", - "newKey": "Nova Chave", - "noApiKeys": "Ainda não há chaves de API.", - "apiKeyActive": "ativo", - "apiKeyUsageHint": "Inclua sua chave no", - "apiKeyUsageHintHeader": "cabeçalho.", - "apiKeyPermissionsHint": "Chaves herdam as permissões do usuário criador.", - "roleUser": "Usuário", - "authMethodDual": "Dupla Autenticação", - "authMethodOidc": "OCIDADE", - "totpSetupFailed": "Falha ao iniciar a configuração TOTP", - "totpEnter6Digits": "Insira um código de 6 dígitos", - "totpEnabledSuccess": "Autenticação de dois fatores ativada", - "totpInvalidCode": "Código inválido, por favor tente novamente", - "totpDisableInputRequired": "Digite seu código TOTP ou senha", - "totpDisabledSuccess": "Autenticação de dois fatores desativada", - "totpDisableFailed": "Falha ao desativar 2FA", - "totpDisableTitle": "Desativar 2FA", - "totpDisablePlaceholder": "Insira o código ou senha TOTP", - "totpDisableConfirm": "Desativar 2FA", - "totpContinueVerify": "Continuar a verificar", - "totpVerifyTitle": "Verificar Código", - "totpBackupTitle": "Códigos de recuperação", - "totpDownloadBackup": "Baixar códigos de recuperação", - "done": "Concluído", - "secretCopied": "Segredo copiado para área de transferência", - "apiKeyNameRequired": "O nome da chave é obrigatório", - "apiKeyCreated": "Chave da API \"{{name}}\" criado", - "apiKeyCreateFailed": "Falha ao criar chave de API", - "apiKeyUser": "Usuário", - "apiKeyExpires": "Expira", - "apiKeyRevoked": "Chave de API \"{{name}}\" revogada", - "apiKeyRevokeFailed": "Falha ao revogar chave de API", - "passwordFieldsRequired": "Senhas atuais e novas são necessárias", - "passwordMismatch": "As senhas não coincidem", - "passwordTooShort": "A senha deve ter pelo menos 6 caracteres", - "passwordUpdated": "Senha atualizada com sucesso", - "passwordUpdateFailed": "Falha ao atualizar a senha", - "deletePasswordRequired": "É necessário uma senha para excluir sua conta", - "deleteFailed": "Falha ao excluir conta", - "deleting": "Excluindo...", - "colorPickerTooltip": "Abrir seletor de cores", - "themeSystem": "SISTEMA", - "themeLight": "Fino", - "themeDark": "Escuro", - "themeDracula": "Drácula", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Ensolarado", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Um Escuro", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ro_RO.json b/src/ui/locales/translated/ro_RO.json index f996b419..9bc5af32 100644 --- a/src/ui/locales/translated/ro_RO.json +++ b/src/ui/locales/translated/ro_RO.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Dosare", - "folder": "Dosar", - "password": "Parolă", - "key": "Cheie", - "sshPrivateKey": "Cheie Privată SSH", - "upload": "Incarca", - "keyPassword": "Parolă cheie", - "sshKey": "Cheie SSH", - "uploadPrivateKeyFile": "Încărcați un fișier cu cheie privată", - "searchCredentials": "Căutare acreditări...", - "addCredential": "Adaugă acreditare", - "caCertificate": "Certificat CA (-cert.pub)", - "caCertificateDescription": "Opţional: Încărcaţi sau lipiţi fişierul de certificat semnat CA (de ex. id_ed25519-cert.pub). Necesar atunci când serverul SSH utilizează o autorizaţie bazată pe certificat.", - "uploadCertFile": "Încărcați fișierul -cert.pub", - "clearCert": "Curăță", - "certLoaded": "Certificat încărcat", - "certPublicKeyLabel": "Certificat CA", - "certTypeLabel": "Tipul certificatului", - "pasteOrUploadCert": "Inserați sau încărcați un certificat -cert.pub...", - "hasCaCert": "Are certificat CA", - "noCaCert": "Fără certificat CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Încărcarea alertelor a eșuat", - "failedToDismissAlert": "Eroare la respingerea alertei" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Configurare server", - "description": "Configuraţi URL-ul serverului de Termix pentru a vă conecta la serviciile de backend", - "serverUrl": "URL Server", - "enterServerUrl": "Te rog introdu un URL de server", - "saveFailed": "Salvarea configurației a eșuat", - "saveError": "Eroare la salvarea configurației", - "saving": "Salvare...", - "saveConfig": "Salvați configurația", - "helpText": "Introduceți adresa URL unde funcționează serverul dvs. Termix (de exemplu, http://localhost:30001 sau https://your-server.com)", - "changeServer": "Schimbă Server", - "mustIncludeProtocol": "URL-ul serverului trebuie să înceapă cu http:// sau http://", - "allowInvalidCertificate": "Permiteți certificatul nevalid", - "allowInvalidCertificateDesc": "A se utiliza numai pentru servere auto-găzduite de încredere, cu certificate autosemnate sau cu adresă IP.", - "useEmbedded": "Utilizare server local", - "embeddedDesc": "Rulează Termix cu serverul local încorporat (nu este nevoie de niciun server de la distanță)", - "embeddedConnecting": "Conectare la serverul local...", - "embeddedNotReady": "Serverul local nu este încă gata. Vă rugăm să aşteptaţi un moment şi să încercaţi din nou.", - "localServer": "Server local" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Eroare la verificarea versiunii", - "checkFailed": "Verificarea actualizărilor a eșuat", - "upToDate": "Aplicația este actualizată", - "currentVersion": "Rulați versiunea {{version}}", - "updateAvailable": "Actualizare disponibilă", - "newVersionAvailable": "O nouă versiune este disponibilă! Funcționezi {{current}}, dar {{latest}} este disponibil.", - "betaVersion": "Versiune Beta", - "betaVersionDesc": "Rulați {{current}}, care este mai nou decât cea mai recentă versiune stabilă {{latest}}.", - "releasedOn": "Publicat pe {{date}}", - "downloadUpdate": "Descarcă actualizare", - "checking": "Se caută actualizări...", - "checkUpdates": "Verifică pentru actualizări", - "checkingUpdates": "Se caută actualizări...", - "updateRequired": "Actualizare necesară" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Inchide", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Continuă", - "maintenance": "Mentenanţă", - "degraded": "Degradat", - "error": "Eroare", - "warning": "Avertizare", - "unsavedChanges": "Modificări nesalvate", - "dismiss": "Renunţaţi", - "loading": "Încărcare...", - "optional": "Opţional", - "connect": "Conectează-te", - "copied": "Copiat", - "connecting": "Conectare...", - "updateAvailable": "Actualizare disponibilă", - "appName": "Termen", - "openInNewTab": "Deschide într-o filă nouă", - "noReleases": "Fără lansări", - "updatesAndReleases": "Actualizări & lansări", - "newVersionAvailable": "O nouă versiune ({{version}}) este disponibilă.", - "failedToFetchUpdateInfo": "Obținerea informațiilor de actualizare a eșuat", - "preRelease": "Pre-Eliberare", - "noReleasesFound": "Nici o lansare găsită.", - "cancel": "Anulează", - "username": "Nume", - "login": "Autentificare", - "register": "Inregistrare", - "password": "Parolă", - "confirmPassword": "Confirmare parolă", - "back": "Înapoi", - "save": "Salvează", - "saving": "Salvare...", - "delete": "Ștergere", - "rename": "Redenumire", - "edit": "Editare", - "add": "Adăugare", - "confirm": "Confirmare", - "no": "Nr", - "or": "SAU", - "next": "Următoarea", - "previous": "Anterior", - "refresh": "Împrospătează", - "language": "Limba", - "checking": "Verificare...", - "checkingDatabase": "Verificare conexiune la baza de date...", - "checkingAuthentication": "Verificare autentificare...", - "backendReconnected": "Conexiune server restaurată", - "connectionDegraded": "Conexiune la server pierdută, se recuperează…", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Elimină", - "create": "Crează", - "update": "Actualizare", - "copy": "Copiază", - "copyFailed": "Copierea în clipboard a eșuat", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Restaurează", - "of": "din" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Acasă", + "home": "Home", "terminal": "Terminal", - "docker": "Doctor", - "tunnels": "Tuneluri", - "fileManager": "Manager fişiere", - "serverStats": "Statistici server", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", "admin": "Admin", - "userProfile": "Profil utilizator", - "splitScreen": "Ecran Divizat", - "confirmClose": "Închide această sesiune activă?", - "close": "Inchide", - "cancel": "Anulează", - "sshManager": "Manager SSH", - "cannotSplitTab": "Nu se poate împărți această filă", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Copiază parola", - "copySudoPassword": "Copiază parola Sudo", - "passwordCopied": "Parola a fost copiată în clipboard", - "noPasswordAvailable": "Nici o parolă disponibilă", - "failedToCopyPassword": "Copierea parolei a eșuat", - "refreshTab": "Reîmprospătează conexiunea", - "openFileManager": "Deschide managerul de fişiere", - "dashboard": "Panou", - "networkGraph": "Grafic rețea", - "quickConnect": "Conectare rapidă", - "sshTools": "Instrumente SSH", - "history": "Istoric", - "hosts": "Gazde", - "snippets": "Snippet-uri", - "hostManager": "Manager Gazdă", - "credentials": "Acreditări", - "connections": "Conexiuni", + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", "roleAdministrator": "Administrator", - "roleUser": "Utilizator" + "roleUser": "User" }, "hosts": { - "hosts": "Gazde", - "noHosts": "Fără gazde SSH", - "retry": "Reîncercați", - "refresh": "Împrospătează", - "optional": "Opţional", - "downloadSample": "Descarcă eșantion", - "failedToDeleteHost": "Nu s-a putut șterge {{name}}", - "importSkipExisting": "Import (săriți peste existență)", - "connectionDetails": "Detalii conexiune", - "ssh": "SH", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", + "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Desktop la distanță", - "port": "Portul", - "username": "Nume", - "folder": "Dosar", - "tags": "Etichete", - "pin": "Fixează", - "addHost": "Adaugă Gazdă", - "editHost": "Editează Gazda", - "cloneHost": "Gazda Clonei", - "enableTerminal": "Activează Terminal", - "enableTunnel": "Activează tunelul", - "enableFileManager": "Activează Managerul de Fișiere", - "enableDocker": "Activează Docker", - "defaultPath": "Cale implicită", - "connection": "Conexiune", - "upload": "Incarca", - "authentication": "Autentificare", - "password": "Parolă", - "key": "Cheie", - "credential": "Acreditări", - "none": "Niciunul", - "sshPrivateKey": "Cheie Privată SSH", - "keyType": "Tip cheie", - "uploadFile": "Incarca fisier", - "tabGeneral": "Generalități", - "tabSsh": "SH", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", + "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", - "tabVnc": "NVC", - "tabTunnels": "Tuneluri", - "tabDocker": "Doctor", - "tabFiles": "Fișiere", - "tabStats": "Statistici", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Partajare", - "tabAuthentication": "Autentificare", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", - "tunnel": "Tunel", - "fileManager": "Manager fişiere", - "serverStats": "Statistici server", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Dosar \"{{oldName}}\" redenumit \"{{newName}}\" cu succes", - "failedToRenameFolder": "Nu s-a putut redenumi dosarul", - "movedToFolder": "Mutat la \"{{folder}}\"", - "editHostTooltip": "Editare gazdă", - "statusChecks": "Verificări stare", - "metricsCollection": "Colecție de valori", - "metricsInterval": "Intervalul de colectare a valorilor", - "metricsIntervalDesc": "Cât de des se colectează statisticile serverului (5s - 1h)", - "behavior": "Comportament", - "themePreview": "Previzualizare temă", - "theme": "Tema", - "fontFamily": "Familia de fonturi", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Spațiere de scrisori", - "lineHeight": "Înălțime linie", - "cursorStyle": "Stil cursor", - "cursorBlink": "Clipire cursor", - "scrollbackBuffer": "Tampon de Derulare", - "bellStyle": "Stil Bell", - "rightClickSelectsWord": "Cuvânt Selectare Click Dreapta", - "fastScrollModifier": "Modificare derulare rapidă", - "fastScrollSensitivity": "Sensibilitate derulare rapidă", - "sshAgentForwarding": "Agent SSH Redirecționare", - "backspaceMode": "Mod spațiu fundal", - "startupSnippet": "Snippet de pornire", - "selectSnippet": "Selectaţi snippet", - "forceKeyboardInteractive": "Forțează tastatura- interactivă", - "overrideCredentialUsername": "Suprascrie utilizatorul de acreditare", - "overrideCredentialUsernameDesc": "Utilizați numele de utilizator specificat mai sus în locul numelui de utilizator al acreditării", - "jumpHostChain": "Lanț gazdă salt", - "portKnocking": "Ciorna portului", - "addKnock": "Adaugă port", - "addProxyNode": "Adaugă modul", - "proxyNode": "Nod proxy", - "proxyType": "Tip Proxy", - "quickActions": "Acțiuni rapide", - "sudoPasswordAutoFill": "Completare automată parolă Sudo", - "sudoPassword": "Parolă Sudo", - "keepaliveInterval": "Interval Keepalive (ms)", - "moshCommand": "Comanda MOSH", - "environmentVariables": "Variabile de mediu", - "addVariable": "Adaugă variabilă", - "docker": "Doctor", - "copyTerminalUrl": "Copiază URL-ul Terminal", - "copyFileManagerUrl": "Copiază URL-ul managerului de fișiere", - "copyRemoteDesktopUrl": "Copiază URL-ul pentru Desktop la distanță", - "failedToConnect": "Conectarea la consolă a eșuat", - "connect": "Conectează-te", - "disconnect": "Deconectare", - "start": "Pornire", - "enableStatusCheck": "Activează verificarea stării", - "enableMetrics": "Activează valorile", - "bulkUpdateFailed": "Actualizare în masă eșuată", - "selectAll": "Selectează tot", - "deselectAll": "Deselectează tot", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Proiectile securizate", - "virtualNetwork": "Rețea virtuală", - "unencryptedShell": "Proiectil necriptat", - "addressIp": "Adresă / IP", - "friendlyName": "Nume prietenos", - "folderAndAdvanced": "Dosar și Avansat", - "privateNotes": "Note private", - "privateNotesPlaceholder": "Detalii despre acest server...", - "pinToTop": "Fixează sus", - "pinToTopDesc": "Arată întotdeauna această gazdă în partea de sus a listei", - "portKnockingSequence": "Secvenţa de Knocking port", - "addKnockBtn": "Adaugă Ciocănire", - "noPortKnocking": "Nici un bătut de port configurat.", - "knockPort": "Port Ciocănire", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Întârziere după (ms)", - "useSocks5Proxy": "Folosește Proxy SOCKS5", - "useSocks5ProxyDesc": "Ruta conexiunea printr-un server proxy", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", "proxyHost": "Proxy Host", - "proxyPort": "Port Proxy", - "proxyUsername": "Utilizator Proxy", - "proxyPassword": "Parolă proxy", - "proxySingleMode": "Proxy unic", - "proxyChainMode": "Lanț Proxy", - "you": "Tu", - "jumpHostChainLabel": "Lanț gazdă salt", - "addJumpBtn": "Adaugă salt", - "noJumpHosts": "Nu sunt gazde salt configurate.", - "selectAServer": "Selectează un server...", - "sshPort": "Portul SSH", - "authMethod": "Metoda de autentificare", - "storedCredential": "Acreditări stocate", - "selectACredential": "Selectați o acreditare...", - "keyTypeLabel": "Tip cheie", - "keyTypeAuto": "Detectare automată", - "keyPasteTab": "Lipește", - "keyUploadTab": "Incarca", - "keyFileLoaded": "Fișier cheie încărcat", - "keyUploadClick": "Click pentru a încărca .pem / .key / .ppk", - "clearKey": "Șterge cheia", - "keySaved": "Cheia SSH salvată", - "keyReplaceNotice": "lipeste o noua cheie de mai jos pentru a o inlocui", - "keyPassphraseSaved": "Parola salvată, tastați pentru a schimba", - "replaceKey": "Înlocuiți cheia", - "forceKeyboardInteractiveLabel": "Forțează interactiv tastatura", - "forceKeyboardInteractiveShortDesc": "Forțează introducerea parolei manuale chiar dacă sunt chei", - "terminalAppearance": "Aspectul terminal", - "colorTheme": "Tema de culoare", - "fontFamilyLabel": "Familia de fonturi", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Stil cursor", - "letterSpacingPx": "Spațiere de scrisori (px)", - "lineHeightLabel": "Înălțime linie", - "bellStyleLabel": "Stil Bell", - "backspaceModeLabel": "Mod spațiu fundal", - "cursorBlinking": "Clipire cursor", - "cursorBlinkingDesc": "Activează animația de clipire pentru cursorul terminal", - "rightClickSelectsWordLabel": "Click dreapta - Cuvânt Selectare", - "rightClickSelectsWordShortDesc": "Selectaţi cuvântul sub cursor la clic-dreapta", - "behaviorAndAdvanced": "Comportament & Avansat", - "scrollbackBufferLabel": "Tampon de Derulare", - "scrollbackMaxLines": "Numărul maxim de linii păstrate în istoric", - "sshAgentForwardingLabel": "Agent SSH Redirecționare", - "sshAgentForwardingShortDesc": "Transmite cheile SSH locale acestei gazde", - "enableAutoMosh": "Activează Auto-Mosh", - "enableAutoMoshDesc": "Preferă Mosh peste SSH dacă este disponibil", - "enableAutoTmux": "Activează Auto-Tmux", - "enableAutoTmuxDesc": "Lansează automat sau atașează la sesiunea tmux", - "sudoPasswordAutoFillLabel": "Completare automată parolă Sudo", - "sudoPasswordAutoFillShortDesc": "Furnizează automat parola sudo când i se solicită", - "sudoPasswordLabel": "Parolă Sudo", - "environmentVariablesLabel": "Variabile de mediu", - "addVariableBtn": "Adaugă variabilă", - "noEnvVars": "Nicio variabilă de mediu configurată.", - "fastScrollModifierLabel": "Modificare derulare rapidă", - "fastScrollSensitivityLabel": "Sensibilitate derulare rapidă", - "moshCommandLabel": "Comanda Moscheii", - "startupSnippetLabel": "Snippet de pornire", - "keepaliveIntervalLabel": "Interval Keepalive (secunde)", - "maxKeepaliveMisses": "Max Keepalive ratează", - "tunnelSettings": "Setări tunel", - "enableTunneling": "Activează tunelarea", - "enableTunnelingDesc": "Activează funcționalitatea tunelului SSH pentru această gazdă", - "serverTunnelsSection": "Tuneluri server", - "addTunnelBtn": "Adaugă tunel", - "noTunnelsConfigured": "Niciun tunel configurat.", - "tunnelLabel": "Tunel {{number}}", - "tunnelType": "Tip tunel", - "tunnelModeLocalDesc": "Înaintează un port local către un port de pe serverul de la distanță (sau o gazdă accesibilă de la acesta).", - "tunnelModeRemoteDesc": "Inainteaza un port pe server inapoi catre un port local de pe calculatorul tau.", - "tunnelModeDynamicDesc": "Creați un proxy SOCKS5 pe un port local pentru un port dinamic de înaintare.", - "sameHost": "Această gazdă (tunel direct)", - "endpointHost": "Gazdă de sfârșit", - "endpointPort": "Port final", - "bindHost": "Leagă Gazda", - "sourcePort": "Portul sursă", - "maxRetries": "Repetări maxime", - "retryIntervalS": "Reîncearcă Interval (s)", - "autoStartLabel": "Auto-pornire", - "autoStartDesc": "Conectează automat acest tunel atunci când gazda este încărcată", - "tunnelConnecting": "Conectare tunel...", - "tunnelDisconnected": "Tunel deconectat", - "failedToConnectTunnel": "Conectare nereușită", - "failedToDisconnectTunnel": "Deconectare eșuată", - "dockerIntegration": "Integrare Docker", - "enableDockerMonitor": "Activează Docker", - "enableDockerMonitorDesc": "Monitorizează și gestionează containerele pe această gazdă prin intermediul Docker", - "enableFileManagerMonitor": "Activează Managerul de Fișiere", - "enableFileManagerMonitorDesc": "Răsfoiți și gestionați fișierele pe această gazdă prin SFTP", - "defaultPathLabel": "Cale implicită", - "fileManagerPathHint": "Directorul se deschide atunci când managerul de fişiere lansează pentru această gazdă.", - "statusChecksLabel": "Verificări stare", - "enableStatusChecks": "Activează Verificările Stării", - "enableStatusChecksDesc": "Periodic pâna la această gazdă pentru a verifica disponibilitatea", - "useGlobalInterval": "Folosește Interval Global", - "useGlobalIntervalDesc": "Suprascrie cu intervalul de verificare a stării pe panoul serverului", - "checkIntervalS": "Verifică intervalul (s)", - "checkIntervalDesc": "Secunde între fiecare limitare a conectivității", - "metricsCollectionLabel": "Colecție de valori", - "enableMetricsLabel": "Activează valorile", - "enableMetricsDesc": "Colectează CPU, RAM, disk și utilizarea rețelei de pe această gazdă", - "useGlobalMetrics": "Folosește Interval Global", - "useGlobalMetricsDesc": "Suprascrie cu intervalul de măsurare la nivelul serverului", - "metricsIntervalS": "Interval de valori", - "metricsIntervalDesc2": "Secunde între imagini metrice", - "visibleWidgets": "Widget-uri vizibile", - "cpuUsageLabel": "Utilizare procesor", - "cpuUsageDesc": "Procentul procesorului, mediile de încărcare, graficul sparkline", - "memoryLabel": "Utilizare memorie", - "memoryDesc": "Utilizare RAM, swap, cache", - "storageLabel": "Utilizare disc", - "storageDesc": "Utilizare disc per punct de montare", - "networkLabel": "Interfețe de rețea", - "networkDesc": "Lista de interfață și lățimea de bandă", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", "uptimeLabel": "Uptime", "uptimeDesc": "System uptime and boot time", - "systemInfoLabel": "Informații despre sistem", - "systemInfoDesc": "OS, kernel, hostname, arhitectură", - "recentLoginsLabel": "Autentificări recente", - "recentLoginsDesc": "Evenimente de conectare cu succes și eșuate", - "topProcessesLabel": "Procese de top", - "topProcessesDesc": "PID, CPU%, MEM%, comandă", - "listeningPortsLabel": "Ascultarea porturilor", - "listeningPortsDesc": "Deschideți porturile cu proces și stare", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", "firewallLabel": "Firewall", - "firewallDesc": "Firewall, AppArmor, status SELinux", - "quickActionsLabel": "Acțiuni rapide", - "quickActionsToolbar": "Acțiunile rapide apar ca butoane în bara de statistici a serverului pentru execuția comenzii cu un singur clic.", - "noQuickActions": "Nici o acțiune rapidă încă.", - "buttonLabel": "Eticheta butonului", - "selectSnippetPlaceholder": "Selectați snippet...", - "addActionBtn": "Adaugă Acțiune", - "hostSharedSuccessfully": "Gazda partajată cu succes", - "failedToShareHost": "Distribuirea gazdei a eșuat", - "accessRevoked": "Acces revocat", - "failedToRevokeAccess": "Eroare la revocarea accesului", - "cancelBtn": "Anulează", - "savingBtn": "Salvare...", - "addHostBtn": "Adaugă Gazdă", - "hostUpdated": "Gazdă actualizată", - "hostCreated": "Gazdă creată", - "failedToSave": "Salvarea gazdei a eșuat", - "credentialUpdated": "Acreditări actualizate", - "credentialCreated": "Acreditări create", - "failedToSaveCredential": "Salvarea acreditărilor a eșuat", - "backToHosts": "Înapoi la gazde", - "backToCredentials": "Înapoi la acreditări", - "pinned": "Fixat", - "noHostsFound": "Nicio gazdă găsită", - "tryDifferentTerm": "Încercați un termen diferit", - "addFirstHost": "Adaugă primul tău gazdă pentru a începe", - "noCredentialsFound": "Acreditările nu au fost găsite", - "addCredentialBtn": "Adaugă acreditare", - "updateCredentialBtn": "Acreditări actualizare", - "features": "Caracteristici", - "noFolder": "(Fără dosar)", - "deleteSelected": "Ștergere", - "exitSelection": "Ieșiți din selecție", - "importSkip": "Import (săriți peste existență)", - "importOverwrite": "Import (suprascriere)", - "collapseBtn": "Restrânge", - "importExportBtn": "Importă / Exportă", - "hostStatusesRefreshed": "Stările gazdei reîmprospătate", - "failedToRefreshHosts": "Reîmprospătarea gazdelor a eșuat", - "movedHostTo": "Mutați {{host}} la \"{{folder}}\"", - "failedToMoveHost": "Mutarea gazdei a eșuat", - "folderRenamedTo": "Dosar redenumit \"{{name}}\"", - "deletedFolder": "Dosar șters \"{{name}}\"", - "failedToDeleteFolder": "Eroare la ștergerea dosarului", - "deleteAllInFolder": "Ștergeți toate gazdele din \"{{name}}\"? Acest lucru nu poate fi anulat.", - "deletedHost": "Eliminat {{name}}", - "copiedToClipboard": "Copiat în clipboard", - "terminalUrlCopied": "URL terminal copiat", - "fileManagerUrlCopied": "URL Manager de fişiere copiat", - "tunnelUrlCopied": "URL tunel copiat", - "dockerUrlCopied": "URL Docker copiat", - "serverStatsUrlCopied": "URL Statistici server copiat", - "rdpUrlCopied": "URL-ul DRP copiat", - "vncUrlCopied": "URL VNC copiat", - "telnetUrlCopied": "URL telnet copiat", - "remoteDesktopUrlCopied": "URL la distanță pentru Desktop copiat", - "expandActions": "Extindeți acțiunile", - "collapseActions": "Acțiuni de restrângere", - "wakeOnLanAction": "Trezire prin LAN", - "wakeOnLanSuccess": "Pachet magic trimis către {{name}}", - "wakeOnLanError": "Nu s-a putut trimite pachetul magic", - "cloneHostAction": "Gazda Clonei", - "copyAddress": "Copiază adresa", - "copyLink": "Copiază link-ul", - "copyTerminalUrlAction": "Copiază URL-ul Terminal", - "copyFileManagerUrlAction": "Copiază URL-ul managerului de fișiere", - "copyTunnelUrlAction": "Copiază URL-ul tunelului", - "copyDockerUrlAction": "Copiază URL-ul Docker", - "copyServerStatsUrlAction": "Copiază URL-ul de statistici al serverului", - "copyRdpUrlAction": "Copiază URL-ul DRP", - "copyVncUrlAction": "Copiază URL-ul VNC", - "copyTelnetUrlAction": "Copiază URL-ul Telnet", - "copyRemoteDesktopUrlAction": "Copiază URL-ul pentru Desktop la distanță", - "deleteCredentialConfirm": "Ștergeți acreditările \"{{name}}\"?", - "deletedCredential": "Eliminat {{name}}", - "deploySSHKeyTitle": "Lansează Cheie SSH", - "deployingBtn": "Desfășurare...", - "deployBtn": "Desfășurare", - "failedToDeployKey": "Lansarea cheii a eșuat", - "deleteHostsConfirm": "Ștergeți {{count}} gazdă{{plural}}? Acest lucru nu poate fi anulat.", - "movedToRoot": "Mutat la root", - "failedToMoveHosts": "Mutarea gazdelor a eșuat", - "enableTerminalFeature": "Activează Terminal", - "disableTerminalFeature": "Dezactivează terminalul", - "enableFilesFeature": "Activare fişiere", - "disableFilesFeature": "Dezactivează fișierele", - "enableTunnelsFeature": "Activează tunelurile", - "disableTunnelsFeature": "Dezactivează tunelurile", - "enableDockerFeature": "Activează Docker", - "disableDockerFeature": "Dezactivează Docker", - "addTagsPlaceholder": "Adaugă etichete...", - "authDetails": "Detalii autentificare", - "credType": "Tip", - "generateKeyPairDesc": "Generarea unei noi perechi de chei, atât private, cât și publice, va fi completată automat.", - "generatingKey": "Generare...", - "generateLabel": "Generează {{label}}", - "uploadFileBtn": "Incarca fisier", - "keyPassphraseOptional": "Parolă cheie (opțional)", - "sshPublicKeyOptional": "Cheie publică SSH (opțional)", - "publicKeyGenerated": "Cheie publică generată", - "failedToGeneratePublicKey": "Nu s-a putut obține cheia publică", - "publicKeyCopied": "Cheie publică copiată", - "keyPairGenerated": "{{label}} pereche de chei generată", - "failedToGenerateKeyPair": "Nu s-a putut genera perechea cheilor", - "searchHostsPlaceholder": "Caută gazde, adrese, etichete…", - "searchCredentialsPlaceholder": "Căutare acreditări…", - "refreshBtn": "Împrospătează", - "addTag": "Adaugă etichete...", - "deleteConfirmBtn": "Ștergere", - "tunnelRequirementsText": "Serverul SSH trebuie să aibă GatewayPorts da, AllowTcpForwarding da și permitere RootLogin setat în /etc/ssh/ssh/sshd_config.", - "deleteHostConfirm": "Ștergeți \"{{name}}\"?", - "enableAtLeastOneProtocol": "Activați cel puțin un protocol de mai sus pentru a configura setările de autentificare și conexiune.", - "keyPassphrase": "Parola cheie", - "connectBtn": "Conectează-te", - "disconnectBtn": "Deconectare", - "basicInformation": "Informatii de baza", - "authDetailsSection": "Detalii autentificare", - "credTypeLabel": "Tip", - "hostsTab": "Gazde", - "credentialsTab": "Acreditări", - "selectMultiple": "Selectează mai multe", - "selectHosts": "Selectează gazdele", - "connectionLabel": "Conexiune", - "authenticationLabel": "Autentificare", - "generateKeyPairTitle": "Generează pereche de chei", - "generateKeyPairDescription": "Generarea unei noi perechi de chei, atât private, cât și publice, va fi completată automat.", - "generateFromPrivateKey": "Generează din Cheie Privată", - "refreshBtn2": "Împrospătează", - "exitSelectionTitle": "Ieșiți din selecție", - "exportAll": "Exportă tot", - "addHostBtn2": "Adaugă Gazdă", - "addCredentialBtn2": "Adaugă acreditare", - "checkingHostStatuses": "Verificare stări gazdă...", - "pinnedSection": "Fixat", - "hostsExported": "Gazde exportate cu succes", - "exportFailed": "Nu s-au putut exporta gazdele", - "sampleDownloaded": "Mostră fișier descărcat", - "failedToDeleteCredential2": "Nu s-a reușit ștergerea acreditărilor", - "noFolderOption": "(Fără dosar)", - "nSelected": "{{count}} selectate", - "featuresMenu": "Caracteristici", - "moveMenu": "Mutare", - "cancelSelection": "Anulează", - "deployDialogDesc": "Lansează {{name}} la tastele autorizate.", - "targetHostLabel": "Gazda țintă", - "selectHostOption": "Selectează o gazdă...", - "keyDeployedSuccess": "Cheie implementată cu succes", - "failedToDeployKey2": "Lansarea cheii a eșuat", - "deletedCount": "{{count}} host-uri șterse", - "failedToDeleteCount": "Ștergerea host-urilor {{count}} a eșuat", - "duplicatedHost": "Duplicat \"{{name}}\"", - "failedToDuplicateHost": "Duplicarea gazdei a eșuat", - "updatedCount": "Actualizat {{count}} gazde", - "friendlyNameLabel": "Nume prietenos", - "descriptionLabel": "Descriere", - "loadingHost": "Se încarcă gazda...", - "loadingHosts": "Se încarcă gazde...", - "loadingCredentials": "Se încarcă acreditările...", - "noHostsYet": "Nicio gazdă încă", - "noHostsMatchSearch": "Nicio gazdă nu se potrivește cu căutarea ta", - "hostNotFound": "Gazda nu a fost găsită", - "searchHosts": "Caută gazde...", - "sortHosts": "Sortează gazdele", - "sortDefault": "Ordine implicită", - "sortNameAsc": "Nume (A → Z)", - "sortNameDesc": "Nume (Z → A)", - "sortIpAsc": "Adresă IP (Asc)", - "sortIpDesc": "Adresă IP (Desc)", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", "sortOnlineFirst": "Online First", "sortOfflineFirst": "Offline First", - "sortPinnedFirst": "Fixat primul", - "filterHosts": "Filtrare gazde", - "filterClearAll": "Ștergeți filtrele", - "filterStatusGroup": "Stare", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", "filterOnline": "Online", "filterOffline": "Offline", - "filterPinned": "Fixat", - "filterAuthGroup": "Tip de autentificare", - "filterAuthPassword": "Parolă", - "filterAuthKey": "Cheie SSH", - "filterAuthCredential": "Acreditare", - "filterAuthNone": "Nici unul", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Caracteristici", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Manager de fișiere", - "filterFeatureTunnel": "Tunel", - "filterFeatureDocker": "Docher", - "filterTagsGroup": "Etichete", - "shareHost": "Distribuie gazda", - "shareHostTitle": "Partajare: {{name}}", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Acest gazdă trebuie să utilizeze o acreditare pentru a activa partajarea. Editați gazda și atribuiți o acreditare mai întâi." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Conexiune", - "authentication": "Autentificare", - "connectionSettings": "Setări conexiune", - "displaySettings": "Setări de afișare", - "audioSettings": "Setări audio", - "rdpPerformance": "Performanță DRP", - "deviceRedirection": "Redirecționare dispozitiv", - "session": "Sesiune", - "gateway": "Poartă", - "remoteApp": "TelecomandareAplicație", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", "clipboard": "Clipboard", - "sessionRecording": "Înregistrare sesiune", - "wakeOnLan": "Pornit-de-LAN", - "vncSettings": "Setări VNC", - "terminalSettings": "Setări terminal", - "rdpPort": "Port RDP", - "username": "Nume", - "password": "Parolă", - "domain": "Domeniu", - "securityMode": "Mod de securitate", - "colorDepth": "Adâncime culoare", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Înălțime", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Metoda de redimensionare", - "clientName": "Nume client", - "initialProgram": "Program inițial", - "serverLayout": "Aspect server", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Portul gateway-ului", - "gatewayUsername": "Utilizator Gateway", - "gatewayPassword": "Parolă gateway", - "gatewayDomain": "Domeniu gateway", - "remoteAppProgram": "Program RemoteApp", - "workingDirectory": "Director de lucru", - "arguments": "Argumente", - "normalizeLineEndings": "Normalizează liniile de sfârșit", - "recordingPath": "Cale de înregistrare", - "recordingName": "Nume înregistrare", - "macAddress": "Adresă MAC", - "broadcastAddress": "Adresă difuzare", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Timpul (e) de așteptare", - "driveName": "Nume unitate", - "drivePath": "Calea Drive", - "ignoreCertificate": "Ignoră certificatul", - "ignoreCertificateDesc": "Permite conexiuni cu certificate autosemnate", - "forceLossless": "Forțează pierderea", - "forceLosslessDesc": "Forțează codificarea fără pierderi a imaginilor (de calitate superioară, lățime de bandă mai mare)", - "disableAudio": "Dezactivare audio", - "disableAudioDesc": "Dezactivează sunetul din sesiunea la distanță", - "enableAudioInput": "Activează intrare audio (Microfon)", - "enableAudioInputDesc": "Retrimite microfonul local la sesiunea de la distanță", - "wallpaper": "Fundal", - "wallpaperDesc": "Arată imaginea de fundal pe desktop (dezactivarea îmbunătățește performanța)", - "theming": "Tema", - "themingDesc": "Activează teme vizuale și stiluri", - "fontSmoothing": "Smoothing font", - "fontSmoothingDesc": "Activează redarea fontului ClearType", - "fullWindowDrag": "Glisare în fereastră completă", - "fullWindowDragDesc": "Arată conținutul ferestrei în timpul tragerii", - "desktopComposition": "Componență Desktop", - "desktopCompositionDesc": "Activează efectele Aero glass", - "menuAnimations": "Animaţii meniu", - "menuAnimationsDesc": "Activare animații de estompare și diapozitiv meniu", - "disableBitmapCaching": "Dezactivează Caching Bitmap", - "disableBitmapCachingDesc": "Dezactivează cache-ul bitmap (poate ajuta cu glituri)", - "disableOffscreenCaching": "Dezactivează Caching în afara ecranului", - "disableOffscreenCachingDesc": "Oprește memoria cache", - "disableGlyphCaching": "Dezactivează Glyph Caching", - "disableGlyphCachingDesc": "Opriţi cache-ul glicol", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Utilizați conducta grafică RemoteFX", - "enablePrinting": "Activează imprimarea", - "enablePrintingDesc": "Redirecţionaţi imprimantele locale la sesiunea de la distanţă", - "enableDriveRedirection": "Activează redirecționarea unității", - "enableDriveRedirectionDesc": "Mapează un folder local ca o unitate în sesiunea de la distanţă", - "createDrivePath": "Crează Calea Unității", - "createDrivePathDesc": "Creează automat dosarul dacă acesta nu există", - "disableDownload": "Dezactivează descărcarea", - "disableDownloadDesc": "Previne descărcarea fişierelor din sesiunea la distanţă", - "disableUpload": "Dezactivează Încărcarea", - "disableUploadDesc": "Împiedicați încărcarea fișierelor în sesiunea de la distanță", - "enableTouch": "Activare atingere", - "enableTouchDesc": "Activează redirecționarea către atingere", - "consoleSession": "Sesiune Consolă", - "consoleSessionDesc": "Conectează-te la consolă (sesiunea 0) în loc de o nouă sesiune", - "sendWolPacket": "Trimite Pachet WOL", - "sendWolPacketDesc": "Trimite un pachet magic pentru a trezi această gazdă înainte de conectare", - "disableCopy": "Dezactivează copierea", - "disableCopyDesc": "Împiedică copierea textului din sesiunea la distanță", - "disablePaste": "Dezactivează Lipirea", - "disablePasteDesc": "Împiedică inserarea textului în sesiunea de la distanţă", - "createPathIfMissing": "Crează calea dacă lipsește", - "createPathIfMissingDesc": "Creează automat directorul de înregistrare", - "excludeOutput": "Exclude ieșirea", - "excludeOutputDesc": "Nu înregistra ieșirea ecranului (doar metadate)", - "excludeMouse": "Exclude mouse", - "excludeMouseDesc": "Nu înregistra mișcările mouse-ului", - "includeKeystrokes": "Include tastări", - "includeKeystrokesDesc": "Înregistrează tastele brute în plus față de ieșirea ecranului", - "vncPort": "Port VNC", - "vncPassword": "Parolă VNC", - "vncUsernameOptional": "Nume utilizator (opţional)", - "vncLeaveBlank": "Lăsați necompletat dacă nu este necesar", - "cursorMode": "Modul Cursor", - "swapRedBlue": "Schimbă Roșu/Albastru", - "swapRedBlueDesc": "Schimbă canalele de culoare roşie şi albastră (rezolvă unele probleme de culoare)", - "readOnly": "Doar citire", - "readOnlyDesc": "Vezi ecranul la distanţă fără a trimite nici o intrare", - "telnetPort": "Port Telnet", - "terminalType": "Tip terminal", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Schemă de culori", - "backspaceKey": "Cheie de fundal", - "saveHostFirst": "Salvează gazda mai întâi.", - "sharingOptionsAfterSave": "Opțiuni de partajare sunt disponibile după ce gazda a fost salvată.", - "sharingLoadError": "Eroare la încărcarea partajării datelor. Verificați conexiunea și încercați din nou.", - "shareHostSection": "Distribuie gazda", - "shareWithUser": "Partajează cu utilizatorul", - "shareWithRole": "Partajează cu Rol", - "selectUser": "Selectează utilizator", - "selectRole": "Selectaţi rolul", - "selectUserOption": "Selectați un utilizator...", - "selectRoleOption": "Selectează un rol...", - "permissionLevel": "Nivel permisiuni", - "expiresInHours": "Expiră în (ore)", - "noExpiryPlaceholder": "Lăsați gol pentru a nu expira", - "shareBtn": "Distribuie", - "currentAccess": "Acces curent", - "typeHeader": "Tip", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Permisiune", - "grantedByHeader": "Oferit de", - "expiresHeader": "Expiră", - "noAccessEntries": "Nici o intrare de acces încă.", - "expiredLabel": "Expirat", - "neverLabel": "Niciodată", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Anulează", - "savingBtn": "Salvare...", - "updateHostBtn": "Actualizare Gazdă", - "addHostBtn": "Adaugă Gazdă" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Caută gazde, comenzi sau setări...", - "quickActions": "Acțiuni rapide", - "hostManager": "Manager Gazdă", - "hostManagerDesc": "Gestionează, adaugă sau editează gazde", - "addNewHost": "Adaugă Gazdă Nouă", - "addNewHostDesc": "Înregistrează un nou gazdă", - "adminSettings": "Setări Admin", - "adminSettingsDesc": "Configurați preferințele de sistem și utilizatorii", - "userProfile": "Profil utilizator", - "userProfileDesc": "Gestionează-ți contul și preferințele", - "addCredential": "Adaugă acreditare", - "addCredentialDesc": "Stocare chei SSH sau parole", - "recentActivity": "Activitate recentă", - "serversAndHosts": "Servere & Gazde", - "noHostsFound": "Nicio gazdă găsită se potrivește \"{{search}}\"", - "links": "Link-uri", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Selectare", - "toggleWith": "Comută cu" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Nici o filă atribuită", - "focusedPane": "Panou activ" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Fără conexiuni", - "noConnectionsDesc": "Deschideți un terminal, un manager de fișiere sau un desktop la distanță pentru a vedea conexiunile aici", - "connectedFor": "Conectat pentru {{duration}}", - "connected": "Conectat", - "disconnected": "Deconectat", - "closeTab": "Închide fila", - "closeConnection": "Conexiune strânsă", - "forgetTab": "Uita", - "removeBackground": "Elimina", - "reconnect": "Reconectați-vă", - "reopenTab": "Redeschide", - "sectionOpen": "Deschide", - "sectionBackground": "Fundal", - "backgroundDesc": "Sesiunile persistă timp de 30 de minute după deconectare și pot fi reconectate.", - "persisted": "A persistat în fundal", - "expiresIn": "Expiră în {{duration}}", - "search": "Căutați conexiuni...", - "noSearchResults": "Nicio conexiune nu corespunde căutării dvs." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Conectare la {{type}} sesiune...", - "connectionError": "Eroare de conexiune", - "connectionFailed": "Conexiune eșuată", - "failedToConnect": "Obținerea token-ului de conectare a eșuat", - "hostNotFound": "Gazda nu a fost găsită", - "noHostSelected": "Niciun gazdă selectat", - "reconnect": "Reconectare", - "retry": "Reîncercați", - "guacdUnavailable": "Serviciul desktop la distanţă (guacd) nu este disponibil. Vă rugăm să vă asiguraţi că guacd rulează şi este accesibil şi configurat corespunzător în setările de administrare.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (ecran blocat)", - "winKey": "Cheie Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Schimb", - "win": "Victorie", - "stickyActive": "{{key}} (latched - click pentru lansare)", - "stickyInactive": "{{key}} (click pentru a latch)", - "esc": "Evadează", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Acasă", - "end": "Sfârșit", - "pageUp": "Pagină în sus", - "pageDown": "Pagină jos", - "arrowUp": "Săgeată în sus", - "arrowDown": "Săgeată jos", - "arrowLeft": "Săgeată la stânga", - "arrowRight": "Săgeată la dreapta", - "fnToggle": "Taste Funcție", - "reconnect": "Reconectează Sesiunea", - "collapse": "Restrânge bara de instrumente", - "expand": "Extinde bara de instrumente", - "dragHandle": "Trageți pentru a repoziționa" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Conectează-te la Gazdă", - "clear": "Curăță", - "paste": "Lipește", - "reconnect": "Reconectare", - "connectionLost": "Conexiune pierdută", - "connected": "Conectat", - "clipboardWriteFailed": "Copierea în clipboard a eșuat. Asigură-te că pagina este deservită prin HTTPS sau localhost.", - "clipboardReadFailed": "Nu a reușit să citească din clipboard. Asigurați-vă că sunt acordate permisiuni pentru clipboard.", - "clipboardHttpWarning": "Lipirea necesită HTTPS. Folosește Ctrl+Shift+V sau servește Termix-ul prin HTTPS.", - "unknownError": "Eroare necunoscută", - "websocketError": "Eroare la conexiunea WebSocket", - "connecting": "Conectare...", - "noHostSelected": "Niciun gazdă selectat", - "reconnecting": "Reconectare... ({{attempt}}/{{max}})", - "reconnected": "Reconectat cu succes", - "tmuxSessionCreated": "sesiune tmux creată: {{name}}", - "tmuxSessionAttached": "sesiune tmux atașată: {{name}}", - "tmuxUnavailable": "tmux nu este instalat pe gazda telecomandei, revenind la shell standard", - "tmuxSessionPickerTitle": "Sesiuni tmux", - "tmuxSessionPickerDesc": "Sesiuni tmux existente găsite pe această gazdă. Selectați una pentru a reatașa sau pentru a crea o sesiune nouă.", - "tmuxWindows": "Ferestre", - "tmuxWindowCount": "{{count}} fereastră", - "tmuxAttached": "Clienți atașați", - "tmuxAttachedCount": "{{count}} atașat", - "tmuxLastActivity": "Ultima activitate", - "tmuxTimeJustNow": "chiar acum", - "tmuxTimeMinutes": "{{count}}m în urmă", - "tmuxTimeHours": "{{count}}acum o oră", - "tmuxTimeDays": "{{count}}d acum", - "tmuxCreateNew": "Începe o sesiune nouă", - "tmuxCopyHint": "Reglaţi selecţia şi apăsaţi Enter pentru a copia în clipboard", - "tmuxDetach": "Detașează de sesiunea tmux", - "tmuxDetached": "Detaşat de sesiunea tmux", - "maxReconnectAttemptsReached": "Reconexiunea maximă a fost atinsă", - "closeTab": "Inchide", - "connectionTimeout": "Conexiune expirată", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Rulând {{command}} - {{host}}", - "totpRequired": "Autentificare dublu factor necesară", - "totpCodeLabel": "Cod de verificare", - "totpVerify": "Verifică", - "warpgateAuthRequired": "Autentificare la urzeală obligatorie", - "warpgateSecurityKey": "Cheie de securitate", - "warpgateAuthUrl": "URL autentificare", - "warpgateOpenBrowser": "Deschide în browser", - "warpgateContinue": "Am finalizat autentificarea", - "opksshAuthRequired": "Autentificare OPKSSH necesară", - "opksshAuthDescription": "Finalizați autentificarea în browser-ul dvs. pentru a continua. Această sesiune va rămâne valabilă 24 de ore.", - "opksshOpenBrowser": "Deschide browser-ul pentru autentificare", - "opksshWaitingForAuth": "Se așteaptă autentificarea în browser...", - "opksshAuthenticating": "Se procesează autentificarea...", - "opksshTimeout": "Autentificarea a expirat. Încercați din nou.", - "opksshAuthFailed": "Autentificare eșuată. Te rugăm să verifici acreditările și să încerci din nou.", - "opksshSignInWith": "Conectați-vă cu {{provider}}", - "sudoPasswordPopupTitle": "Introduceți parola?", - "websocketAbnormalClose": "Conexiune închisă neașteptat. Acest lucru se poate datora unei probleme de configurare invers proxy sau SSL. Vă rugăm să verificați jurnalele serverului.", - "connectionLogTitle": "Jurnal de conexiune", - "connectionLogCopy": "Copiază jurnalele în clipboard", - "connectionLogEmpty": "Încă nu există jurnale de conexiune", - "connectionLogWaiting": "Se așteaptă jurnalele de conexiune...", - "connectionLogCopied": "Jurnalele de conexiune copiate în clipboard", - "connectionLogCopyFailed": "Copierea jurnalelor în clipboard a eșuat", - "connectionRejected": "Conexiune respinsă de server. Verificați autentificarea și configurarea rețelei.", - "hostKeyRejected": "Verificarea cheii gazdă SSH a fost respinsă. Conexiune anulată.", - "sessionTakenOver": "Sesiunea a fost deschisă într-o altă filă. Reconectare..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Niciun gazdă selectat", - "initializingEditor": "Se inițializează editorul...", - "file": "Fişier", - "folder": "Dosar", - "uploadFile": "Incarca fisier", - "downloadFile": "Descărcare", - "extractArchive": "Extrage arhiva", - "extractingArchive": "Se extrage {{name}}...", - "archiveExtractedSuccessfully": "{{name}} extrase cu succes", - "extractFailed": "Extragere eșuată", - "compressFile": "Fișier de comprimare", - "compressFiles": "Comprimă fişiere", - "compressFilesDesc": "Comprimă {{count}} elemente într-o arhivă", - "archiveName": "Nume arhivă", - "enterArchiveName": "Introduceți numele arhivei...", - "compressionFormat": "Format de comprimare", - "selectedFiles": "Fișiere selectate", - "andMoreFiles": "și {{count}} mai mult...", - "compress": "Comprimă", - "compressingFiles": "Se comprimă {{count}} elemente în {{name}}...", - "filesCompressedSuccessfully": "{{name}} creat cu succes", - "compressFailed": "Compresie eșuată", - "edit": "Editare", - "preview": "Previzualizare", - "previous": "Anterior", - "next": "Următoarea", - "pageXOfY": "Pagina {{current}} {{total}}", - "zoomOut": "Micșorare", - "zoomIn": "Mărire în", - "newFile": "Fișier nou", - "newFolder": "Dosar nou", - "rename": "Redenumire", - "uploading": "Încărcare...", - "uploadingFile": "Se încarcă {{name}}...", - "fileName": "Numele fișierului", - "folderName": "Nume folder", - "fileUploadedSuccessfully": "Fişier \"{{name}}\" încărcat cu succes", - "failedToUploadFile": "Încărcarea fișierului a eșuat", - "fileDownloadedSuccessfully": "Fişier \"{{name}}\" descărcat cu succes", - "failedToDownloadFile": "Descărcarea fișierului a eșuat", - "fileCreatedSuccessfully": "Fişier \"{{name}}\" creat cu succes", - "folderCreatedSuccessfully": "Dosar \"{{name}}\" creat cu succes", - "failedToCreateItem": "Crearea elementului a eșuat", - "operationFailed": "Operația {{operation}} a eșuat pentru {{name}}: {{error}}", - "failedToResolveSymlink": "Rezolvarea legăturii simbolice a eșuat", - "itemsDeletedSuccessfully": "{{count}} elemente șterse cu succes", - "failedToDeleteItems": "Ștergerea elementelor a eșuat", - "sudoPasswordRequired": "Parolă administrator necesară", - "enterSudoPassword": "Introduceți parola sudo pentru a continua această operațiune", - "sudoPassword": "Parolă Sudo", - "sudoOperationFailed": "Operația Sudo a eșuat", - "sudoAuthFailed": "Autentificare Sudo eșuată", - "dragFilesToUpload": "Plasați fișierele aici pentru a le încărca", - "emptyFolder": "Acest dosar este gol", - "searchFiles": "Căutare fișiere...", - "upload": "Incarca", - "selectHostToStart": "Selectați un gazdă pentru a începe gestionarea fișierelor", - "sshRequiredForFileManager": "Managerul de fișiere necesită SSH. Acest gazdă nu are SSH activat.", - "failedToConnect": "Conectarea la SSH a eșuat", - "failedToLoadDirectory": "Încărcarea directorului a eșuat", - "noSSHConnection": "Nicio conexiune SSH disponibilă", - "copy": "Copiază", - "cut": "Taie", - "paste": "Lipește", - "copyPath": "Copiază calea", - "copyPaths": "Copiază calea", - "delete": "Ștergere", - "properties": "Proprietăți", - "refresh": "Împrospătează", - "downloadFiles": "Descărcați {{count}} fișiere în browser", - "copyFiles": "Copiază elementele {{count}}", - "cutFiles": "Tăiați {{count}} elemente", - "deleteFiles": "Ştergeţi {{count}} elemente", - "filesCopiedToClipboard": "{{count}} elemente copiate în clipboard", - "filesCutToClipboard": "{{count}} elemente tăiate în clipboard", - "pathCopiedToClipboard": "Cale copiată în clipboard", - "pathsCopiedToClipboard": "{{count}} căi copiate în clipboard", - "failedToCopyPath": "Copierea căii în clipboard a eșuat", - "movedItems": "Mutat {{count}} elemente", - "failedToDeleteItem": "Ștergerea elementului a eșuat", - "itemRenamedSuccessfully": "{{type}} redenumit cu succes", - "failedToRenameItem": "Nu s-a putut redenumi elementul", - "download": "Descărcare", - "permissions": "Permisiuni", - "size": "Dimensiune", - "modified": "Modificat", - "path": "Cale", - "confirmDelete": "Sunteți sigur că doriți să ștergeți {{name}}?", - "permissionDenied": "Permisiune refuzată", - "serverError": "Eroare server", - "fileSavedSuccessfully": "Fișier salvat cu succes", - "failedToSaveFile": "Salvarea fișierului a eșuat", - "confirmDeleteSingleItem": "Sunteţi sigur că doriţi să ştergeţi definitiv \"{{name}}\"?", - "confirmDeleteMultipleItems": "Sunteți sigur că doriți să ștergeți definitiv elementele {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Sunteți sigur că doriți să ștergeți definitiv elementele {{count}} ? Aceasta include dosarele și conținutul lor.", - "confirmDeleteFolder": "Sunteţi sigur că doriţi să ştergeţi definitiv folderul \"{{name}}\" şi tot conţinutul acestuia?", - "permanentDeleteWarning": "Această acțiune nu poate fi anulată. Elementul(ele) va fi șters(e) definitiv de pe server.", - "recent": "Recente", - "pinned": "Fixat", - "folderShortcuts": "Comenzi rapide dosar", - "failedToReconnectSSH": "Reconectarea sesiunii SSH a eșuat", - "openTerminalHere": "Deschide Terminalul aici", - "run": "Rulează", - "openTerminalInFolder": "Deschide terminalul în acest folder", - "openTerminalInFileLocation": "Deschide terminalul la locația fișierului", - "runningFile": "Rulare - {{file}}", - "onlyRunExecutableFiles": "Pot rula numai fișiere executabile", - "directories": "Directoare", - "removedFromRecentFiles": "Eliminat \"{{name}}\" din fișierele recente", - "removeFailed": "Eliminare eșuată", - "unpinnedSuccessfully": "Deconectat \"{{name}}\" cu succes", - "unpinFailed": "Anulare fixare eșuată", - "removedShortcut": "Scurtătură eliminată \"{{name}}\"", - "removeShortcutFailed": "Eliminare comandă rapidă eșuată", - "clearedAllRecentFiles": "Toate fișierele recente au fost șterse", - "clearFailed": "Curățare eșuată", - "removeFromRecentFiles": "Șterge din fișierele recente", - "clearAllRecentFiles": "Ştergeţi toate fişierele recente", - "unpinFile": "Anulează fixarea fișierului", - "removeShortcut": "Elimină scurtătura", - "pinFile": "Fixează fișierul", - "addToShortcuts": "Adaugă la comenzi rapide", - "pasteFailed": "Lipirea a eșuat", - "noUndoableActions": "Nicio acțiune de neatins", - "undoCopySuccess": "Operație de copiere neterminată: {{count}} au copiat fișiere", - "undoCopyFailedDelete": "Anulare eșuată: Nu s-au putut șterge fișierele copiate", - "undoCopyFailedNoInfo": "Anulare eșuată: Nu s-a putut găsi informațiile copiate ale fișierului", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", - "undoMoveFailedMove": "Anulare eșuată: Nu se pot muta fișierele înapoi", - "undoMoveFailedNoInfo": "Anulare eșuată: Nu am putut găsi informațiile despre fișierul mutat", - "undoDeleteNotSupported": "Operația de ștergere nu poate fi anulată: Fișierele au fost șterse permanent de pe server", - "undoTypeNotSupported": "Tip de operațiune undo nesuportată", - "undoOperationFailed": "Operația de anulare a eșuat", - "unknownError": "Eroare necunoscută", - "confirm": "Confirmare", - "find": "Găsiți...", - "replace": "Înlocuiește", - "downloadInstead": "Descarcă în schimb", - "keyboardShortcuts": "Scurtături tastatură", - "searchAndReplace": "Căutare & Înlocuire", - "editing": "Editare", - "search": "Caută", - "findNext": "Găsește următorul", - "findPrevious": "Găsește anterior", - "save": "Salvează", - "selectAll": "Selectează tot", - "undo": "Anulează", - "redo": "Reface", - "moveLineUp": "Mută linia în sus", - "moveLineDown": "Mută linia jos", - "toggleComment": "Comutare comentariu", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Încărcarea imaginii a eșuat", - "startTyping": "Începeți să tastați...", - "unknownSize": "Dimensiune necunoscută", - "fileIsEmpty": "Fișierul este gol", - "largeFileWarning": "Avertisment pentru fișiere mari", - "largeFileWarningDesc": "Acest fișier este {{size}} în dimensiune, ceea ce poate cauza probleme de performanță când este deschis ca text.", - "fileNotFoundAndRemoved": "Fișierul \"{{name}}\" nu a fost găsit și a fost eliminat din fișiere recente/fixate", - "failedToLoadFile": "Eroare la încărcarea fișierului: {{error}}", - "serverErrorOccurred": "Eroare de server. Te rugăm să încerci din nou mai târziu.", - "autoSaveFailed": "Salvarea automată a eșuat", - "fileAutoSaved": "Fișier salvat automat", - "moveFileFailed": "Nu s-a reușit mutarea {{name}}", - "moveOperationFailed": "Operațiune de mutare eșuată", - "canOnlyCompareFiles": "Se pot compara doar două fișiere", - "comparingFiles": "Comparare fișiere: {{file1}} și {{file2}}", - "dragFailed": "Operațiune de tragere eșuată", - "filePinnedSuccessfully": "Fişier \"{{name}}\" fixat cu succes", - "pinFileFailed": "Nu s-a reușit fixarea fișierului", - "fileUnpinnedSuccessfully": "Fişier \"{{name}}\" desprins cu succes", - "unpinFileFailed": "Nu s-a putut anula fixarea fișierului", - "shortcutAddedSuccessfully": "Scurtătură folder \"{{name}}\" adăugat cu succes", - "addShortcutFailed": "Adăugarea de comenzi rapide a eșuat", - "operationCompletedSuccessfully": "{{operation}} {{count}} elemente cu succes", - "operationCompleted": "{{operation}} {{count}} elemente", - "downloadFileSuccess": "Fișier {{name}} descărcat cu succes", - "downloadFileFailed": "Descărcare eșuată", - "moveTo": "Mutați în {{name}}", - "diffCompareWith": "Diff comparativ cu {{name}}", - "dragOutsideToDownload": "Trage fereastra afară pentru a descărca ({{count}} fișiere)", - "newFolderDefault": "Dosar nou", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Au fost mutate cu succes {{count}} elemente în {{target}}", - "move": "Mutare", - "searchInFile": "Căutare în fișier (Ctrl+F)", - "showKeyboardShortcuts": "Afișare scurtături tastatură", - "startWritingMarkdown": "Începe să scrii conținutul markdown...", - "loadingFileComparison": "Se încarcă compararea fișierelor...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Compară", - "sideBySide": "Listă lângă laterală", + "compare": "Compare", + "sideBySide": "Side by Side", "inline": "Inline", - "fileComparison": "Comparare fișiere: {{file1}} vs {{file2}}", - "fileTooLarge": "Fișier prea mare: {{error}}", - "sshConnectionFailed": "Conexiunea SSH a eșuat. Verificați conexiunea la {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Eroare la încărcarea fișierului: {{error}}", - "connecting": "Conectare...", - "connectedSuccessfully": "Conectat cu succes", - "totpVerificationFailed": "Verificarea TOTP a eșuat", - "warpgateVerificationFailed": "Autentificarea cu teleportare eșuată", - "authenticationFailed": "Autentificare eșuată", - "verificationCodePrompt": "Cod de verificare:", - "changePermissions": "Schimbă Permisiunile", - "currentPermissions": "Permisiuni curente", - "owner": "Proprietar", - "group": "Grup", - "others": "Altele", - "read": "Citește", - "write": "Scrie", - "execute": "Execută", - "permissionsChangedSuccessfully": "Permisiuni schimbate cu succes", - "failedToChangePermissions": "Schimbarea permisiunilor a eșuat", - "name": "Nume", - "sortByName": "Nume", - "sortByDate": "Data modificării", - "sortBySize": "Dimensiune", - "ascending": "Crescător", - "descending": "Descrescător", - "root": "Rădăcină", - "new": "Nou", - "sortBy": "Sortează după", - "items": "Articole", - "selected": "Selectate", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", "editor": "Editor", "octal": "Octal", - "storage": "Depozitare", - "disk": "Disc", - "used": "Utilizat", - "of": "din", - "toggleSidebar": "Comută bara laterală", - "cannotLoadPdf": "Nu se poate încărca PDF", - "pdfLoadError": "A apărut o eroare la încărcarea acestui fișier PDF.", - "loadingPdf": "Se încarcă PDF...", - "loadingPage": "Se încarcă pagina..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Copiați către gazdă…", - "moveToHost": "Mutare la gazdă…", - "copyItemsToHost": "Copiați elementele {{count}} pe gazda…", - "moveItemsToHost": "Mută elementele {{count}} către gazda…", - "noHostsConnected": "Nu sunt disponibile alte gazde de gestionare a fișierelor.", - "noHostsConnectedHint": "Adăugați o altă gazdă SSH cu File Manager activat în Host Manager.", - "selectDestinationHost": "Selectați gazda de destinație", - "destinationPath": "Calea de destinație", - "recentDestinations": "Destinații recente", - "collapseRecentDestinations": "Restrângeți destinațiile recente", - "expandRecentDestinations": "Extindeți destinațiile recente", - "browseFolders": "Răsfoiți folderele de destinație", - "browseDestination": "Răsfoiți sau introduceți calea", - "confirmCopy": "Copie", - "confirmMove": "Mişcare", - "transferring": "Transferarea…", - "compressing": "Comprimarea…", - "extracting": "Extragerea…", - "transferringItems": "Se transferă {{current}} din {{total}} articole…", - "transferSuccess": "Transfer finalizat", - "transferError": "Transferul a eșuat", - "transferPartial": "Transfer finalizat cu erori {{count}}", - "transferPartialHint": "Nu s-a putut transfera: {{paths}}", - "itemsSummary": "{{count}} articole", - "destMustBeDirectory": "Destinația trebuie să fie un director pentru transferurile cu mai multe elemente", - "selectThisFolder": "Selectați acest folder", - "browsePathWillBeCreated": "Acest folder nu există încă. Va fi creat când va începe transferul.", - "browsePathError": "Nu s-a putut deschide această cale pe gazda destinație.", - "goUp": "Mergi sus", - "copyFolderToHost": "Copiați folderul pe gazdă…", - "moveFolderToHost": "Mută folderul pe gazdă…", - "hostReady": "Gata", - "hostConnecting": "Conectare…", - "hostDisconnected": "Neconectat", - "hostAuthRequired": "Autentificare necesară — deschideți mai întâi File Manager pe această gazdă", - "hostConnectionFailed": "Conexiunea a eșuat", - "metricsTitle": "Orele de transfer", - "metricsPrepare": "Pregătiți destinația: {{duration}}", - "metricsCompress": "Comprimare pe sursă: {{duration}}", - "metricsHopSourceRead": "Sursă → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → destinație (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → destinație (locală): {{throughput}}", - "metricsTransfer": "De la un capăt la altul: {{throughput}} ({{duration}})", - "metricsExtract": "Extras la destinație: {{duration}}", - "metricsSourceDelete": "Eliminare din sursă: {{duration}}", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", "metricsTotal": "Total: {{duration}}", - "progressCompressing": "Comprimare pe gazda sursă…", - "progressExtracting": "Extragerea la destinație…", - "progressTransferring": "Transferul datelor…", - "progressReconnecting": "Reconectare…", - "parallelSegmentsLabel": "Benzi de transfer paralele", - "parallelSegmentsOption": "{{count}} benzi", - "parallelSegmentsHint": "Fișierele mari sunt împărțite în blocuri de 256 MB. Mai multe benzi utilizează conexiuni separate (cum ar fi pornirea mai multor transferuri) pentru un debit total mai mare.", - "progressTotalSpeed": "{{speed}} total ({{lanes}} benzi)", - "progressTransferringItems": "Transferarea fișierelor ({{current}} din {{total}})…", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} fișiere", - "sourceNotDeletedPartial": "Fișiere sursă păstrate (transfer parțial)", - "jumpHostLimitation": "Ambele gazde trebuie să fie accesibile de pe serverul Termix. Rutarea directă de la gazdă la gazdă nu este acceptată.", - "cancel": "Anula", - "methodLabel": "Metoda de transfer", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", "methodAuto": "Auto", - "methodTar": "Arhivă Tar", - "methodItemSftp": "SFTP per fișier", - "methodAutoHint": "Alege SFTP tar sau per fișier în funcție de numărul de fișiere, dimensiune și compresibilitate. Fișierele individuale utilizează întotdeauna SFTP în flux continuu.", - "methodTarHint": "Comprimare la sursă, transfer o arhivă, extragere la destinație. Necesită tar pe ambele gazde Unix.", - "methodItemSftpHint": "Transferă fiecare fișier individual prin SFTP. Funcționează pe toate gazdele, inclusiv Windows.", - "methodPreviewLoading": "Metoda de calcul a transferului…", - "methodPreviewError": "Nu s-a putut previzualiza metoda de transfer. Serverul va alege în continuare o metodă când porniți.", - "methodPreviewWillUseTar": "Se va folosi: arhiva Tar", - "methodPreviewWillUseItemSftp": "Se va utiliza: SFTP per fișier", - "methodPreviewScanSummary": "{{fileCount}} fișiere, {{totalSize}} în total (scanate pe gazda sursă).", - "methodItemSftpLimitation": "Fiecare fișier folosește același flux SFTP ca o copie a unui singur fișier, una după alta. Progresul este combinat în toate fișierele, astfel încât bara se mișcă lent în timpul fișierelor mari.", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Ai ales SFTP per fișier.", - "user_tar": "Ai ales arhiva tar.", - "tar_unavailable": "Tar nu este disponibil pe una sau ambele gazde — se va folosi în schimb SFTP per fișier.", - "windows_host": "Este implicată o gazdă Windows — nu se folosește fișierul tar.", - "auto_multi_large": "Automat: fișiere multiple, inclusiv un fișier mare ({{largestSize}}) cu date compresibile — pachete tar într-un singur transfer.", - "auto_single_large_in_archive": "Automat: un fișier mare ({{largestSize}}) în acest set — SFTP per fișier.", - "auto_many_incompressible": "Automat: date în mare parte incompresibile — SFTP per fișier.", - "auto_many_files": "Auto: multe fișiere ({{fileCount}}) — tar reduce costul per fișier.", - "auto_default": "Automat: SFTP per fișier pentru acest set." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Anula", - "progressCancelling": "Anulare…", - "progressStalled": "Blocat", - "resumedHint": "Reconectare la un transfer activ început într-o altă fereastră.", - "transferCancelled": "Transfer anulat", - "transferCancelledCopyHint": "Fișiere parțiale pot rămâne în destinație.", - "transferCancelledMoveHint": "Fișiere parțiale pot rămâne în destinație. Fișierele sursă nu au fost eliminate.", - "cleanupDestFiles": "Destinația de curățare", - "cleanupDestFilesSuccess": "Fișiere parțiale eliminate din destinație", - "cleanupDestFilesPartial": "Unele fișiere parțiale nu au putut fi eliminate", - "cleanupDestFilesNothing": "Nimic de curățat la destinație", - "cleanupDestFilesError": "Curățarea a eșuat", - "retryTransfer": "Reîncercați", - "retryTransferError": "Reîncercare eșuată", - "transferFailedRetryHint": "Date parțiale au fost păstrate la destinație. Reîncercarea va fi reluată după restabilirea conexiunii." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Fără tuneluri SSH", - "createFirstTunnelMessage": "Nu ai creat încă nici un tunel SSH. Configurați conexiunile tunelului în Managerul de Gazdă pentru a începe.", - "connected": "Conectat", - "disconnected": "Deconectat", - "connecting": "Conectare...", - "error": "Eroare", - "canceling": "Anulare...", - "connect": "Conectează-te", - "disconnect": "Deconectare", - "cancel": "Anulează", - "port": "Portul", - "localPort": "Port local", - "remotePort": "Port la distanță", - "currentHostPort": "Portul Gazdei Curente", - "endpointPort": "Port final", - "bindIp": "IP local", - "endpointSshConfig": "Configurare SSH final", - "endpointSshHost": "Sfârșit gazdă SSH", - "endpointSshHostPlaceholder": "Selectați un gazdă configurat", - "endpointSshHostRequired": "Selectați o gazdă SSH pentru fiecare tunel client.", - "attempt": "Încercare de {{current}} de {{max}}", - "nextRetryIn": "Următoarea reîncercare în {{seconds}} secunde", - "clientTunnels": "Tuneluri client", - "clientTunnel": "Tunelul clientului", - "addClientTunnel": "Adaugă Tunel Client", - "noClientTunnels": "Nu există tuneluri client configurate pe acest desktop.", - "tunnelName": "Nume tunel", - "remoteHost": "Gazdă la distanță", - "autoStart": "Pornire automată", - "clientAutoStartDesc": "Începe atunci când acest client pentru desktop se deschide și rămâne conectat.", - "clientManualStartDesc": "Folosiți Start și Stop de la acest rând. Termixul nu îl va deschide automat.", - "clientRemoteServerNote": "Redirecționarea la distanță poate necesita AllowTcpForwarding și GatewayPorts pe serverul SSH final. Portul de la distanță se închide atunci când acest desktop se deconectează.", - "clientTunnelStarted": "Tunelul clientului a început", - "clientTunnelStopped": "Tunelul clientului oprit", - "tunnelTestSucceeded": "Test tunel reușit", - "tunnelTestFailed": "Testul tunelului a eșuat", - "localSaved": "Tuneluri client salvate", - "localSaveError": "Salvarea tunelurilor client locale nu a reușit", - "invalidBindIp": "IP-ul local trebuie să fie o adresă IPv4 validă.", - "invalidLocalTargetIp": "IP-ul ţintă local trebuie să fie o adresă IPv4 validă.", - "invalidLocalPort": "Portul local trebuie să aibă între 1 şi 65535.", - "invalidRemotePort": "Portul la distanță trebuie să fie între 1 și 65535.", - "invalidLocalTargetPort": "Portul ţintă local trebuie să fie între 1 şi 65535.", - "invalidEndpointPort": "Portul punctului final trebuie să fie cuprins între 1 și 65535.", - "duplicateAutoStartBind": "Doar un singur tunel de pornire automată poate utiliza {{bind}}.", - "manualControlError": "Actualizarea stării tunelului a eșuat.", - "active": "Activ", - "start": "Pornire", - "stop": "Oprește", - "test": "Testare", - "type": "Tip tunel", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", "typeLocal": "Local (-L)", - "typeRemote": "Distanta (-R)", - "typeDynamic": "Dinamic (-D)", - "typeServerLocalDesc": "Până la punctul final gazda curentă.", - "typeServerRemoteDesc": "Punct final înapoi la gazda curentă.", - "typeClientLocalDesc": "Localizarea calculatorului este finalizată.", - "typeClientRemoteDesc": "Punct final înapoi la calculatorul local.", - "typeClientDynamicDesc": "SOCKS pe calculatorul local.", - "typeDynamicDesc": "Transmitere trafic SOCKS5 CONNECT prin SSH", - "forwardDescriptionServerLocal": "Actuala gazdă {{sourcePort}} → final {{endpointPort}}.", - "forwardDescriptionServerRemote": "Sfârşit {{endpointPort}} → Gazda curentă {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS pe gazda curentă {{sourcePort}}.", - "forwardDescriptionClientLocal": "{{sourcePort}} → {{endpointPort}} telecomandă.", - "forwardDescriptionClientRemote": "{{sourcePort}} → {{endpointPort}} local.", - "forwardDescriptionClientDynamic": "SOCKS pentru portul local {{sourcePort}}.", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "{{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → {{localPort}} local", - "autoNameClientDynamic": "SOCKS {{localPort}} prin {{endpoint}}", - "route": "Rută:", - "lastStarted": "Ultima dată început", - "lastTested": "Ultimul test", - "lastError": "Ultima eroare", - "maxRetries": "Repetări maxime", - "maxRetriesDescription": "Numărul maxim de încercări de reîncercare.", - "retryInterval": "Interval de reîncercare (secunde)", - "retryIntervalDescription": "Timpul de asteptare intre incercarile de reincercare.", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", "local": "Local", - "remote": "Distanta", - "destination": "Destinație", - "host": "Gazdă", - "mode": "Mod", - "noHostSelected": "Niciun gazdă selectat", - "working": "Procesare..." + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "procesor", - "memory": "Memorie", - "disk": "Disc", - "network": "Rețea", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", "uptime": "Uptime", - "processes": "Procese", - "available": "Disponibil", - "free": "Gratuit", - "connecting": "Conectare...", - "connectionFailed": "Conectarea la server a eșuat", - "naCpus": "N/un procesor", - "cpuCores_one": "{{count}} Nucleu", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Utilizare procesor", - "memoryUsage": "Utilizare memorie", - "diskUsage": "Utilizare disc", - "failedToFetchHostConfig": "Preluarea configuratiei gazdei a esuat", - "serverOffline": "Server offline", - "cannotFetchMetrics": "Valorile nu pot fi preluate de pe serverul offline", - "totpFailed": "Verificarea TOTP a eșuat", - "noneAuthNotSupported": "Statisticile serverului nu acceptă tipul de autentificare 'non'.", - "load": "Încărcare", - "systemInfo": "Informatii Sistem", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Sistem de operare", - "kernel": "Nucleu", - "seconds": "secunde", - "networkInterfaces": "Interfețe de rețea", - "noInterfacesFound": "Nu s-au găsit interfețe de rețea", - "noProcessesFound": "Nici un proces găsit", - "loginStats": "Statistici autentificare SSH", - "noRecentLoginData": "Nu există date recente de conectare", - "executingQuickAction": "Executând {{name}}...", - "quickActionSuccess": "{{name}} completat cu succes", - "quickActionFailed": "{{name}} a eșuat", - "quickActionError": "Eșec la executarea {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Ascultarea porturilor", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Portul", - "address": "Adresa", - "process": "Procesare", - "noData": "Date din porturi care nu sunt ascultate" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { "title": "Firewall", - "inactive": "Inactiv", - "policy": "Politică", - "rules": "reguli", - "noData": "Nu sunt disponibile date firewall-uri", - "action": "Acțiune", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Portul", - "source": "Sursa", - "anywhere": "Oriunde" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Încărcare medie", - "swap": "Schimbă", - "architecture": "Arhitectură", - "refresh": "Împrospătează", - "retry": "Reîncercați" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Gestionare SSH și desktop la distanță auto-găzduită", - "loginTitle": "Autentificare în Termix", - "registerTitle": "Creare cont", - "forgotPassword": "Parolă uitată?", - "rememberMe": "Ține minte dispozitivul pentru 30 de zile (include TOTP)", - "noAccount": "Nu ai un cont?", - "hasAccount": "Ai deja un cont?", - "twoFactorAuth": "Autentificare în doi pași", - "enterCode": "Introduceți codul de verificare", - "backupCode": "Sau folosește codul backup-ului", - "verifyCode": "Verifică codul", - "redirectingToApp": "Redirecționare către aplicație...", - "sshAuthenticationRequired": "Autentificare SSH necesară", - "sshNoKeyboardInteractive": "Autentificare cu tastatură indisponibilă", - "sshAuthenticationFailed": "Autentificare eșuată", - "sshAuthenticationTimeout": "Timp de autentificare", - "sshNoKeyboardInteractiveDescription": "Serverul nu acceptă autentificare interactivă tastatură. Vă rugăm să furnizați parola sau cheia SSH.", - "sshAuthFailedDescription": "Acreditările furnizate au fost incorecte. Te rugăm să încerci din nou cu acreditări valide.", - "sshTimeoutDescription": "Încercarea de autentificare a expirat. Încercați din nou.", - "sshProvideCredentialsDescription": "Te rugăm să introduci datele tale de autentificare SSH pentru a te conecta la acest server.", - "sshPasswordDescription": "Introduceți parola pentru această conexiune SSH.", - "sshKeyPasswordDescription": "Dacă cheia SSH este criptată, introduceți parola de acces aici.", - "passphraseRequired": "Parolă necesară", - "passphraseRequiredDescription": "Cheia SSH este criptată. Introduceți parola de acces pentru a o debloca.", - "back": "Înapoi", - "firstUser": "Primul utilizator", - "firstUserMessage": "Sunteți primul utilizator și va fi făcut administrator. Puteți vedea setările de administrare în meniul dropdown. Dacă credeți că este o greșeală, verificați jurnalele de andocare, sau creați o problemă GitHub.", - "external": "Extern", - "loginWithExternal": "Autentificare cu furnizor extern", - "loginWithExternalDesc": "Autentifică-te folosind furnizorul de identitate extern configurat", - "externalNotSupportedInElectron": "Autentificarea externă nu este încă acceptată în aplicația Electron. Vă rugăm să folosiți versiunea web pentru autentificarea OIDC.", - "resetPasswordButton": "Resetare parolă", - "sendResetCode": "Trimite codul de resetare", - "resetCodeDesc": "Introdu numele tău de utilizator pentru a primi un cod de resetare a parolei. Codul va fi autentificat în jurnalele containerelor de andocare.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verifică codul", - "enterResetCode": "Introdu codul de 6 cifre din jurnalele containerelor de andocare pentru utilizator:", - "newPassword": "Parolă nouă", - "confirmNewPassword": "Confirmare parolă", - "enterNewPassword": "Introduceți noua parolă pentru utilizator:", - "signUp": "Înscrie-te", - "desktopApp": "Aplicație desktop", - "loggingInToDesktopApp": "Conectare la aplicația desktop", - "loadingServer": "Se încarcă serverul...", - "dataLossWarning": "Resetarea parolei în acest mod va șterge toate gazdele SSH salvate, credențialele și alte date criptate. Această acțiune nu poate fi anulată. Folosiți doar dacă ați uitat parola și nu sunteți conectat.", - "authenticationDisabled": "Autentificare dezactivată", - "authenticationDisabledDesc": "Toate metodele de autentificare sunt momentan dezactivate. Vă rugăm să contactaţi administratorul.", - "attemptsRemaining": "{{count}} încercări rămase" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verificare cheie gazdă SSH", - "keyChangedWarning": "Cheie gazdă SSH schimbată", - "firstConnectionTitle": "Conectare pentru prima dată la această gazdă", - "firstConnectionDescription": "Autenticitatea acestui gazdă nu poate fi stabilită. Verificați ca amprenta să corespundă cu ceea ce așteptați.", - "keyChangedDescription": "Cheia gazdă a acestui server s-a schimbat de la ultima conexiune. Aceasta ar putea indica o problemă de securitate.", - "previousKey": "Cheie anterioară", - "newFingerprint": "Amprentă nouă", - "fingerprint": "Amprentă", - "verifyInstructions": "Dacă aveți încredere în această gazdă, faceți clic pe Accept pentru a continua și a salva amprenta pentru conexiunile viitoare.", - "securityWarning": "Avertizare de securitate", - "acceptAndContinue": "Acceptați și continuați", - "acceptNewKey": "Acceptați o nouă cheie și continuați" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Nu s-a putut conecta la baza de date", - "unknownError": "Eroare necunoscută", - "loginFailed": "Autentificare eșuată", - "failedPasswordReset": "Nu s-a reușit inițializarea resetării parolei", - "failedVerifyCode": "Verificarea codului de resetare a eșuat", - "failedCompleteReset": "Finalizarea resetării parolei a eșuat", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Pornirea autentificării OIDC a eșuat", - "silentSigninOidcUnavailable": "Autentificare silențioasă a fost solicitată, dar autentificarea OIDC nu este disponibilă.", - "failedUserInfo": "Obținerea informațiilor utilizatorului a eșuat după autentificare", - "oidcAuthFailed": "Autentificarea OIDC a eșuat", - "invalidAuthUrl": "URL autorizare invalid primit de la backend", - "requiredField": "Acest câmp este necesar", - "minLength": "Lungimea minimă este {{min}}", - "passwordMismatch": "Parolele nu corespund", - "passwordLoginDisabled": "Numele de utilizator/parola este dezactivat în prezent", - "sessionExpired": "Sesiunea a expirat - vă rugăm să vă autentificați din nou", - "totpRateLimited": "Evaluare limitată: Prea multe încercări de verificare TOTP. Vă rugăm să încercaţi din nou mai târziu.", - "totpRateLimitedWithTime": "Evaluare limitată: Prea multe încercări de verificare TOTP. Vă rugăm să așteptați {{time}} secunde înainte de a încerca din nou.", - "resetCodeRateLimited": "Evaluare limitată: Prea multe încercări de verificare. Vă rugăm să încercați din nou mai târziu.", - "resetCodeRateLimitedWithTime": "Evaluare limitată: Prea multe încercări de verificare. Vă rugăm să așteptați {{time}} secunde înainte de a încerca din nou.", - "authTokenSaveFailed": "Salvarea token-ului de autentificare a eșuat", - "failedToLoadServer": "Încărcarea serverului a eșuat" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Înregistrarea unui cont nou este momentan dezactivată de un administrator. Vă rugăm să vă autentificați sau să contactați un administrator.", - "userNotAllowed": "Contul dvs. nu este autorizat să se înregistreze. Vă rugăm să contactaţi un administrator.", - "databaseConnectionFailed": "Conectarea la serverul bazei de date a eșuat", - "resetCodeSent": "Cod de resetare trimis la jurnalele Docker", - "codeVerified": "Cod verificat cu succes", - "passwordResetSuccess": "Parola de resetare cu succes", - "loginSuccess": "Autentificare reușită", - "registrationSuccess": "Înregistrare reușită" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Tuneluri locale pentru desktop care vizează gazdele SSH configurate.", - "c2sTunnelPresets": "Setările Tunelului Client", - "c2sTunnelPresetsDesc": "Salvați lista locală de tunel a acestui client desktop ca o anumită presetare de server sau încărcați o presetare înapoi în acest client.", - "c2sTunnelPresetsUnavailable": "Setările de presetare ale clientului sunt disponibile doar în clientul desktop.", - "c2sPresetName": "Nume presetare", - "c2sPresetNamePlaceholder": "Numele prestabilit al clientului", - "c2sPresetToLoad": "Presetare la încărcare", - "c2sNoPresetSelected": "Nicio presetare selectată", - "c2sNoPresets": "Nicio presetare salvată", - "c2sLoadPreset": "Încărcare", - "c2sCurrentLocalConfig": "{{count}} local client tunel configurat pe acest desktop.", - "c2sPresetSyncNote": "Presetarile sunt instantanee explicite; incarcarea uneia inlocuieste aceasta lista locala de clienti clienti din lista locala.", - "c2sPresetSaved": "Presetare client tunel salvat", - "c2sPresetLoaded": "Setul de presetare al clientului încărcat local", - "c2sPresetRenamed": "Setul prestabilit al clientului a fost redenumit", - "c2sPresetDeleted": "Setare presetare client ștearsă", - "c2sPresetLoadError": "Încărcarea presetărilor client tunel a eșuat" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Limba", - "keyPassword": "parola cheii", - "pastePrivateKey": "Lipiți cheia privată aici...", - "localListenerHost": "127.0.0,1 (ascultați local)", - "localTargetHost": "127.0.0,1 (țintă pe acest calculator)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Introduceți parola", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Panou", - "loading": "Se încarcă tabloul de bord...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Suport", + "support": "Support", "discord": "Discord", - "serverOverview": "Vizualizare server", - "version": "Versiune", - "upToDate": "Până la data", - "updateAvailable": "Actualizare disponibilă", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", "uptime": "Uptime", - "database": "Baza de date", - "healthy": "sănătos", - "error": "Eroare", - "totalHosts": "Total gazde", - "totalTunnels": "Total tuneluri", - "totalCredentials": "Total acreditări", - "recentActivity": "Activitate recentă", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Se încarcă activitatea recentă...", - "noRecentActivity": "Nicio activitate recentă", - "quickActions": "Acțiuni rapide", - "addHost": "Adaugă Gazdă", - "addCredential": "Adaugă acreditare", - "adminSettings": "Setări Admin", - "userProfile": "Profil utilizator", - "serverStats": "Statistici server", - "loadingServerStats": "Se încarcă statisticile serverului...", - "noServerData": "Nu sunt disponibile date de server", - "cpu": "procesor", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Personalizează tabloul de bord", - "dashboardSettings": "Setări tablou de bord", - "enableDisableCards": "Activează/Dezactivează Cardurile", - "resetLayout": "Resetare la valorile implicite", - "serverOverviewCard": "Vizualizare server", - "recentActivityCard": "Activitate recentă", - "networkGraphCard": "Grafic rețea", - "networkGraph": "Grafic rețea", - "quickActionsCard": "Acțiuni rapide", - "serverStatsCard": "Statistici server", - "panelMain": "Principal", - "panelSide": "Lateral", - "justNow": "chiar acum" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABILE", - "hostsOnline": "Gazde Online", - "activeTunnels": "Tuneluri active", - "registerNewServer": "Înregistrează un nou server", - "storeSshKeysOrPasswords": "Stocare chei SSH sau parole", - "manageUsersAndRoles": "Gestionează utilizatorii și rolurile", - "manageYourAccount": "Gestionează-ți contul", - "hostStatus": "Starea Gazdei", - "noHostsConfigured": "Nicio gazdă configurată", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Adăugaţi:", - "commandPalette": "Paletă de comandă", - "done": "Terminat", - "editModeInstructions": "Trage cardurile pentru a reordona · Trage separatorul de coloană pentru a redimensiona coloane · Trage marginea de jos a unui card pentru a-i redimensiona înălțimea · Gunoi pentru a elimina", - "empty": "Golire", - "clear": "Curăță" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Adaugă Gazdă", - "addGroup": "Adaugă grup", - "addLink": "Adaugă link", - "zoomIn": "Mărire în", - "zoomOut": "Micșorare", - "resetView": "Resetare vizualizare", - "selectHost": "Selectați Gazda", - "chooseHost": "Alege o gazdă...", - "parentGroup": "Grup părinte", - "noGroup": "Niciun grup", - "groupName": "Nume grup", - "color": "Culoare", - "source": "Sursa", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Mutare în grup", - "selectGroup": "Selectați grupul...", - "addConnection": "Adaugă conexiune", - "hostDetails": "Detalii Gazdă", - "removeFromGroup": "Elimină din grup", - "addHostHere": "Adaugă gazdă aici", - "editGroup": "Editează grup", - "delete": "Ștergere", - "add": "Adăugare", - "create": "Crează", - "move": "Mutare", - "connect": "Conectează-te", - "createGroup": "Creare grup", - "selectSourcePlaceholder": "Selectează sursa...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Fișier nevalid", - "hostAlreadyExists": "Gazda este deja în topologie", - "connectionExists": "Conexiunea există deja", - "unknown": "Necunoscut", - "name": "Nume", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", "status": "Status", - "failedToAddNode": "Adăugarea nodului a eșuat", - "sourceDifferentFromTarget": "Sursa și ținta trebuie să fie diferite", - "exportJSON": "Exportă JSON", - "importJSON": "Importă JSON", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Manager fişiere", - "tunnel": "Tunel", - "docker": "Doctor", - "serverStats": "Statistici server", - "noNodes": "Nu există încă noduri" + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Dockerul nu este activat pentru această gazdă", - "validating": "Se validează Doctorul...", - "connecting": "Conectare...", - "error": "Eroare", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Conectarea la Docker a eșuat", - "containerStarted": "Container {{name}} a început", - "failedToStartContainer": "Pornirea containerului {{name}} a eșuat", - "containerStopped": "Container {{name}} oprit", - "failedToStopContainer": "Nu s-a putut opri containerul {{name}}", - "containerRestarted": "Container {{name}} repornit", - "failedToRestartContainer": "Repornirea containerului {{name}} a eșuat", - "containerPaused": "Containerul {{name}} a fost întrerupt", - "containerUnpaused": "Container {{name}} neîntrerupt", - "failedToTogglePauseContainer": "Comutarea comutării întreruperii pentru containerul {{name}}", - "containerRemoved": "Container {{name}} eliminat", - "failedToRemoveContainer": "Nu s-a putut elimina containerul {{name}}", - "image": "Imagine", - "ports": "Porturi", - "noPorts": "Nu există porturi", - "start": "Pornire", - "confirmRemoveContainer": "Sunteți sigur că doriți să eliminați containerul '{{name}}'? Această acțiune nu poate fi anulată.", - "runningContainerWarning": "Avertisment: Acest recipient rulează. Scoaterea va opri mai întâi containerul.", - "loadingContainers": "Se încarcă containerele...", - "manager": "Managerul Docker", - "autoRefresh": "Reîmprospătare automată", - "timestamps": "Marcaje de timp", - "lines": "Linii", - "filterLogs": "Filtrează jurnalele...", - "refresh": "Împrospătează", - "download": "Descărcare", - "clear": "Curăță", - "logsDownloaded": "Loguri descărcate cu succes", - "last50": "Ultimele 50", - "last100": "Ultimele 100", - "last500": "Ultimele 500", - "last1000": "Ultimele 1000", - "allLogs": "Toate jurnalele", - "noLogsMatching": "Niciun jurnal se potrivește \"{{query}}\"", - "noLogsAvailable": "Niciun jurnal disponibil", - "noContainersFound": "Nici un container găsit", - "noContainersFoundHint": "Nu sunt containere docker disponibile pe această gazdă", - "searchPlaceholder": "Căutare containere...", - "allStatuses": "Toate stările", - "stateRunning": "Rulează", - "statePaused": "Pauză", - "stateExited": "Ieşit", - "stateRestarting": "Repornire", - "noContainersMatchFilters": "Niciun container nu se potrivește cu filtrele tale", - "noContainersMatchFiltersHint": "Încercați să ajustați criteriile de căutare sau filtrare", - "failedToFetchStats": "Preluarea statisticilor containerelor a eșuat", - "containerNotRunning": "Containerul nu rulează", - "startContainerToViewStats": "Începeți containerul pentru a vizualiza statisticile", - "loadingStats": "Se încarcă statisticile...", - "errorLoadingStats": "Eroare la încărcarea statisticilor", - "noStatsAvailable": "Nu există statistici disponibile", - "cpuUsage": "Utilizare procesor", - "current": "Actuală", - "memoryUsage": "Utilizare memorie", - "networkIo": "Rețea I/O", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Ieșire", - "blockIo": "Blochează I/O", - "read": "Citește", - "write": "Scrie", - "pids": "PID-uri", - "containerInformation": "Informații despre container", - "name": "Nume", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Județ", - "containerMustBeRunning": "Containerul trebuie să ruleze pentru a accesa consola", - "verificationCodePrompt": "Introduceți codul de verificare", - "totpVerificationFailed": "Verificarea TOTP a eșuat. Încercați din nou.", - "warpgateVerificationFailed": "Autentificarea cu teleportare a eșuat. Te rugăm să încerci din nou.", - "connectedTo": "Conectat la {{containerName}}", - "disconnected": "Deconectat", - "consoleError": "Eroare de consolă", - "errorMessage": "Eroare: {{message}}", - "failedToConnect": "Conectarea la container a eșuat", - "console": "Consolă", - "selectShell": "Selectează proiectil", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "pânză", - "ash": "cenuşă", - "connect": "Conectează-te", - "disconnect": "Deconectare", - "notConnected": "Nu este conectat", - "clickToConnect": "Faceți clic pe conectare pentru a începe o sesiune de shell", - "connectingTo": "Conectare la {{containerName}}...", - "containerNotFound": "Containerul nu a fost găsit", - "backToList": "Înapoi la listă", - "logs": "Jurnale", - "stats": "Statistici", - "consoleTab": "Consolă", - "startContainerToAccess": "Porniți containerul pentru a accesa consola" + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Generalități", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Utilizatori", - "sectionSessions": "Sesiuni", - "sectionRoles": "Roluri", - "sectionDatabase": "Baza de date", - "sectionApiKeys": "Chei API", - "allowRegistration": "Permite înregistrarea utilizatorului", - "allowRegistrationDesc": "Permite utilizatorilor noi să se înregistreze automat", - "allowPasswordLogin": "Permiteți autentificarea cu parolă", - "allowPasswordLoginDesc": "Nume utilizator/parolă", - "oidcAutoProvision": "Furnizare automată OIDC", - "oidcAutoProvisionDesc": "Creare automată conturi pentru utilizatori OIDC chiar și atunci când înregistrarea este dezactivată", - "allowPasswordReset": "Permiteți resetarea parolei", - "allowPasswordResetDesc": "Resetează codul prin jurnalele Docker", - "sessionTimeout": "Timeout sesiune", - "hours": "ore", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Monitorizarea implicita", - "statusCheck": "Verificare stare", - "metrics": "Valori", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", "sec": "sec", - "logLevel": "Nivel jurnal", - "enableGuacamole": "Activează Guacamol", - "enableGuacamoleDesc": "desktop la distanță RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Configurați OpenID Connect pentru câmpurile marcate * sunt obligatorii.", - "oidcClientId": "ID client", - "oidcClientSecret": "Secret client", - "oidcAuthUrl": "URL-ul de autorizare", - "oidcIssuerUrl": "URL emitent", - "oidcTokenUrl": "URL token", - "oidcUserIdentifier": "Calea identificatorului utilizatorului", - "oidcDisplayName": "Afișează calea numelui", - "oidcScopes": "Domeniu", - "oidcUserinfoUrl": "Suprascrie URL-ul utilizatorului", - "oidcAllowedUsers": "Utilizatori permiși", - "oidcAllowedUsersDesc": "Un e-mail pe linie. Lăsați gol pentru a permite tuturor.", - "removeOidc": "Elimină", - "usersCount": "{{count}} utilizatori", - "createUser": "Crează", - "newRole": "Rol nou", - "roleName": "Nume", - "roleDisplayName": "Nume afișat", - "roleDescription": "Descriere", - "rolesCount": "{{count}} rol", - "createRole": "Crează", - "creating": "Creare...", - "exportDatabase": "Exportă baza de date", - "exportDatabaseDesc": "Descarcă o copie de rezervă a tuturor host-urilor, credențialelor și setărilor", - "export": "Exportă", - "exporting": "Exportare...", - "importDatabase": "Importă baza de date", - "importDatabaseDesc": "Restaurează dintr-un fișier de backup .sqlite", - "importDatabaseSelected": "Selectat: {{name}}", - "selectFile": "Selectaţi fişierul", - "changeFile": "Schimbă", - "import": "Importă", - "importing": "Importare...", - "apiKeysCount": "{{count}} chei", - "newApiKey": "Cheie API nouă", - "apiKeyCreatedWarning": "Cheie creată - copiați-o acum, nu va mai fi afișată din nou.", - "apiKeyName": "Nume", - "apiKeyUser": "Utilizator", - "apiKeySelectUser": "Selectați un utilizator...", - "apiKeyExpiresAt": "Expiră la", - "createKey": "Crează cheie", - "apiKeyNoExpiry": "Fără expirare", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Autentificare dublă", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", "authTypeLocal": "Local", "adminStatusAdministrator": "Administrator", - "adminStatusRegularUser": "Utilizator obișnuit", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "PERSONALIZARE", - "youBadge": "Tu", - "sessionsActive": "{{count}} activ", - "sessionActive": "Activ: {{time}}", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Toate", - "revokeAllSessionsSuccess": "Toate sesiunile pentru utilizator au fost revocate", - "revokeAllSessionsFailed": "Eroare la revocarea sesiunilor", - "revokeSessionFailed": "Eroare la revocarea sesiunii", - "addRole": "Adaugă rol", - "noCustomRoles": "Niciun rol personalizat definit", - "removeRoleFailed": "Nu s-a putut elimina rolul", - "assignRoleFailed": "Atribuirea rolului a eșuat", - "deleteRoleFailed": "Ștergerea rolului a eșuat", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", "userAdminAccess": "Administrator", - "userAdminAccessDesc": "Acces complet la toate setările de administrare", - "userRoles": "Roluri", - "revokeAllUserSessions": "Revocă toate sesiunile", - "revokeAllUserSessionsDesc": "Forțează reautentificarea pe toate dispozitivele", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Ștergerea acestui utilizator este permanentă.", - "deleteUser": "Șterge {{username}}", - "deleting": "Ștergere...", - "deleteUserFailed": "Ștergerea utilizatorului a eșuat", - "deleteUserSuccess": "Utilizator \"{{username}}\" șters", - "deleteRoleSuccess": "Rol \"{{name}}\" şters", - "revokeKeySuccess": "Cheie \"{{name}}\" revocată", - "revokeKeyFailed": "Anularea cheii a eșuat", - "copiedToClipboard": "Copiat în clipboard", - "done": "Terminat", - "createUserTitle": "Creare utilizator", - "createUserDesc": "Creează un nou cont local.", - "createUserUsername": "Nume", - "createUserPassword": "Parolă", - "createUserPasswordHint": "Minim 6 caractere.", - "createUserEnterUsername": "Introdu numele de utilizator", - "createUserEnterPassword": "Introduceți parola", - "createUserSubmit": "Creare utilizator", - "editUserTitle": "Gestionare utilizator: {{username}}", - "editUserDesc": "Editează roluri, stare administrator, sesiuni și setări cont.", - "editUserUsername": "Nume", - "editUserAuthType": "Tip Auth", - "editUserAdminStatus": "Starea Administratorului", - "editUserUserId": "ID Utilizator", - "linkAccountTitle": "Link OIDC la contul de parolă", - "linkAccountDesc": "Îmbinați contul OIDC {{username}} cu un cont local existent.", - "linkAccountWarningTitle": "Acest lucru va:", - "linkAccountEffect1": "Ștergeți contul OIDC-only", - "linkAccountEffect2": "Adăugați autentificarea OIDC în contul țintă", - "linkAccountEffect3": "Permite atât OIDC cât și parola de conectare", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Introduceți numele de utilizator al contului local pentru a vă conecta la", - "linkAccounts": "Conturi de legătură", - "linkAccountSuccess": "Cont OIDC conectat la „{{username}}”", - "linkAccountFailed": "Conectarea contului OIDC a eșuat", - "linkAccountInProgress": "Se conectează...", - "saving": "Salvare...", - "updateRegistrationFailed": "Actualizarea setării de înregistrare a eșuat", - "updatePasswordLoginFailed": "Actualizarea parolei de conectare a eșuat", - "updateOidcAutoProvisionFailed": "Actualizarea setării de autofurnizare OIDC a eșuat", - "updatePasswordResetFailed": "Actualizarea setării de resetare a parolei a eșuat", - "sessionTimeoutRange2": "Perioada de expirare a sesiunii trebuie să fie între 1 şi 720 ore", - "sessionTimeoutSaved": "Sepire sesiune salvată", - "sessionTimeoutSaveFailed": "Salvarea sesiunii a eșuat", - "monitoringIntervalInvalid": "Valori interval nevalid", - "monitoringSaved": "Setări de monitorizare salvate", - "monitoringSaveFailed": "Salvarea setărilor de monitorizare a eșuat", - "guacamoleSaved": "Setări Guacamole salvate", - "guacamoleSaveFailed": "Salvarea setărilor Guacamole a eșuat", - "guacamoleUpdateFailed": "Actualizarea setării Guacamole a eșuat", - "logLevelUpdateFailed": "Actualizarea nivelului de jurnal a eșuat", - "oidcSaved": "Configurație OIDC salvată", - "oidcSaveFailed": "Salvarea configurației OIDC a eșuat", - "oidcRemoved": "Configurație OIDC eliminată", - "oidcRemoveFailed": "Ștergerea configurației OIDC a eșuat", - "createUserRequired": "Numele de utilizator și parola sunt necesare", - "createUserPasswordTooShort": "Parola trebuie să conțină cel puțin 6 caractere", - "createUserSuccess": "Utilizator \"{{username}}\" creat", - "createUserFailed": "Crearea utilizatorului a eșuat", - "updateAdminStatusFailed": "Actualizarea stării de administrare a eșuat", - "allSessionsRevoked": "Toate sesiunile au fost anulate", - "revokeSessionsFailed": "Eroare la revocarea sesiunilor", - "createRoleRequired": "Numele și numele afișat sunt obligatorii", - "createRoleSuccess": "Rol \"{{name}}\" creat", - "createRoleFailed": "Crearea rolului a eșuat", - "apiKeyNameRequired": "Numele cheii este necesar", - "apiKeyUserRequired": "ID-ul de utilizator este necesar", - "apiKeyCreatedSuccess": "API-cheie \"{{name}}\" creat", - "apiKeyCreateFailed": "Crearea cheii API a eșuat", - "exportSuccess": "Baza de date exportată cu succes", - "exportFailed": "Exportarea bazei de date a eșuat", - "importSelectFile": "Vă rugăm să selectaţi mai întâi un fişier", - "importCompleted": "Import finalizat: {{total}} articole importate, {{skipped}} omis", - "importFailed": "Importare eșuată: {{error}}", - "importError": "Importarea bazei de date a eșuat" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Gazdă", - "hostPlaceholder": "192.168.1.1 sau exemplu.com", - "portLabel": "Portul", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Nume", - "usernamePlaceholder": "utilizator", - "authLabel": "Autorizare", - "passwordLabel": "Parolă", - "passwordPlaceholder": "parolă", - "privateKeyLabel": "Cheie privată", - "privateKeyPlaceholder": "Lipiți cheia privată...", - "credentialLabel": "Acreditări", - "credentialPlaceholder": "Selectați o acreditare salvată", - "connectToTerminal": "Conectează-te la Terminal", - "connectToFiles": "Conectează-te la Fișiere" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Niciun terminal selectat", - "noTerminalSelectedHint": "Deschide o fereastră SSH terminal pentru a vedea istoricul comenzilor", - "searchPlaceholder": "Istoricul căutării...", - "clearAll": "Șterge tot", - "noHistoryEntries": "Nici o intrare în istoric", - "trackingDisabled": "Urmărirea istoricului este dezactivată", - "trackingDisabledHint": "Activați-l în setările profilului dvs. pentru a înregistra comenzi." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Înregistrare cheie", - "recordToTerminals": "Înregistrare la terminale", - "selectAll": "Toate", - "selectNone": "Niciunul", - "noTerminalTabsOpen": "Nici o filă terminală deschisă", - "selectTerminalsAbove": "Selectați terminalele de mai sus", - "broadcastInputPlaceholder": "Scrie aici pentru a transmite tastatura...", - "stopRecording": "Oprește înregistrarea", - "startRecording": "Începe înregistrarea", - "settingsTitle": "Setări", - "enableRightClickCopyPaste": "Activează copy/paste clic-dreapta" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Aspect", - "selectLayoutAbove": "Selectaţi un aspect de mai sus", - "selectLayoutHint": "Alege câte panouri să fie afișate", - "panesTitle": "Panouri", - "openTabsTitle": "Deschide filele", - "dragTabsHint": "Trageți filele în panourile de mai sus sau utilizați Atribuire rapidă", - "dropHere": "Plasează aici", - "emptyPane": "Golire", - "dashboard": "Panou", - "clearSplitScreen": "Golește ecranul împărțit", - "quickAssign": "Atribuire rapidă", - "alreadyAssigned": "Panou {{index}}", - "splitTab": "Tabulă divizată", - "addToSplit": "Adăugați la divizare", - "removeFromSplit": "Eliminați din Split", - "assignToPane": "Atribuire panoului" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Creează fragmente", - "createSnippetDescription": "Creați un nou snippet de comenzi pentru execuție rapidă", - "nameLabel": "Nume", - "namePlaceholder": "de exemplu, reporniţi Nginx", - "descriptionLabel": "Descriere", - "descriptionPlaceholder": "Descriere opțională", - "optional": "Opţional", - "folderLabel": "Dosar", - "noFolder": "Niciun dosar (neclasificat)", - "commandLabel": "Comanda", - "commandPlaceholder": "de exemplu, sudo systemctl repornire nginx", - "cancel": "Anulează", - "createSnippetButton": "Creează fragmente", - "createFolderTitle": "Creare folder", - "createFolderDescription": "Organizați fragmentele în dosare", - "folderNameLabel": "Nume folder", - "folderNamePlaceholder": "de ex. Comenzi de sistem, Scripturi Docker", - "folderColorLabel": "Culoare dosar", - "folderIconLabel": "Iconiță Folder", - "previewLabel": "Previzualizare", - "folderNameFallback": "Nume folder", - "createFolderButton": "Creare folder", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Toate", - "selectNone": "Niciunul", - "noTerminalTabsOpen": "Nici o filă terminală deschisă", - "searchPlaceholder": "Caută fragmente...", - "newSnippet": "Fragment nou", - "newFolder": "Dosar nou", - "run": "Rulează", - "noSnippetsInFolder": "Niciun snippets în acest dosar", - "uncategorized": "Neclasificat", - "editSnippetTitle": "Editare fragmente", - "editSnippetDescription": "Actualizează această comandă snippet", - "saveSnippetButton": "Salvează modificările", - "createSuccess": "Fragment creat cu succes", - "createFailed": "Crearea snippet a eșuat", - "updateSuccess": "Fragment actualizat cu succes", - "updateFailed": "Actualizarea snippet a eșuat", - "deleteFailed": "Ștergerea snippet-ului a eșuat", - "folderCreateSuccess": "Folder creat cu succes", - "folderCreateFailed": "Nu s-a reușit crearea dosarului", - "editFolderTitle": "Editare dosar", - "editFolderDescription": "Redenumiți sau schimbați aspectul acestui dosar", - "saveFolderButton": "Salvează modificările", - "editFolder": "Editare dosar", - "deleteFolder": "Ștergere dosar", - "folderDeleteSuccess": "Dosar \"{{name}}\" șters", - "folderDeleteFailed": "Eroare la ștergerea dosarului", - "folderEditSuccess": "Dosar actualizat cu succes", - "folderEditFailed": "Actualizarea dosarului a eșuat", - "confirmRunMessage": "Executați „{{name}}”?", - "confirmRunButton": "Aleargă", - "runSuccess": "Ran \"{{name}}\" în terminalul(ele) {{count}}", - "copySuccess": "A copiat \"{{name}}\" în clipboard", - "shareTitle": "Distribuie fragmentul", - "shareUser": "Utilizator", - "shareRole": "Rol", - "selectUser": "Selectați un utilizator...", - "selectRole": "Selectează un rol...", - "shareSuccess": "Snippet partajat cu succes", - "shareFailed": "Distribuire snippet eșuată", - "revokeSuccess": "Acces revocat", - "revokeFailed": "Eroare la revocarea accesului", - "currentAccess": "Acces curent", - "shareLoadError": "Încărcarea datelor partajate a eșuat", - "loading": "Încărcare...", - "close": "Inchide", - "reorderFailed": "Nu s-a putut salva ordinea fragmentelor." + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Cont", - "sectionAppearance": "Aspectul", - "sectionSecurity": "Securitate", - "sectionApiKeys": "Chei API", - "sectionC2sTunnels": "Tuneluri C2S", - "usernameLabel": "Nume", - "roleLabel": "Rol", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", "roleAdministrator": "Administrator", - "authMethodLabel": "Metoda de autentificare", + "authMethodLabel": "Auth Method", "authMethodLocal": "Local", - "twoFaLabel": "2F", - "twoFaOn": "Activat", - "twoFaOff": "Dezactivat", - "versionLabel": "Versiune", - "deleteAccount": "Ștergere cont", - "deleteAccountDescription": "Șterge permanent contul tău", - "deleteButton": "Ștergere", - "deleteAccountPermanent": "Această acţiune este permanentă şi nu poate fi anulată.", - "deleteAccountWarning": "Toate sesiunile, gazdele, credențialele și setările vor fi șterse definitiv.", - "confirmPasswordDeletePlaceholder": "Introduceți parola pentru a confirma", - "languageLabel": "Limba", - "themeLabel": "Tema", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Culoare accent", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Comanda Autocompletare", - "commandAutocompleteDesc": "Arată autocompletarea în timpul tastării", - "historyTracking": "Urmărire istoric", - "historyTrackingDesc": "Urmăriți comenzile terminalului", - "syntaxHighlighting": "Evidențiere sintaxă", - "syntaxHighlightingDesc": "Evidențiază ieșirea terminalului", - "commandPalette": "Paletă de comandă", - "commandPaletteDesc": "Activează scurtătura pentru tastatură", - "reopenTabsOnLogin": "Redeschideți filele la conectare", - "reopenTabsOnLoginDesc": "Restaurați filele deschise atunci când vă conectați sau reîmprospătați pagina, chiar și de pe un alt dispozitiv", - "confirmTabClose": "Confirmă fila închisă", - "confirmTabCloseDesc": "Întreabă înainte de a închide filele terminale", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Arată etichetele gazdei", - "showHostTagsDesc": "Afișează etichetele în lista de host-uri", - "hostTrayOnClick": "Faceți clic pentru a extinde acțiunile gazdei", - "hostTrayOnClickDesc": "Afișați întotdeauna butoanele de conectare; faceți clic pentru a extinde opțiunile de gestionare în loc să treceți cu mouse-ul peste", - "pinAppRail": "Fixează aplicația Pin Rail", - "pinAppRailDesc": "Mențineți șina aplicației din bara laterală stângă mereu extinsă, în loc să se extindă la trecerea cu mouse-ul peste mouse", - "settingsSnippets": "Snippet-uri", - "foldersCollapsed": "Dosare Restrânse", - "foldersCollapsedDesc": "Restrânge implicit dosarele", - "confirmExecution": "Confirmare execuție", - "confirmExecutionDesc": "Confirmă înainte de a rula snippet-uri", - "settingsUpdates": "Actualizări", - "disableUpdateChecks": "Dezactivează verificările actualizărilor", - "disableUpdateChecksDesc": "Opriți verificarea pentru actualizări", - "totpAuthenticator": "Autentificator TOTP", - "totpEnabled": "2FA este activat", - "totpDisabled": "Adăugare securitate suplimentară autentificare", - "disable": "Dezactivează", - "enable": "Activare", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Scanați codul QR sau introduceți secret în aplicația de autentificare, apoi introduceți codul de 6 cifre", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifică", - "changePassword": "Schimbare parolă", - "currentPasswordLabel": "Parola curentă", - "currentPasswordPlaceholder": "Parola curentă", - "newPasswordLabel": "Parolă nouă", - "newPasswordPlaceholder": "Parola nouă", - "confirmPasswordLabel": "Confirmare parolă nouă", - "confirmPasswordPlaceholder": "Confirmare parolă nouă", - "updatePassword": "Actualizare parolă", - "createApiKeyTitle": "Creare cheie API", - "createApiKeyDescription": "Generează o nouă cheie API pentru acces programatic.", - "apiKeyNameLabel": "Nume", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "opţional", - "cancel": "Anulează", - "createKey": "Crează cheie", - "apiKeyCount": "{{count}} chei", - "newKey": "Cheie nouă", - "noApiKeys": "Nici o cheie API încă.", - "apiKeyActive": "Activ", - "apiKeyUsageHint": "Includeți cheia dumneavoastră în", - "apiKeyUsageHintHeader": "antet.", - "apiKeyPermissionsHint": "Taste moștenesc permisiunile utilizatorului de a crea.", - "roleUser": "Utilizator", - "authMethodDual": "Autentificare dublă", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Pornirea setării TOTP a eșuat", - "totpEnter6Digits": "Introduceți un cod de 6 cifre", - "totpEnabledSuccess": "Autentificare doi factori activată", - "totpInvalidCode": "Cod nevalid, încercați din nou", - "totpDisableInputRequired": "Introduceți codul sau parola TOTP", - "totpDisabledSuccess": "Autentificare doi factori dezactivată", - "totpDisableFailed": "Dezactivarea 2FA a eșuat", - "totpDisableTitle": "Dezactivează 2FA", - "totpDisablePlaceholder": "Introduceți codul TOTP sau parola", - "totpDisableConfirm": "Dezactivează 2FA", - "totpContinueVerify": "Continuați să verificați", - "totpVerifyTitle": "Verifică codul", - "totpBackupTitle": "Coduri de rezervă", - "totpDownloadBackup": "Descarcă codurile de rezervă", - "done": "Terminat", - "secretCopied": "Secret copiat în clipboard", - "apiKeyNameRequired": "Numele cheii este necesar", - "apiKeyCreated": "API-cheie \"{{name}}\" creat", - "apiKeyCreateFailed": "Crearea cheii API a eșuat", - "apiKeyUser": "Utilizator", - "apiKeyExpires": "Expiră", - "apiKeyRevoked": "Cheie API \"{{name}}\" revocată", - "apiKeyRevokeFailed": "Eroare la revocarea cheii API", - "passwordFieldsRequired": "Sunt necesare parole curente și noi", - "passwordMismatch": "Parolele nu corespund", - "passwordTooShort": "Parola trebuie să conțină cel puțin 6 caractere", - "passwordUpdated": "Parola actualizată cu succes", - "passwordUpdateFailed": "Actualizarea parolei a eșuat", - "deletePasswordRequired": "Parola este necesară pentru a șterge contul dvs.", - "deleteFailed": "Ștergerea contului a eșuat", - "deleting": "Ștergere...", - "colorPickerTooltip": "Deschide selectorul de culori", - "themeSystem": "Sistem", - "themeLight": "Lumină", - "themeDark": "Întunecat", - "themeDracula": "Draculă", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solarizat", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Un întunecat", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/ru_RU.json b/src/ui/locales/translated/ru_RU.json index 77750546..9bc5af32 100644 --- a/src/ui/locales/translated/ru_RU.json +++ b/src/ui/locales/translated/ru_RU.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Папки", - "folder": "Папка", - "password": "Пароль", - "key": "Ключ", - "sshPrivateKey": "SSH закрытый ключ", - "upload": "Выгрузить", - "keyPassword": "Пароль ключа", - "sshKey": "SSH ключ", - "uploadPrivateKeyFile": "Загрузить файл с закрытым ключом", - "searchCredentials": "Поиск учетных данных...", - "addCredential": "Добавить учетные данные", - "caCertificate": "Сертификат ЦС (-cert.pub)", - "caCertificateDescription": "Необязательно: Загрузите или вставьте файл подписанного CA-сертификата (например, id_ed25519-cert.pub). Обязательно, когда SSH сервер использует авторизацию на основе сертификата.", - "uploadCertFile": "Загрузить -cert.pub файл", - "clearCert": "Очистить", - "certLoaded": "Сертификат загружен", - "certPublicKeyLabel": "Сертификат ЦС", - "certTypeLabel": "Тип сертификата", - "pasteOrUploadCert": "Вставьте или загрузите сертификат -cert.pub...", - "hasCaCert": "Имеет сертификат ЦС", - "noCaCert": "Нет сертификата ЦС" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Не удалось загрузить оповещения", - "failedToDismissAlert": "Не удалось уволить оповещение" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Конфигурация сервера", - "description": "Настройте URL сервера Termix для подключения к вашим сервисам backend", - "serverUrl": "URL сервера", - "enterServerUrl": "Пожалуйста, введите URL сервера", - "saveFailed": "Не удалось сохранить настройки", - "saveError": "Ошибка при сохранении конфигурации", - "saving": "Сохранение...", - "saveConfig": "Сохранить конфигурацию", - "helpText": "Введите URL, на котором запущен сервер Termix (например, http://localhost:30001 или https://your-server.com)", - "changeServer": "Сменить сервер", - "mustIncludeProtocol": "Адрес сервера должен начинаться с http:// или https://", - "allowInvalidCertificate": "Разрешить использование недействительного сертификата", - "allowInvalidCertificateDesc": "Использовать только для доверенных серверов, размещенных на собственном сервере и имеющих самоподписанные сертификаты или сертификаты IP-адресов.", - "useEmbedded": "Использовать локальный сервер", - "embeddedDesc": "Запустить Termix со встроенным локальным сервером (удалённый сервер не требуется)", - "embeddedConnecting": "Подключение к локальному серверу...", - "embeddedNotReady": "Локальный сервер еще не готов. Пожалуйста, подождите минуту и повторите попытку.", - "localServer": "Локальный сервер" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Ошибка проверки версии", - "checkFailed": "Не удалось проверить обновления", - "upToDate": "Приложение обновлено", - "currentVersion": "Вы используете версию {{version}}", - "updateAvailable": "Доступно обновление", - "newVersionAvailable": "Доступна новая версия! Вы используете {{current}}, но {{latest}} доступен.", - "betaVersion": "Бета-версия", - "betaVersionDesc": "Вы используете {{current}}, который новее, чем последний стабильный релиз {{latest}}.", - "releasedOn": "Выпущено на {{date}}", - "downloadUpdate": "Загрузить обновление", - "checking": "Проверка обновлений...", - "checkUpdates": "Проверить обновления", - "checkingUpdates": "Проверка обновлений...", - "updateRequired": "Требуется обновление" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Закрыть", - "minimize": "Свернуть", - "online": "Онлайн", - "offline": "Оффлайн", - "continue": "Продолжить", - "maintenance": "Техническое обслуживание", - "degraded": "Ухудшение", - "error": "Ошибка", - "warning": "Предупреждение", - "unsavedChanges": "Несохраненные изменения", - "dismiss": "Отклонить", - "loading": "Загрузка...", - "optional": "Опционально", - "connect": "Подключиться", - "copied": "Скопировано", - "connecting": "Подключение...", - "updateAvailable": "Доступно обновление", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Открыть в новой вкладке", - "noReleases": "Нет релизов", - "updatesAndReleases": "Обновления и релизы", - "newVersionAvailable": "Доступна новая версия ({{version}}).", - "failedToFetchUpdateInfo": "Не удалось получить информацию об обновлении", - "preRelease": "Пре-релиз", - "noReleasesFound": "Релизы не найдены.", - "cancel": "Отмена", - "username": "Имя пользователя", - "login": "Логин", - "register": "Регистрация", - "password": "Пароль", - "confirmPassword": "Подтверждение пароля", - "back": "Назад", - "save": "Сохранить", - "saving": "Сохранение...", - "delete": "Удалить", - "rename": "Переименовать", - "edit": "Редактирование", - "add": "Добавить", - "confirm": "Подтвердить", - "no": "Нет", - "or": "ИЛИ", - "next": "Следующий", - "previous": "Предыдущий", - "refresh": "Обновить", - "language": "Язык", - "checking": "Проверка...", - "checkingDatabase": "Проверка подключения к базе данных...", - "checkingAuthentication": "Проверка аутентификации...", - "backendReconnected": "Соединение с сервером восстановлено", - "connectionDegraded": "Соединение с сервером потеряно, восстановление…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Удалить", - "create": "Создать", - "update": "Обновить", - "copy": "Копировать", - "copyFailed": "Не удалось скопировать в буфер обмена", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Восстановить", - "of": "из" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Домашний", - "terminal": "Терминал", - "docker": "Докер", - "tunnels": "Туннели", - "fileManager": "Файловый менеджер", - "serverStats": "Статистика сервера", - "admin": "Админ", - "userProfile": "Профиль пользователя", - "splitScreen": "Разделить экран", - "confirmClose": "Закрыть эту активную сессию?", - "close": "Закрыть", - "cancel": "Отмена", - "sshManager": "SSH менеджер", - "cannotSplitTab": "Нельзя разделить эту вкладку", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Скопировать пароль", - "copySudoPassword": "Скопировать пароль Sudo", - "passwordCopied": "Пароль скопирован в буфер обмена", - "noPasswordAvailable": "Пароль не доступен", - "failedToCopyPassword": "Не удалось скопировать пароль", - "refreshTab": "Обновить соединение", - "openFileManager": "Открыть файловый менеджер", - "dashboard": "Панель", - "networkGraph": "Сетевой график", - "quickConnect": "Быстрое подключение", - "sshTools": "SSH инструменты", - "history": "История", - "hosts": "Узлы", - "snippets": "Сниппеты", - "hostManager": "Менеджер хостов", - "credentials": "Учетные данные", - "connections": "Связи", - "roleAdministrator": "Администратор", - "roleUser": "Пользователь" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Узлы", - "noHosts": "Нет SSH хостов", - "retry": "Повторить", - "refresh": "Обновить", - "optional": "Опционально", - "downloadSample": "Скачать образец", - "failedToDeleteHost": "Не удалось удалить {{name}}", - "importSkipExisting": "Импорт (пропустить существующий)", - "connectionDetails": "Детали подключения", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Удаленный рабочий стол", - "port": "Порт", - "username": "Имя пользователя", - "folder": "Папка", - "tags": "Теги", - "pin": "Закрепить", - "addHost": "Добавить хост", - "editHost": "Изменить хост", - "cloneHost": "Клонировать хост", - "enableTerminal": "Включить терминал", - "enableTunnel": "Включить Туннель", - "enableFileManager": "Включить Диспетчер файлов", - "enableDocker": "Включить Docker", - "defaultPath": "Путь по умолчанию", - "connection": "Подключение", - "upload": "Выгрузить", - "authentication": "Аутентификация", - "password": "Пароль", - "key": "Ключ", - "credential": "Учётные данные", - "none": "Нет", - "sshPrivateKey": "SSH закрытый ключ", - "keyType": "Тип ключа", - "uploadFile": "Загрузить файл", - "tabGeneral": "Общие положения", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", - "tabVnc": "ВНК", - "tabTunnels": "Туннели", - "tabDocker": "Докер", - "tabFiles": "Файлы", - "tabStats": "Статистика", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Поделиться", - "tabAuthentication": "Проверка подлинности", - "terminal": "Терминал", - "tunnel": "Туннель", - "fileManager": "Файловый менеджер", - "serverStats": "Статистика сервера", - "status": "Статус", - "folderRenamed": "Папка \"{{oldName}}\" успешно переименована в \"{{newName}}\"", - "failedToRenameFolder": "Не удалось переименовать папку", - "movedToFolder": "Перемещено в \"{{folder}}\"", - "editHostTooltip": "Изменить узел", - "statusChecks": "Проверки состояния", - "metricsCollection": "Коллекция метрик", - "metricsInterval": "Интервал сбора метрик", - "metricsIntervalDesc": "Как часто собирать статистику сервера (5s - 1ч)", - "behavior": "Поведение", - "themePreview": "Предпросмотр темы", - "theme": "Тема", - "fontFamily": "Семейство шрифтов", - "fontSize": "Размер шрифта", - "letterSpacing": "Интервал букв", - "lineHeight": "Высота линии", - "cursorStyle": "Стиль курсора", - "cursorBlink": "Ярлык курсора", - "scrollbackBuffer": "Буфер прокрутки", - "bellStyle": "Стиль колокольни", - "rightClickSelectsWord": "Щелкните правой кнопкой мыши для выбора слова", - "fastScrollModifier": "Модификатор быстрой прокрутки", - "fastScrollSensitivity": "Быстрая чувствительность прокрутки", - "sshAgentForwarding": "Перенаправление SSH агента", - "backspaceMode": "Режим Backspace", - "startupSnippet": "Сниппет запуска", - "selectSnippet": "Выбрать сниппет", - "forceKeyboardInteractive": "Интерактивная клавиатура", - "overrideCredentialUsername": "Переопределить имя пользователя", - "overrideCredentialUsernameDesc": "Используйте имя пользователя, указанное выше, вместо имени пользователя", - "jumpHostChain": "Цепь Прыжков", - "portKnocking": "Летание портов", - "addKnock": "Добавить порт", - "addProxyNode": "Добавить узел", - "proxyNode": "Узел прокси", - "proxyType": "Тип прокси", - "quickActions": "Быстрые действия", - "sudoPasswordAutoFill": "Автозаполнение пароля", - "sudoPassword": "Sudo Пароль", - "keepaliveInterval": "Защитный интервал (мс)", - "moshCommand": "Команда MOSH", - "environmentVariables": "Переменные окружения", - "addVariable": "Добавить переменную", - "docker": "Докер", - "copyTerminalUrl": "Копировать URL терминала", - "copyFileManagerUrl": "Копировать URL диспетчера файлов", - "copyRemoteDesktopUrl": "Копировать URL удаленного рабочего стола", - "failedToConnect": "Не удалось подключиться к консоли", - "connect": "Подключиться", - "disconnect": "Отключиться", - "start": "Начать", - "enableStatusCheck": "Включить проверку статуса", - "enableMetrics": "Включить метрики", - "bulkUpdateFailed": "Массовое обновление не удалось", - "selectAll": "Выделить все", - "deselectAll": "Отменить выбор", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Защищенная раковина", - "virtualNetwork": "Виртуальная сеть", - "unencryptedShell": "Незашифрованная оболочка", - "addressIp": "Адрес / IP", - "friendlyName": "Дружественное имя", - "folderAndAdvanced": "Папка и расширенные", - "privateNotes": "Личные заметки", - "privateNotesPlaceholder": "Детали об этом сервере...", - "pinToTop": "Прикрепить к началу", - "pinToTopDesc": "Всегда показывать этот хост в верхней части списка", - "portKnockingSequence": "Порт отталкивает последовательность", - "addKnockBtn": "Добавить тук", - "noPortKnocking": "Нет настроенных портов.", - "knockPort": "Порт Тука", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Задержка после (мс)", - "useSocks5Proxy": "Использовать SOCKS5 прокси", - "useSocks5ProxyDesc": "Перенаправлять соединение через прокси-сервер", - "proxyHost": "Прокси хост", - "proxyPort": "Порт прокси", - "proxyUsername": "Имя пользователя прокси", - "proxyPassword": "Пароль прокси", - "proxySingleMode": "Одиночный прокси", - "proxyChainMode": "Цепочка прокси", - "you": "Вы", - "jumpHostChainLabel": "Цепь Прыжков", - "addJumpBtn": "Добавить прыжок", - "noJumpHosts": "Хосты прыжков не настроены.", - "selectAServer": "Выберите сервер...", - "sshPort": "SSH порт", - "authMethod": "Метод аутентификации", - "storedCredential": "Сохраненные учетные данные", - "selectACredential": "Выберите учетные данные...", - "keyTypeLabel": "Тип ключа", - "keyTypeAuto": "Автоопределение", - "keyPasteTab": "Вставить", - "keyUploadTab": "Выгрузить", - "keyFileLoaded": "Файл ключа загружен", - "keyUploadClick": "Нажмите, чтобы загрузить .pem / .key / .ppk", - "clearKey": "Очистить ключ", - "keySaved": "SSH ключ сохранен", - "keyReplaceNotice": "вставьте новый ключ ниже, чтобы заменить его", - "keyPassphraseSaved": "Парольная фраза сохранена, тип для изменения", - "replaceKey": "Заменить ключ", - "forceKeyboardInteractiveLabel": "Интерактивная клавиатура", - "forceKeyboardInteractiveShortDesc": "Принудительно вводить пароль вручную, даже если есть ключи", - "terminalAppearance": "Внешний вид терминала", - "colorTheme": "Цветовая схема", - "fontFamilyLabel": "Семейство шрифтов", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Стиль курсора", - "letterSpacingPx": "Интервал букв (px)", - "lineHeightLabel": "Высота линии", - "bellStyleLabel": "Стиль колокольни", - "backspaceModeLabel": "Режим Backspace", - "cursorBlinking": "Привязка курсора", - "cursorBlinkingDesc": "Включить анимацию мигания для курсора терминала", - "rightClickSelectsWordLabel": "Щелкните правой кнопкой мыши по слову", - "rightClickSelectsWordShortDesc": "Выберите слово под курсором при клике правой кнопкой мыши", - "behaviorAndAdvanced": "Поведение и расширенные", - "scrollbackBufferLabel": "Буфер прокрутки", - "scrollbackMaxLines": "Максимальное количество строк в истории", - "sshAgentForwardingLabel": "Перенаправление SSH агента", - "sshAgentForwardingShortDesc": "Передайте ваши локальные ключи SSH этому хосту", - "enableAutoMosh": "Включить автомош", - "enableAutoMoshDesc": "Предпочитайте Mosh по SSH если есть", - "enableAutoTmux": "Включить авторежим", - "enableAutoTmuxDesc": "Автоматически запускать или прикрепить к сеансу tmux", - "sudoPasswordAutoFillLabel": "Автозаполнение пароля", - "sudoPasswordAutoFillShortDesc": "Автоматически предоставлять пароль sudo при появлении запроса", - "sudoPasswordLabel": "Sudo Пароль", - "environmentVariablesLabel": "Переменные окружения", - "addVariableBtn": "Добавить переменную", - "noEnvVars": "Переменные окружения не настроены.", - "fastScrollModifierLabel": "Модификатор быстрой прокрутки", - "fastScrollSensitivityLabel": "Быстрая чувствительность прокрутки", - "moshCommandLabel": "Мош Командование", - "startupSnippetLabel": "Сниппет запуска", - "keepaliveIntervalLabel": "Интервал поддержания соединения (секунды)", - "maxKeepaliveMisses": "Максимальное количество пропусков", - "tunnelSettings": "Настройки Туннеля", - "enableTunneling": "Включить туннелирование", - "enableTunnelingDesc": "Включить функциональность SSH туннеля для этого узла", - "serverTunnelsSection": "Серверные туннели", - "addTunnelBtn": "Добавить Туннель", - "noTunnelsConfigured": "Туннели не настроены.", - "tunnelLabel": "Туннель {{number}}", - "tunnelType": "Тип туннеля", - "tunnelModeLocalDesc": "Переслать локальный порт на порт удаленного сервера (или хост, доступный из него).", - "tunnelModeRemoteDesc": "Переслать порт на удаленный сервер обратно на локальный порт на вашей машине.", - "tunnelModeDynamicDesc": "Создайте SOCKS5 прокси на локальном порту для динамической пересылки портов.", - "sameHost": "Этот хост (прямой туннель)", - "endpointHost": "Конечная точка хоста", - "endpointPort": "Порт точки входа", - "bindHost": "Привязать хост", - "sourcePort": "Порт источника", - "maxRetries": "Макс. повторов", - "retryIntervalS": "Интервал повтора (ы)", - "autoStartLabel": "Автозапуск", - "autoStartDesc": "Автоматически подключать этот туннель при загрузке узла", - "tunnelConnecting": "Подключение туннеля...", - "tunnelDisconnected": "Туннель отключен", - "failedToConnectTunnel": "Не удалось подключиться", - "failedToDisconnectTunnel": "Не удалось отключить", - "dockerIntegration": "Интеграция Docker", - "enableDockerMonitor": "Включить Docker", - "enableDockerMonitorDesc": "Отслеживать и управлять контейнерами на этом хосте через Docker", - "enableFileManagerMonitor": "Включить Диспетчер файлов", - "enableFileManagerMonitorDesc": "Просмотр и управление файлами на этом хосте поверх SFTP", - "defaultPathLabel": "Путь по умолчанию", - "fileManagerPathHint": "Каталог для открытия при запуске файлового менеджера для этого узла.", - "statusChecksLabel": "Проверки состояния", - "enableStatusChecks": "Включить проверку состояния", - "enableStatusChecksDesc": "Периодически запрашивать этот узел для проверки доступности", - "useGlobalInterval": "Использовать глобальный интервал", - "useGlobalIntervalDesc": "Переопределить с интервалом проверки состояния сервера", - "checkIntervalS": "Проверить интервал (ы)", - "checkIntervalDesc": "Секунд между каждым пингом подключения", - "metricsCollectionLabel": "Коллекция метрик", - "enableMetricsLabel": "Включить метрики", - "enableMetricsDesc": "Собрать процессор, ОЗУ, диск и сетевое использование с этого узла", - "useGlobalMetrics": "Использовать глобальный интервал", - "useGlobalMetricsDesc": "Переопределить с помощью интервала общесистемных метрик", - "metricsIntervalS": "Интервал (ы) метрик", - "metricsIntervalDesc2": "Секунды между метрическими снимками", - "visibleWidgets": "Видимые виджеты", - "cpuUsageLabel": "Загрузка ЦП", - "cpuUsageDesc": "Процент ЦП, средние значения загрузки, график искривления", - "memoryLabel": "Использование памяти", - "memoryDesc": "Использование ОЗУ, swap, кэш", - "storageLabel": "Использование диска", - "storageDesc": "Использование диска за точку монтирования", - "networkLabel": "Сетевые интерфейсы", - "networkDesc": "Список интерфейсов и пропускная способность", - "uptimeLabel": "Время работы", - "uptimeDesc": "Время работы системы и время загрузки", - "systemInfoLabel": "Системная информация", - "systemInfoDesc": "ОС, ядро, имя хоста, архитектура", - "recentLoginsLabel": "Последние Логины", - "recentLoginsDesc": "Успешные и неудачные события входа", - "topProcessesLabel": "Топ процессов", - "topProcessesDesc": "PID, CPU%, MEM%, команда", - "listeningPortsLabel": "Порты прослушивания", - "listeningPortsDesc": "Открывать порты процесса и состояния", - "firewallLabel": "Брандмауэр", - "firewallDesc": "Брандмауэр, AppArmor, статус SELinux", - "quickActionsLabel": "Быстрые действия", - "quickActionsToolbar": "На панели инструментов Server Stats отображаются быстрые действия для выполнения команды одним щелчком.", - "noQuickActions": "Пока нет быстрых действий.", - "buttonLabel": "Ярлык кнопки", - "selectSnippetPlaceholder": "Выберите сниппет...", - "addActionBtn": "Добавить действие", - "hostSharedSuccessfully": "Хост успешно поделился", - "failedToShareHost": "Не удалось поделиться узлом", - "accessRevoked": "Доступ отозван", - "failedToRevokeAccess": "Не удалось отменить доступ", - "cancelBtn": "Отмена", - "savingBtn": "Сохранение...", - "addHostBtn": "Добавить хост", - "hostUpdated": "Хост обновлен", - "hostCreated": "Узел создан", - "failedToSave": "Не удалось сохранить узел", - "credentialUpdated": "Учетные данные обновлены", - "credentialCreated": "Учетные данные созданы", - "failedToSaveCredential": "Не удалось сохранить учетные данные", - "backToHosts": "Вернуться к узлам", - "backToCredentials": "Назад к учетным данным", - "pinned": "Прикреплено", - "noHostsFound": "Хосты не найдены", - "tryDifferentTerm": "Попробуйте другой термин", - "addFirstHost": "Добавьте свой первый хост, чтобы начать", - "noCredentialsFound": "Учетные данные не найдены", - "addCredentialBtn": "Добавить учетные данные", - "updateCredentialBtn": "Обновить учетные данные", - "features": "Возможности", - "noFolder": "(Нет папки)", - "deleteSelected": "Удалить", - "exitSelection": "Выйти из выбора", - "importSkip": "Импорт (пропустить существующий)", - "importOverwrite": "Импорт (перезапись)", - "collapseBtn": "Свернуть", - "importExportBtn": "Импорт / Экспорт", - "hostStatusesRefreshed": "Статусы узлов обновлены", - "failedToRefreshHosts": "Не удалось обновить хосты", - "movedHostTo": "Перемещено {{host}} в \"{{folder}}\"", - "failedToMoveHost": "Не удалось переместить узел", - "folderRenamedTo": "Папка переименована в \"{{name}}\"", - "deletedFolder": "Удалена папка \"{{name}}\"", - "failedToDeleteFolder": "Не удалось удалить папку", - "deleteAllInFolder": "Удалить все узлы в \"{{name}}\"? Это не может быть отменено.", - "deletedHost": "{{name}} удалена", - "copiedToClipboard": "Скопировано в буфер обмена", - "terminalUrlCopied": "URL терминала скопирован", - "fileManagerUrlCopied": "URL менеджера файлов скопирован", - "tunnelUrlCopied": "Ссылка на туннель скопирована", - "dockerUrlCopied": "URL Docker скопирован", - "serverStatsUrlCopied": "Скопирован URL статистики сервера", - "rdpUrlCopied": "URL RDP скопирован", - "vncUrlCopied": "VNC URL скопирован", - "telnetUrlCopied": "Скопирован Telnet URL", - "remoteDesktopUrlCopied": "URL удаленного рабочего стола скопирован", - "expandActions": "Развернуть действия", - "collapseActions": "Свернуть действия", - "wakeOnLanAction": "Пробуждение по локальной сети", - "wakeOnLanSuccess": "Волшебный пакет отправлен на {{name}}", - "wakeOnLanError": "Не удалось отправить волшебный пакет.", - "cloneHostAction": "Клонировать хост", - "copyAddress": "Копировать адрес", - "copyLink": "Скопировать ссылку", - "copyTerminalUrlAction": "Копировать URL терминала", - "copyFileManagerUrlAction": "Копировать URL диспетчера файлов", - "copyTunnelUrlAction": "Скопировать URL туннеля", - "copyDockerUrlAction": "Скопировать URL Docker", - "copyServerStatsUrlAction": "Копировать URL статистики сервера", - "copyRdpUrlAction": "Копировать URL RDP", - "copyVncUrlAction": "Копировать VNC URL", - "copyTelnetUrlAction": "Скопировать URL Telnet", - "copyRemoteDesktopUrlAction": "Копировать URL удаленного рабочего стола", - "deleteCredentialConfirm": "Удалить учетные данные \"{{name}}?", - "deletedCredential": "{{name}} удалена", - "deploySSHKeyTitle": "Установка SSH ключа", - "deployingBtn": "Развертывание...", - "deployBtn": "Развертывание", - "failedToDeployKey": "Не удалось развернуть ключ", - "deleteHostsConfirm": "Удалить {{count}} хост{{plural}}? Это действие не может быть отменено.", - "movedToRoot": "Перемещено в root", - "failedToMoveHosts": "Не удалось переместить хосты", - "enableTerminalFeature": "Включить терминал", - "disableTerminalFeature": "Отключить терминал", - "enableFilesFeature": "Включить файлы", - "disableFilesFeature": "Отключить файлы", - "enableTunnelsFeature": "Включить туннели", - "disableTunnelsFeature": "Отключить туннели", - "enableDockerFeature": "Включить Docker", - "disableDockerFeature": "Отключить Docker", - "addTagsPlaceholder": "Добавить теги...", - "authDetails": "Подробности аутентификации", - "credType": "Тип", - "generateKeyPairDesc": "Создать новую пару ключей, как приватные, так и открытые ключи будут заполняться автоматически.", - "generatingKey": "Создание...", - "generateLabel": "Сгенерировать {{label}}", - "uploadFileBtn": "Загрузить файл", - "keyPassphraseOptional": "Пароль ключа (необязательно)", - "sshPublicKeyOptional": "Публичный SSH ключ (необязательно)", - "publicKeyGenerated": "Публичный ключ сгенерирован", - "failedToGeneratePublicKey": "Не удалось получить открытый ключ", - "publicKeyCopied": "Открытый ключ скопирован", - "keyPairGenerated": "Сгенерирована пара ключей {{label}}", - "failedToGenerateKeyPair": "Не удалось сгенерировать пару ключей", - "searchHostsPlaceholder": "Поиск хостов, адресов, тегов…", - "searchCredentialsPlaceholder": "Поиск учетных данных…", - "refreshBtn": "Обновить", - "addTag": "Добавить теги...", - "deleteConfirmBtn": "Удалить", - "tunnelRequirementsText": "SSH сервер должен иметь GatewayPorts да, AllowTcpForwarding да и PermitRootLogin да в /etc/ssh/sshd_config.", - "deleteHostConfirm": "Удалить \"{{name}}\"?", - "enableAtLeastOneProtocol": "Включите по крайней мере один протокол для настройки параметров аутентификации и соединения.", - "keyPassphrase": "Ключевая фраза", - "connectBtn": "Подключиться", - "disconnectBtn": "Отключиться", - "basicInformation": "Основная информация", - "authDetailsSection": "Подробности аутентификации", - "credTypeLabel": "Тип", - "hostsTab": "Узлы", - "credentialsTab": "Полномочия", - "selectMultiple": "Выбрать несколько", - "selectHosts": "Выберите хосты", - "connectionLabel": "Подключение", - "authenticationLabel": "Проверка подлинности", - "generateKeyPairTitle": "Сгенерировать ключевую пару", - "generateKeyPairDescription": "Создать новую пару ключей, как приватные, так и открытые ключи будут заполняться автоматически.", - "generateFromPrivateKey": "Сгенерировать из приватного ключа", - "refreshBtn2": "Обновить", - "exitSelectionTitle": "Выйти из выбора", - "exportAll": "Экспортировать все", - "addHostBtn2": "Добавить хост", - "addCredentialBtn2": "Добавить учетные данные", - "checkingHostStatuses": "Проверка состояния узла...", - "pinnedSection": "Прикреплено", - "hostsExported": "Узлы успешно экспортированы", - "exportFailed": "Не удалось экспортировать хосты.", - "sampleDownloaded": "Пример загруженного файла", - "failedToDeleteCredential2": "Не удалось удалить учетные данные", - "noFolderOption": "(Нет папки)", - "nSelected": "{{count}} выбран", - "featuresMenu": "Возможности", - "moveMenu": "Переместить", - "cancelSelection": "Отмена", - "deployDialogDesc": "Разверните {{name}} на authorized_keys узла.", - "targetHostLabel": "Целевой хост", - "selectHostOption": "Выберите хост...", - "keyDeployedSuccess": "Ключ успешно развернут", - "failedToDeployKey2": "Не удалось развернуть ключ", - "deletedCount": "Удаленные {{count}} узлы", - "failedToDeleteCount": "Не удалось удалить узлы {{count}}", - "duplicatedHost": "Дублировано \"{{name}}\"", - "failedToDuplicateHost": "Не удалось дублировать узел", - "updatedCount": "Обновлено узлов {{count}}", - "friendlyNameLabel": "Дружественное имя", - "descriptionLabel": "Описание", - "loadingHost": "Загрузка хоста...", - "loadingHosts": "Загрузка узлов...", - "loadingCredentials": "Загрузка учетных данных...", - "noHostsYet": "Хостов пока нет", - "noHostsMatchSearch": "Нет хостов, соответствующих вашему запросу", - "hostNotFound": "Хост не найден", - "searchHosts": "Поиск узлов...", - "sortHosts": "Сортировка хостов", - "sortDefault": "Порядок по умолчанию", - "sortNameAsc": "Имя (от А до Я)", - "sortNameDesc": "Имя (Z → A)", - "sortIpAsc": "IP-адрес (восходящий)", - "sortIpDesc": "IP-адрес (описание)", - "sortOnlineFirst": "Онлайн прежде всего", - "sortOfflineFirst": "Офлайн-первый", - "sortPinnedFirst": "Закреплено первым", - "filterHosts": "Фильтрация хостов", - "filterClearAll": "Очистить фильтры", - "filterStatusGroup": "Статус", - "filterOnline": "Онлайн", - "filterOffline": "Офлайн", - "filterPinned": "Закреплено", - "filterAuthGroup": "Тип аутентификации", - "filterAuthPassword": "Пароль", - "filterAuthKey": "Ключ SSH", - "filterAuthCredential": "Удостоверение личности", - "filterAuthNone": "Никто", - "filterAuthOpkssh": "ОПКШ", - "filterProtocolGroup": "Протокол", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "РДП", - "filterProtocolVnc": "ВНК", - "filterProtocolTelnet": "Телнет", - "filterFeaturesGroup": "Функции", - "filterFeatureTerminal": "Терминал", - "filterFeatureFileManager": "Файловый менеджер", - "filterFeatureTunnel": "Туннель", + "filterProtocolRdp": "RDP", + "filterProtocolVnc": "VNC", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Теги", - "shareHost": "Поделиться хостом", - "shareHostTitle": "Поделиться: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Этот узел должен использовать учетные данные для включения общего доступа. Сначала отредактируйте узел и назначьте учетные данные." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Подключение", - "authentication": "Проверка подлинности", - "connectionSettings": "Настройки соединения", - "displaySettings": "Настройки отображения", - "audioSettings": "Настройки звука", - "rdpPerformance": "Производительность RDP", - "deviceRedirection": "Перенаправление устройства", - "session": "Сессия", - "gateway": "Шлюз", - "remoteApp": "Удалить", - "clipboard": "Буфер обмена", - "sessionRecording": "Запись сессии", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Настройки VNC", - "terminalSettings": "Настройки терминала", - "rdpPort": "Порт RDP", - "username": "Имя пользователя", - "password": "Пароль", - "domain": "Домен", - "securityMode": "Режим безопасности", - "colorDepth": "Глубина цвета", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Высота", - "dpi": "ДОИ", - "resizeMethod": "Изменить размер", - "clientName": "Имя Клиента", - "initialProgram": "Начальная программа", - "serverLayout": "Макет сервера", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Порт шлюза", - "gatewayUsername": "Имя пользователя шлюза", - "gatewayPassword": "Пароль шлюза", - "gatewayDomain": "Домен шлюза", - "remoteAppProgram": "Программа для удаленного приложения", - "workingDirectory": "Рабочая папка", - "arguments": "Аргументы", - "normalizeLineEndings": "Нормализовать концы линии", - "recordingPath": "Путь записи", - "recordingName": "Имя записи", - "macAddress": "MAC адрес", - "broadcastAddress": "Адрес трансляции", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Время ожидания (ы)", - "driveName": "Имя диска", - "drivePath": "Путь к диску", - "ignoreCertificate": "Игнорировать сертификат", - "ignoreCertificateDesc": "Разрешить соединения с хостами с самоподписанными сертификатами", - "forceLossless": "Неустрашимый", - "forceLosslessDesc": "Принудительное кодирование изображения без потерь (высокое качество, больше пропускной способности)", - "disableAudio": "Отключить звук", - "disableAudioDesc": "Отключить весь звук из удаленного сеанса", - "enableAudioInput": "Включить аудио ввод (Microphone)", - "enableAudioInputDesc": "Передать локальный микрофон на удаленный сеанс", - "wallpaper": "Обои", - "wallpaperDesc": "Показывать обои рабочего стола (отключение увеличивает производительность)", - "theming": "Темы", - "themingDesc": "Включить визуальные темы и стили", - "fontSmoothing": "Сглаживание шрифта", - "fontSmoothingDesc": "Включить рендеринг шрифта ClearType", - "fullWindowDrag": "Полное переключение окна", - "fullWindowDragDesc": "Показывать содержимое окна при перетаскивании", - "desktopComposition": "Состав рабочего стола", - "desktopCompositionDesc": "Включить эффекты Aero", - "menuAnimations": "Анимация меню", - "menuAnimationsDesc": "Включить анимацию затухания меню и слайда", - "disableBitmapCaching": "Отключить кэширование изображений", - "disableBitmapCachingDesc": "Выключить кэш изображений (может помочь с подсветками)", - "disableOffscreenCaching": "Отключить кэширование", - "disableOffscreenCachingDesc": "Выключить кэш оффэкрана", - "disableGlyphCaching": "Отключить кэширование символов", - "disableGlyphCachingDesc": "Выключить кэш глифов", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Использовать графический конвейер RemoteFX", - "enablePrinting": "Включить печать", - "enablePrintingDesc": "Перенаправить локальные принтеры на удаленный сеанс", - "enableDriveRedirection": "Включить перенаправление диска", - "enableDriveRedirectionDesc": "Распознавать локальную папку как диск в удаленном сеансе", - "createDrivePath": "Создать путь к диску", - "createDrivePathDesc": "Автоматически создайте папку, если она не существует", - "disableDownload": "Отключить загрузку", - "disableDownloadDesc": "Запретить загрузку файлов с удаленного сеанса", - "disableUpload": "Отключить загрузку", - "disableUploadDesc": "Предотвратить загрузку файлов на удаленный сеанс", - "enableTouch": "Включить касание", - "enableTouchDesc": "Включить переадресацию ввода касания", - "consoleSession": "Сеанс консоли", - "consoleSessionDesc": "Подключиться к консоли (сессия 0) вместо новой сессии", - "sendWolPacket": "Отправить WOL пакет", - "sendWolPacketDesc": "Отправить волшебный пакет, чтобы разбудить этот хост перед подключением", - "disableCopy": "Отключить копирование", - "disableCopyDesc": "Запретить копирование текста из удаленного сеанса", - "disablePaste": "Отключить вставку", - "disablePasteDesc": "Предотвратить вставку текста в удаленный сеанс", - "createPathIfMissing": "Создать путь, если отсутствует", - "createPathIfMissingDesc": "Автоматически создавать директорию записи", - "excludeOutput": "Исключить вывод", - "excludeOutputDesc": "Не записывать вывод экрана (только метаданные)", - "excludeMouse": "Исключить мышь", - "excludeMouseDesc": "Не записывать движения мыши", - "includeKeystrokes": "Включить горячие клавиши", - "includeKeystrokesDesc": "Запись сырых клавиш в дополнение к экрану", - "vncPort": "Порт VNC", - "vncPassword": "Пароль VNC", - "vncUsernameOptional": "Имя пользователя (необязательно)", - "vncLeaveBlank": "Оставьте пустым, если не требуется", - "cursorMode": "Режим курсора", - "swapRedBlue": "Поменять красный/синий", - "swapRedBlueDesc": "Поменять местами красно-синие каналы (исправляет некоторые проблемы цвета)", - "readOnly": "Только чтение", - "readOnlyDesc": "Просмотр удаленного экрана без отправки ввода", - "telnetPort": "Порт Telnet", - "terminalType": "Тип терминала", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Цветовая схема", - "backspaceKey": "Ключ Backspace", - "saveHostFirst": "Сначала сохраните узел.", - "sharingOptionsAfterSave": "Параметры общего доступа доступны после сохранения узла.", - "sharingLoadError": "Не удалось загрузить данные обмена. Проверьте подключение и повторите попытку.", - "shareHostSection": "Поделиться хостом", - "shareWithUser": "Поделиться с пользователем", - "shareWithRole": "Поделиться с ролью", - "selectUser": "Выбрать пользователя", - "selectRole": "Выберите роль", - "selectUserOption": "Выберите пользователя...", - "selectRoleOption": "Выберите роль...", - "permissionLevel": "Уровень разрешений", - "expiresInHours": "Истекает через (часы)", - "noExpiryPlaceholder": "Оставьте пустым для отсутствия срока действия", - "shareBtn": "Поделиться", - "currentAccess": "Текущий доступ", - "typeHeader": "Тип", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Разрешение", - "grantedByHeader": "Предоставлено", - "expiresHeader": "Истекает", - "noAccessEntries": "Нет записей доступа.", - "expiredLabel": "Истёк", - "neverLabel": "Никогда", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Отмена", - "savingBtn": "Сохранение...", - "updateHostBtn": "Обновить хост", - "addHostBtn": "Добавить хост" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Поиск узлов, команд или настроек...", - "quickActions": "Быстрые действия", - "hostManager": "Менеджер хостов", - "hostManagerDesc": "Управлять, добавлять или редактировать хосты", - "addNewHost": "Добавить новый хост", - "addNewHostDesc": "Зарегистрировать новый хост", - "adminSettings": "Админ настройки", - "adminSettingsDesc": "Настройка системных настроек и пользователей", - "userProfile": "Профиль пользователя", - "userProfileDesc": "Управление учетной записью и настройками", - "addCredential": "Добавить учетные данные", - "addCredentialDesc": "Хранить ключи или пароли SSH", - "recentActivity": "Недавняя активность", - "serversAndHosts": "Серверы и хосты", - "noHostsFound": "Не найдено узлов по запросу \"{{search}}\"", - "links": "Ссылки", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Выбрать", - "toggleWith": "Переключить с" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Вкладка не назначена", - "focusedPane": "Активная панель" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Нет подключений", - "noConnectionsDesc": "Откройте терминал, файловый менеджер или удаленный рабочий стол, чтобы увидеть подключения здесь.", - "connectedFor": "Подключено для {{duration}}", - "connected": "Подключено", - "disconnected": "Отключено", - "closeTab": "Закрыть вкладку", - "closeConnection": "Тесная связь", - "forgetTab": "Забывать", - "removeBackground": "Удалять", - "reconnect": "Переподключитесь", - "reopenTab": "Возобновить работу", - "sectionOpen": "Открыть", - "sectionBackground": "Фон", - "backgroundDesc": "После отключения сеанс связи длится 30 минут, после чего его можно повторно подключить.", - "persisted": "Сохранялся на заднем плане", - "expiresIn": "Срок действия истекает через {{duration}}", - "search": "Поиск связей...", - "noSearchResults": "Поисковые запросы не соответствуют вашим запросам." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Подключение к {{type}} сессии...", - "connectionError": "Ошибка подключения", - "connectionFailed": "Не удалось подключиться", - "failedToConnect": "Не удалось получить токен подключения", - "hostNotFound": "Хост не найден", - "noHostSelected": "Хост не выбран", - "reconnect": "Переподключить", - "retry": "Повторить", - "guacdUnavailable": "Служба удаленного рабочего стола (guacd) недоступна. Убедитесь, что guacd запущен и правильно настроен в настройках администратора.", - "ctrlAltDel": "Ctrl + Alt+Del", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", + "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { - "ctrlAltDel": "Ctrl + Alt+Del", - "winL": "Win+L (экран блокировки)", - "winKey": "Ключ Windows", + "ctrlAltDel": "Ctrl+Alt+Del", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Сдвиг", - "win": "Победить", - "stickyActive": "{{key}} (закрыто - нажмите, чтобы освободить)", - "stickyInactive": "{{key}} (нажмите для защелки)", - "esc": "Экран", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Домашний", - "end": "Конец", - "pageUp": "Страница вверх", - "pageDown": "Страница вниз", - "arrowUp": "Стрелка вверх", - "arrowDown": "Стрелка вниз", - "arrowLeft": "Стрелка влево", - "arrowRight": "Стрелка вправо", - "fnToggle": "Функциональные ключи", - "reconnect": "Переподключить сессию", - "collapse": "Свернуть панель инструментов", - "expand": "Развернуть панель инструментов", - "dragHandle": "Перетащите для перемещения" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Подключиться к хосту", - "clear": "Очистить", - "paste": "Вставить", - "reconnect": "Переподключить", - "connectionLost": "Соединение потеряно", - "connected": "Подключено", - "clipboardWriteFailed": "Не удалось скопировать в буфер обмена. Убедитесь, что страница отправляется через HTTPS или localhost.", - "clipboardReadFailed": "Не удалось прочитать из буфера обмена. Убедитесь, что права доступа предоставлены.", - "clipboardHttpWarning": "Вставка требует HTTPS. Используйте Ctrl+Shift+V или используйте Termix по HTTPS.", - "unknownError": "Произошла неизвестная ошибка", - "websocketError": "Ошибка подключения к WebSocket", - "connecting": "Подключение...", - "noHostSelected": "Хост не выбран", - "reconnecting": "Переподключение... ({{attempt}}/{{max}})", - "reconnected": "Переподключено успешно", - "tmuxSessionCreated": "сеанс tmux создан: {{name}}", - "tmuxSessionAttached": "прикрепленный сеанс tmux: {{name}}", - "tmuxUnavailable": "tmux не установлен на удаленном хосте, возвращаясь к стандартной оболочке", - "tmuxSessionPickerTitle": "tmux сеансы", - "tmuxSessionPickerDesc": "Найденные на этом узле сеансы tmux. Выберите один для повторного присоединения или создания новой сессии.", - "tmuxWindows": "Окна", - "tmuxWindowCount": "{{count}} окно", - "tmuxAttached": "Прикрепленные клиенты", - "tmuxAttachedCount": "{{count}} прикреплен", - "tmuxLastActivity": "Последнее действие", - "tmuxTimeJustNow": "только что", - "tmuxTimeMinutes": "{{count}}м назад", - "tmuxTimeHours": "{{count}}ч назад", - "tmuxTimeDays": "{{count}}дней назад", - "tmuxCreateNew": "Начать новую сессию", - "tmuxCopyHint": "Настройте выделение и нажмите Enter для копирования в буфер обмена", - "tmuxDetach": "Отсоединиться от сеанса tmux", - "tmuxDetached": "Отключено от сеанса tmux", - "maxReconnectAttemptsReached": "Достигнуто максимальное число попыток восстановления соединения", - "closeTab": "Закрыть", - "connectionTimeout": "Таймаут подключения", - "terminalTitle": "Терминал - {{host}}", - "terminalWithPath": "Терминал - {{host}}:{{path}}", - "runTitle": "Запуск {{command}} - {{host}}", - "totpRequired": "Требуется двухфакторная аутентификация", - "totpCodeLabel": "Проверочный код", - "totpVerify": "Подтвердить", - "warpgateAuthRequired": "Требуется аутентификация по Warpgate", - "warpgateSecurityKey": "Ключ безопасности", - "warpgateAuthUrl": "URL аутентификации", - "warpgateOpenBrowser": "Открыть в браузере", - "warpgateContinue": "Я завершаю аутентификацию", - "opksshAuthRequired": "Требуется аутентификация OPKSSH", - "opksshAuthDescription": "Завершите аутентификацию в браузере, чтобы продолжить. Этот сеанс будет оставаться действительным в течение 24 часов.", - "opksshOpenBrowser": "Аутентификация в браузере", - "opksshWaitingForAuth": "Ожидание аутентификации в браузере...", - "opksshAuthenticating": "Обработка аутентификации...", - "opksshTimeout": "Истекло время аутентификации. Попробуйте еще раз.", - "opksshAuthFailed": "Аутентификация не удалась. Проверьте учетные данные и повторите попытку.", - "opksshSignInWith": "Войти с помощью {{provider}}", - "sudoPasswordPopupTitle": "Вставить пароль?", - "websocketAbnormalClose": "Неожиданное соединение закрыто. Это может быть вызвано проблемой конфигурации обратного прокси или SSL. Пожалуйста, проверьте журналы сервера.", - "connectionLogTitle": "Журнал подключений", - "connectionLogCopy": "Скопировать логи в буфер обмена", - "connectionLogEmpty": "Пока нет журналов подключения", - "connectionLogWaiting": "Ожидание журналов подключения...", - "connectionLogCopied": "Журналы подключения скопированы в буфер обмена", - "connectionLogCopyFailed": "Не удалось скопировать журналы в буфер обмена", - "connectionRejected": "Соединение отклонено сервером. Проверьте вашу аутентификацию и конфигурацию сети.", - "hostKeyRejected": "Проверка SSH ключа хоста отклонена. Соединение отменено.", - "sessionTakenOver": "Сессия была открыта в другой вкладке. Повторное подключение..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Хост не выбран", - "initializingEditor": "Инициализация редактора...", - "file": "Файл", - "folder": "Папка", - "uploadFile": "Загрузить файл", - "downloadFile": "Скачать", - "extractArchive": "Извлечь архив", - "extractingArchive": "Извлечение {{name}}...", - "archiveExtractedSuccessfully": "{{name}} успешно извлечено", - "extractFailed": "Извлечь не удалось", - "compressFile": "Сжать файл", - "compressFiles": "Сжать файлы", - "compressFilesDesc": "Сжатие объектов {{count}} в архив", - "archiveName": "Имя архива", - "enterArchiveName": "Введите имя архива...", - "compressionFormat": "Формат сжатия", - "selectedFiles": "Выбранные файлы", - "andMoreFiles": "и еще {{count}}...", - "compress": "Сжать", - "compressingFiles": "Сжатие {{count}} элементов в {{name}}...", - "filesCompressedSuccessfully": "{{name}} успешно создан", - "compressFailed": "Сжатие не удалось", - "edit": "Редактирование", - "preview": "Предпросмотр", - "previous": "Предыдущий", - "next": "Следующий", - "pageXOfY": "Страница {{current}} из {{total}}", - "zoomOut": "Уменьшить", - "zoomIn": "Увеличить", - "newFile": "Новый файл", - "newFolder": "Новая папка", - "rename": "Переименовать", - "uploading": "Загрузка...", - "uploadingFile": "Загрузка {{name}}...", - "fileName": "Имя файла", - "folderName": "Имя папки", - "fileUploadedSuccessfully": "Файл \"{{name}}\" успешно загружен", - "failedToUploadFile": "Не удалось загрузить файл", - "fileDownloadedSuccessfully": "Файл \"{{name}}\" успешно загружен", - "failedToDownloadFile": "Не удалось загрузить файл", - "fileCreatedSuccessfully": "Файл \"{{name}}\" успешно создан", - "folderCreatedSuccessfully": "Папка \"{{name}}\" успешно создана", - "failedToCreateItem": "Не удалось создать элемент", - "operationFailed": "Ошибка операции {{operation}} для {{name}}: {{error}}", - "failedToResolveSymlink": "Не удалось разрешить символическую ссылку", - "itemsDeletedSuccessfully": "{{count}} элементы успешно удалены", - "failedToDeleteItems": "Не удалось удалить элементы", - "sudoPasswordRequired": "Требуется пароль администратора", - "enterSudoPassword": "Введите пароль sudo для продолжения этой операции", - "sudoPassword": "Sudo пароль", - "sudoOperationFailed": "Сбой операции с Суданом", - "sudoAuthFailed": "Ошибка аутентификации в Sudo", - "dragFilesToUpload": "Перетащите файлы сюда, чтобы загрузить", - "emptyFolder": "Эта папка пуста", - "searchFiles": "Поиск файлов...", - "upload": "Выгрузить", - "selectHostToStart": "Выберите хост для запуска управления файлами", - "sshRequiredForFileManager": "Файловый менеджер требует SSH. Этот хост не поддерживает SSH.", - "failedToConnect": "Не удалось подключиться к SSH", - "failedToLoadDirectory": "Не удалось загрузить каталог", - "noSSHConnection": "Нет доступного SSH соединения", - "copy": "Копировать", - "cut": "Вырезать", - "paste": "Вставить", - "copyPath": "Копировать путь", - "copyPaths": "Копировать пути", - "delete": "Удалить", - "properties": "Свойства", - "refresh": "Обновить", - "downloadFiles": "Скачать файлы {{count}} в браузер", - "copyFiles": "Копировать {{count}} элементов", - "cutFiles": "Вырезать {{count}} элементов", - "deleteFiles": "Удалить {{count}} элементов", - "filesCopiedToClipboard": "{{count}} элементов скопировано в буфер обмена", - "filesCutToClipboard": "{{count}} элементов вырезано в буфер обмена", - "pathCopiedToClipboard": "Путь скопирован в буфер обмена", - "pathsCopiedToClipboard": "{{count}} пути скопированы в буфер обмена", - "failedToCopyPath": "Не удалось скопировать путь в буфер обмена", - "movedItems": "Перемещено {{count}} элементов", - "failedToDeleteItem": "Не удалось удалить элемент", - "itemRenamedSuccessfully": "{{type}} успешно переименован", - "failedToRenameItem": "Не удалось переименовать элемент", - "download": "Скачать", - "permissions": "Права доступа", - "size": "Размер", - "modified": "Изменено", - "path": "Путь", - "confirmDelete": "Вы уверены, что хотите удалить {{name}}?", - "permissionDenied": "Отказано в доступе", - "serverError": "Ошибка сервера", - "fileSavedSuccessfully": "Файл успешно сохранен", - "failedToSaveFile": "Не удалось сохранить файл", - "confirmDeleteSingleItem": "Вы уверены, что хотите навсегда удалить \"{{name}}\"?", - "confirmDeleteMultipleItems": "Вы уверены, что хотите навсегда удалить элементы {{count}}?", - "confirmDeleteMultipleItemsWithFolders": "Вы уверены, что хотите навсегда удалить {{count}} ? Включает в себя папки и их содержимое.", - "confirmDeleteFolder": "Вы уверены, что хотите навсегда удалить папку \"{{name}}\" и все ее содержимое?", - "permanentDeleteWarning": "Это действие нельзя отменить. Элемент(ы) будут окончательно удалены с сервера.", - "recent": "Недавние", - "pinned": "Прикреплено", - "folderShortcuts": "Ярлыки папок", - "failedToReconnectSSH": "Не удалось переподключить SSH сеанс", - "openTerminalHere": "Открыть терминал здесь", - "run": "Запустить", - "openTerminalInFolder": "Открыть терминал в этой папке", - "openTerminalInFileLocation": "Открыть терминал в расположении файла", - "runningFile": "Запуск - {{file}}", - "onlyRunExecutableFiles": "Можно запускать только исполняемые файлы", - "directories": "Каталоги", - "removedFromRecentFiles": "Удалено \"{{name}}\" из последних файлов", - "removeFailed": "Не удалось удалить", - "unpinnedSuccessfully": "Откреплено \"{{name}}\" успешно", - "unpinFailed": "Открепить не удалось", - "removedShortcut": "Удалён ярлык \"{{name}}\"", - "removeShortcutFailed": "Не удалось удалить ярлык", - "clearedAllRecentFiles": "Все недавние файлы удалены", - "clearFailed": "Сбой очистки", - "removeFromRecentFiles": "Удалить из недавних файлов", - "clearAllRecentFiles": "Очистить все недавние файлы", - "unpinFile": "Открепить файл", - "removeShortcut": "Удалить ярлык", - "pinFile": "Закрепить файл", - "addToShortcuts": "Добавить в ярлыки", - "pasteFailed": "Ошибка вставки", - "noUndoableActions": "Нет невыполнимых действий", - "undoCopySuccess": "Операция отмены копии: удалено {{count}} скопированных файлов", - "undoCopyFailedDelete": "Не удалось отменить: Не удалось удалить скопированные файлы", - "undoCopyFailedNoInfo": "Ошибка отмены: Не удалось найти скопированную информацию о файле", - "undoMoveSuccess": "Операция отменена: Перемещение файлов {{count}} обратно в исходное место", - "undoMoveFailedMove": "Не удалось отменить: Не удалось переместить файлы обратно", - "undoMoveFailedNoInfo": "Не удалось отменить: информация о перемещенном файле не найдена", - "undoDeleteNotSupported": "Операция удаления не может быть отменена: файлы были навсегда удалены с сервера", - "undoTypeNotSupported": "Неподдерживаемый тип операции отмены", - "undoOperationFailed": "Не удалось отменить операцию", - "unknownError": "Неизвестная ошибка", - "confirm": "Подтвердить", - "find": "Найти...", - "replace": "Заменить", - "downloadInstead": "Загрузить вместо этого", - "keyboardShortcuts": "Горячие клавиши", - "searchAndReplace": "Поиск и замена", - "editing": "Редактирование", - "search": "Искать", - "findNext": "Найти далее", - "findPrevious": "Найти предыдущий", - "save": "Сохранить", - "selectAll": "Выделить все", - "undo": "Отменить", - "redo": "Повторить", - "moveLineUp": "Переместить строку вверх", - "moveLineDown": "Переместить линию вниз", - "toggleComment": "Переключить комментарий", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Не удалось загрузить изображение", - "startTyping": "Начните печатать...", - "unknownSize": "Неизвестный размер", - "fileIsEmpty": "Файл пуст", - "largeFileWarning": "Предупреждение о большом файле", - "largeFileWarningDesc": "Этот файл имеет размер {{size}} , что может вызвать проблемы производительности при открытии текста в виде текста.", - "fileNotFoundAndRemoved": "Файл \"{{name}}\" не найден и был удален из недавних / прикрепленных файлов", - "failedToLoadFile": "Не удалось загрузить файл: {{error}}", - "serverErrorOccurred": "Произошла ошибка сервера. Пожалуйста, повторите попытку позже.", - "autoSaveFailed": "Ошибка автосохранения", - "fileAutoSaved": "Файл автоматически сохранен", - "moveFileFailed": "Не удалось переместить {{name}}", - "moveOperationFailed": "Операция перемещения не удалась", - "canOnlyCompareFiles": "Можно сравнить только два файла", - "comparingFiles": "Сравнение файлов: {{file1}} и {{file2}}", - "dragFailed": "Ошибка при перетаскивании", - "filePinnedSuccessfully": "Файл \"{{name}}\" успешно закреплен", - "pinFileFailed": "Не удалось закрепить файл", - "fileUnpinnedSuccessfully": "Файл \"{{name}}\" успешно откреплен", - "unpinFileFailed": "Не удалось открепить файл", - "shortcutAddedSuccessfully": "Ярлык папки \"{{name}}\" успешно добавлен", - "addShortcutFailed": "Не удалось добавить ярлык", - "operationCompletedSuccessfully": "{{operation}} {{count}} элементов успешно", - "operationCompleted": "{{operation}} {{count}} элементов", - "downloadFileSuccess": "Файл {{name}} успешно загружен", - "downloadFileFailed": "Ошибка загрузки", - "moveTo": "Переместить в {{name}}", - "diffCompareWith": "Сравнение разницы с {{name}}", - "dragOutsideToDownload": "Перетащите внешнее окно для загрузки (файлы{{count}})", - "newFolderDefault": "Новая папка", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Успешно перемещено {{count}} элементов в {{target}}", - "move": "Переместить", - "searchInFile": "Поиск в файле (Ctrl+F)", - "showKeyboardShortcuts": "Показать горячие клавиши", - "startWritingMarkdown": "Начните писать ваше содержимое markdown...", - "loadingFileComparison": "Загрузка сравнения файлов...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Сравнить", - "sideBySide": "Грань по боку", - "inline": "Встроенный", - "fileComparison": "Сравнение файлов: {{file1}} против {{file2}}", - "fileTooLarge": "Файл слишком большой: {{error}}", - "sshConnectionFailed": "Не удалось подключиться к SSH. Пожалуйста, проверьте подключение к {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Не удалось загрузить файл: {{error}}", - "connecting": "Подключение...", - "connectedSuccessfully": "Подключено успешно", - "totpVerificationFailed": "Ошибка проверки TOTP", - "warpgateVerificationFailed": "Ошибка аутентификации Warpgate", - "authenticationFailed": "Аутентификация не удалась", - "verificationCodePrompt": "Код подтверждения:", - "changePermissions": "Изменить права доступа", - "currentPermissions": "Текущие разрешения", - "owner": "Владелец", - "group": "Группа", - "others": "Другие", - "read": "Чтение", - "write": "Написать", - "execute": "Выполнить", - "permissionsChangedSuccessfully": "Права доступа успешно изменены", - "failedToChangePermissions": "Не удалось изменить права доступа", - "name": "Наименование", - "sortByName": "Наименование", - "sortByDate": "Дата изменения", - "sortBySize": "Размер", - "ascending": "По возрастанию", - "descending": "По убыванию", - "root": "Корень", - "new": "Новый", - "sortBy": "Сортировать по", - "items": "Предметы", - "selected": "Выбрано", - "editor": "Редактор", - "octal": "Октальный", - "storage": "Хранилище", - "disk": "Диск", - "used": "Использовано", - "of": "из", - "toggleSidebar": "Переключить боковую панель", - "cannotLoadPdf": "Не удается загрузить PDF", - "pdfLoadError": "Произошла ошибка при загрузке этого PDF-файла.", - "loadingPdf": "Загрузка PDF...", - "loadingPage": "Загрузка страницы..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Скопировать на хост…", - "moveToHost": "Переместить на хост…", - "copyItemsToHost": "Скопировать {{count}} элементов на хост…", - "moveItemsToHost": "Переместить {{count}} элементов на хост…", - "noHostsConnected": "Других хостов для файловых менеджеров нет в наличии.", - "noHostsConnectedHint": "Добавьте еще один SSH-хост с включенным файловым менеджером в диспетчере хостов.", - "selectDestinationHost": "Выберите целевой хост", - "destinationPath": "Путь назначения", - "recentDestinations": "Недавние направления", - "collapseRecentDestinations": "Свернуть список последних направлений", - "expandRecentDestinations": "Развернуть список недавно открытых направлений", - "browseFolders": "Просмотрите папки назначения", - "browseDestination": "Просмотрите или введите путь", - "confirmCopy": "Копия", - "confirmMove": "Двигаться", - "transferring": "Передача…", - "compressing": "Сжатие…", - "extracting": "Извлечение…", - "transferringItems": "Передача {{current}} элементов {{total}}…", - "transferSuccess": "Передача завершена", - "transferError": "Перевод не удался", - "transferPartial": "Передача завершена с ошибками {{count}}", - "transferPartialHint": "Не удалось передать: {{paths}}", - "itemsSummary": "{{count}} предметов", - "destMustBeDirectory": "В качестве пункта назначения должен быть каталог для передачи нескольких элементов.", - "selectThisFolder": "Выберите эту папку", - "browsePathWillBeCreated": "Эта папка пока не существует. Она будет создана при начале передачи.", - "browsePathError": "Не удалось открыть этот путь на целевом хосте.", - "goUp": "Поднимитесь", - "copyFolderToHost": "Скопируйте папку на хост…", - "moveFolderToHost": "Переместить папку на хост…", - "hostReady": "Готовый", - "hostConnecting": "Подключение…", - "hostDisconnected": "Не подключено", - "hostAuthRequired": "Требуется аутентификация — сначала откройте файловый менеджер на этом хосте.", - "hostConnectionFailed": "Соединение не удалось.", - "metricsTitle": "Сроки трансферов", - "metricsPrepare": "Подготовьте пункт назначения: {{duration}}", - "metricsCompress": "Сжатие по источнику: {{duration}}", - "metricsHopSourceRead": "Источник → сервер: {{throughput}}", - "metricsHopDestSftpWrite": "Сервер → назначение (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Сервер → назначение (локальное): {{throughput}}", - "metricsTransfer": "Сквозное сканирование: {{throughput}} ({{duration}})", - "metricsExtract": "Выгрузка в пункте назначения: {{duration}}", - "metricsSourceDelete": "Удалить из источника: {{duration}}", - "metricsTotal": "Итого: {{duration}}", - "progressCompressing": "Сжатие на исходном хосте…", - "progressExtracting": "Извлечение в целевом месте…", - "progressTransferring": "Передача данных…", - "progressReconnecting": "Воссоединение…", - "parallelSegmentsLabel": "Параллельные полосы для пересадки", - "parallelSegmentsOption": "{{count}} полосы", - "parallelSegmentsHint": "Большие файлы разбиваются на фрагменты по 256 МБ. Для увеличения общей пропускной способности используются несколько каналов связи (например, запуск нескольких передач одновременно).", - "progressTotalSpeed": "{{speed}} всего ({{lanes}} полос)", - "progressTransferringItems": "Передача файлов ({{current}} из {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} файлы", - "sourceNotDeletedPartial": "Исходные файлы сохранены (частичная передача).", - "jumpHostLimitation": "Оба хоста должны быть доступны с сервера Termix. Прямая маршрутизация между хостами не поддерживается.", - "cancel": "Отмена", - "methodLabel": "Метод передачи", - "methodAuto": "Авто", - "methodTar": "архив Tar", - "methodItemSftp": "SFTP для каждого файла", - "methodAutoHint": "Выбор способа передачи данных (tar или SFTP для каждого файла) зависит от количества файлов, их размера и возможности сжатия. Для отдельных файлов всегда используется потоковая передача SFTP.", - "methodTarHint": "Сжатие на источнике, передача одного архива, распаковка в пункте назначения. Требуется tar на обоих Unix-хостах.", - "methodItemSftpHint": "Передавайте каждый файл по отдельности по протоколу SFTP. Работает на всех хостах, включая Windows.", - "methodPreviewLoading": "Метод расчета переноса…", - "methodPreviewError": "Не удалось предварительно просмотреть способ передачи. Сервер все равно выберет способ передачи при запуске.", - "methodPreviewWillUseTar": "Будет использоваться: архив Tar", - "methodPreviewWillUseItemSftp": "Будет использоваться: SFTP для каждого файла.", - "methodPreviewScanSummary": "{{fileCount}} файлов, {{totalSize}} всего (просканировано на исходном хосте).", - "methodItemSftpLimitation": "Каждый файл копируется по одному и тому же SFTP-потоку, один за другим. Прогресс суммируется по всем файлам, поэтому индикатор выполнения движется медленно при работе с большими файлами.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Вы выбрали SFTP для каждого файла.", - "user_tar": "Вы выбрали архив tar.", - "tar_unavailable": "Tar недоступен на одном или обоих хостах — вместо него будет использоваться SFTP для каждого файла.", - "windows_host": "Используется хост под управлением Windows — архив tar не применяется.", - "auto_multi_large": "Авто: несколько файлов, включая большой файл ({{largestSize}}) с сжимаемыми данными — tar-архивы объединяются в один файл для передачи.", - "auto_single_large_in_archive": "Авто: один большой файл ({{largestSize}}) в этом наборе — SFTP для каждого файла.", - "auto_many_incompressible": "Авто: преимущественно несжимаемые данные — SFTP для каждого файла.", - "auto_many_files": "Авто: много файлов ({{fileCount}}) — tar уменьшает накладные расходы на каждый файл.", - "auto_default": "Авто: SFTP для каждого файла в этом наборе." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Отмена", - "progressCancelling": "Отмена…", - "progressStalled": "Застрял", - "resumedHint": "Восстановлено соединение с активной передачей данных, начатой в другом окне.", - "transferCancelled": "Трансфер отменен", - "transferCancelledCopyHint": "Неполные файлы могут остаться на целевом устройстве.", - "transferCancelledMoveHint": "В целевом хранилище могут остаться неполные файлы. Исходные файлы не были удалены.", - "cleanupDestFiles": "Очистка места назначения", - "cleanupDestFilesSuccess": "Из целевого хранилища удалены частично скопированные файлы.", - "cleanupDestFilesPartial": "Некоторые частично скопированные файлы удалить не удалось.", - "cleanupDestFilesNothing": "На месте назначения ничего не нужно убирать.", - "cleanupDestFilesError": "Очистка не удалась", - "retryTransfer": "Повторить попытку", - "retryTransferError": "Повторная попытка не удалась.", - "transferFailedRetryHint": "Частичные данные были сохранены на целевом устройстве. Повторная попытка будет предпринята после восстановления соединения." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Нет SSH туннелей", - "createFirstTunnelMessage": "Вы еще не создали SSH туннелей. Настройте туннельные соединения в менеджере хостов, чтобы начать.", - "connected": "Подключено", - "disconnected": "Отключено", - "connecting": "Подключение...", - "error": "Ошибка", - "canceling": "Отмена...", - "connect": "Подключиться", - "disconnect": "Отключиться", - "cancel": "Отмена", - "port": "Порт", - "localPort": "Локальный порт", - "remotePort": "Удаленный порт", - "currentHostPort": "Текущий порт хоста", - "endpointPort": "Порт точки входа", - "bindIp": "Локальный IP", - "endpointSshConfig": "Конфигурация SSH", - "endpointSshHost": "Конечная точка SSH хоста", - "endpointSshHostPlaceholder": "Выберите настроенный хост", - "endpointSshHostRequired": "Выберите SSH хост конечной точки для каждого клиентского туннеля.", - "attempt": "Попытка {{current}} из {{max}}", - "nextRetryIn": "Следующая повтор через {{seconds}} секунд", - "clientTunnels": "Клиентские туннели", - "clientTunnel": "Клиентский Туннель", - "addClientTunnel": "Добавить клиентский туннель", - "noClientTunnels": "На этом рабочем столе не настроены клиентские туннели.", - "tunnelName": "Название Туннеля", - "remoteHost": "Удаленный хост", - "autoStart": "Автозапуск", - "clientAutoStartDesc": "Запускается после открытия этого десктопного клиента и остаётся подключенным.", - "clientManualStartDesc": "Используйте Start and Stop from this row. Termix не будет открывать его автоматически.", - "clientRemoteServerNote": "Удаленная пересылка может потребовать AllowTcpForwarding и GatewayPorts на сервере SSH. Удаленный порт закрывается при отключении рабочего стола.", - "clientTunnelStarted": "Клиентский туннель запущен", - "clientTunnelStopped": "Клиентский туннель остановлен", - "tunnelTestSucceeded": "Туннельный тест выполнен", - "tunnelTestFailed": "Тест туннеля не удался", - "localSaved": "Клиентские туннели сохранены", - "localSaveError": "Не удалось сохранить локальные клиентские туннели", - "invalidBindIp": "Локальный IP должен быть действительным адресом IPv4.", - "invalidLocalTargetIp": "Локальный целевой IP должен быть действительным адресом IPv4.", - "invalidLocalPort": "Локальный порт должен быть от 1 до 65535.", - "invalidRemotePort": "Удаленный порт должен быть от 1 до 65535.", - "invalidLocalTargetPort": "Локальный целевой порт должен быть от 1 до 65535.", - "invalidEndpointPort": "Порт конечной точки должен быть от 1 до 65535.", - "duplicateAutoStartBind": "Только один клиентский туннель автозапуска может использовать {{bind}}.", - "manualControlError": "Не удалось обновить состояние туннеля.", - "active": "Активный", - "start": "Начать", - "stop": "Остановить", - "test": "Тест", - "type": "Тип туннеля", - "typeLocal": "Локальный (-L)", - "typeRemote": "Удалённый (-R)", - "typeDynamic": "Динамика (-D)", - "typeServerLocalDesc": "Текущий хост до конечной точки.", - "typeServerRemoteDesc": "Вернуться к текущему хосту.", - "typeClientLocalDesc": "Локальный компьютер к конечной точке.", - "typeClientRemoteDesc": "Вернуться на локальный компьютер.", - "typeClientDynamicDesc": "SOCKS на локальном компьютере.", - "typeDynamicDesc": "Переадресация SOCKS5 CONNECT трафика через SSH", - "forwardDescriptionServerLocal": "Текущий хост {{sourcePort}} → конечная точка {{endpointPort}}.", - "forwardDescriptionServerRemote": "Конечная точка {{endpointPort}} → текущий хост {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS при текущем хосте {{sourcePort}}.", - "forwardDescriptionClientLocal": "Локальный {{sourcePort}} → удаленный {{endpointPort}}.", - "forwardDescriptionClientRemote": "Удаленная {{sourcePort}} → локальная {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS на локальном порту {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS через {{endpoint}}", - "autoNameClientLocal": "Локальная {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → локальная {{localPort}}", - "autoNameClientDynamic": "{{localPort}} SOCKS {{localPort}} через {{endpoint}}", - "route": "Маршрут:", - "lastStarted": "Последнее начало", - "lastTested": "Последняя проверка", - "lastError": "Последняя ошибка", - "maxRetries": "Макс. повторов", - "maxRetriesDescription": "Максимальное количество попыток повторной попытки.", - "retryInterval": "Интервал повтора (секунд)", - "retryIntervalDescription": "Время ожидания между попытками повтора.", - "local": "Локальный", - "remote": "Удалённый", - "destination": "Назначение", - "host": "Хост", - "mode": "Режим", - "noHostSelected": "Хост не выбран", - "working": "Работаю..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "ЦП", - "memory": "Память", - "disk": "Диск", - "network": "Сеть", - "uptime": "Время работы", - "processes": "Процессы", - "available": "Доступно", - "free": "Свободно", - "connecting": "Подключение...", - "connectionFailed": "Не удалось подключиться к серверу", - "naCpus": "Н/А ЦП(и)", - "cpuCores_one": "Ядро {{count}}", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Загрузка ЦП", - "memoryUsage": "Использование памяти", - "diskUsage": "Использование диска", - "failedToFetchHostConfig": "Не удалось получить конфигурацию узла", - "serverOffline": "Сервер оффлайн", - "cannotFetchMetrics": "Не удается получить метрики с сервера оффлайн", - "totpFailed": "Ошибка проверки TOTP", - "noneAuthNotSupported": "Статистика сервера не поддерживает тип аутентификации «нет».", - "load": "Нагрузка", - "systemInfo": "Информация о системе", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Операционная система", - "kernel": "Ядро", - "seconds": "секунд", - "networkInterfaces": "Сетевые интерфейсы", - "noInterfacesFound": "Сетевых интерфейсов не найдено", - "noProcessesFound": "Процессы не найдены", - "loginStats": "Статистика входа SSH", - "noRecentLoginData": "Нет последних данных для входа", - "executingQuickAction": "Выполнение {{name}}...", - "quickActionSuccess": "{{name}} успешно завершен", - "quickActionFailed": "{{name}} не удалось", - "quickActionError": "Не удалось выполнить {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Прослушиваемые порты", - "protocol": "Протокол", - "port": "Порт", - "address": "Адрес", - "process": "Процесс", - "noData": "Нет данных портов для прослушивания" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Брандмауэр", - "inactive": "Неактивный", - "policy": "Политика", - "rules": "правила", - "noData": "Данные брандмауэра не доступны", - "action": "Действие", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Порт", - "source": "Источник", - "anywhere": "Где угодно" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Средняя нагрузка", - "swap": "Поменять", - "architecture": "Архитектура", - "refresh": "Обновить", - "retry": "Повторить" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Самостоятельно размещаемые SSH-серверы и системы удаленного управления рабочим столом", - "loginTitle": "Войти в Термикс", - "registerTitle": "Создать Аккаунт", - "forgotPassword": "Забыли пароль?", - "rememberMe": "Запомнить устройство на 30 дней (включая TOTP)", - "noAccount": "У вас нет учетной записи?", - "hasAccount": "Уже зарегистрированы?", - "twoFactorAuth": "Двухфакторная аутентификация", - "enterCode": "Введите код подтверждения", - "backupCode": "Или используйте код резервной копии", - "verifyCode": "Проверить код", - "redirectingToApp": "Перенаправление на приложение...", - "sshAuthenticationRequired": "Требуется SSH аутентификация", - "sshNoKeyboardInteractive": "Клавиатура - Интерактивная аутентификация недоступна", - "sshAuthenticationFailed": "Ошибка аутентификации", - "sshAuthenticationTimeout": "Таймаут аутентификации", - "sshNoKeyboardInteractiveDescription": "Сервер не поддерживает интерактивную аутентификацию с клавиатуры. Пожалуйста, введите ваш пароль или SSH ключ.", - "sshAuthFailedDescription": "Предоставленные учетные данные неверны. Пожалуйста, попробуйте еще раз с действительными учетными данными.", - "sshTimeoutDescription": "Время попытки аутентификации истекло. Пожалуйста, попробуйте еще раз.", - "sshProvideCredentialsDescription": "Пожалуйста, предоставьте ваши учетные данные SSH для подключения к этому серверу.", - "sshPasswordDescription": "Введите пароль для этого SSH соединения.", - "sshKeyPasswordDescription": "Если ваш SSH ключ зашифрован, введите здесь ключевую фразу.", - "passphraseRequired": "Требуется пароль", - "passphraseRequiredDescription": "SSH ключ зашифрован. Пожалуйста, введите парольную фразу для разблокировки.", - "back": "Назад", - "firstUser": "Первый пользователь", - "firstUserMessage": "Вы первый пользователь и будете созданы администратором. Вы можете просмотреть настройки администратора в раскрывающемся списке пользователя боковой панели. Если вы считаете, что это ошибка, проверьте логи docker или создайте GitHub.", - "external": "Внешний", - "loginWithExternal": "Войти через внешнего провайдера", - "loginWithExternalDesc": "Войти с помощью настроенного провайдера внешней идентификации", - "externalNotSupportedInElectron": "Внешняя аутентификация еще не поддерживается в приложении Electron. Пожалуйста, используйте веб-версию для входа в OIDC.", - "resetPasswordButton": "Сбросить пароль", - "sendResetCode": "Отправить код сброса", - "resetCodeDesc": "Введите имя пользователя для получения кода сброса пароля. Код будет вошел в журналы контейнера docker.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Проверить код", - "enterResetCode": "Введите 6-значный код из журналов docker container для пользователя:", - "newPassword": "Новый пароль", - "confirmNewPassword": "Подтверждение пароля", - "enterNewPassword": "Введите новый пароль для пользователя:", - "signUp": "Регистрация", - "desktopApp": "Приложение для рабочего стола", - "loggingInToDesktopApp": "Вход в приложение для компьютера", - "loadingServer": "Загрузка сервера...", - "dataLossWarning": "Сброс пароля таким образом удалит все сохраненные SSH хосты, учетные данные и другие зашифрованные данные. Это действие нельзя отменить. Используйте только если вы забыли пароль и не вошли в систему.", - "authenticationDisabled": "Аутентификация отключена", - "authenticationDisabledDesc": "Все методы аутентификации в настоящее время отключены. Обратитесь к администратору.", - "attemptsRemaining": "{{count}} попыток осталось" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Проверка SSH ключа хоста", - "keyChangedWarning": "SSH ключ хоста изменен", - "firstConnectionTitle": "Впервые подключение к этому узлу", - "firstConnectionDescription": "Логичность этого узла не может быть установлена. Проверьте отпечаток пальца так, что вы ожидаете.", - "keyChangedDescription": "Ключ хоста для этого сервера изменился с момента вашего последнего подключения. Это может указывать на проблему безопасности.", - "previousKey": "Предыдущий ключ", - "newFingerprint": "Новый отпечаток пальца", - "fingerprint": "Отпечаток пальца", - "verifyInstructions": "Если вы доверяете этому узлу, нажмите «Принять», чтобы продолжить, и сохраните отпечаток пальца для будущих подключений.", - "securityWarning": "Предупреждение о безопасности", - "acceptAndContinue": "Принять и продолжить", - "acceptNewKey": "Принять новый ключ и продолжить" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Не удалось подключиться к базе данных", - "unknownError": "Неизвестная ошибка", - "loginFailed": "Вход не удался", - "failedPasswordReset": "Не удалось сбросить пароль", - "failedVerifyCode": "Не удалось проверить код сброса", - "failedCompleteReset": "Не удалось завершить сброс пароля", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Не удалось запустить вход OIDC", - "silentSigninOidcUnavailable": "Запрошен беззвучный вход, но OIDC вход недоступен.", - "failedUserInfo": "Не удалось получить информацию о пользователе после входа", - "oidcAuthFailed": "Ошибка аутентификации OIDC", - "invalidAuthUrl": "Неправильный URL-адрес авторизации получен из бэкэнда", - "requiredField": "Это поле является обязательным", - "minLength": "Минимальная длина - {{min}}", - "passwordMismatch": "Пароли не совпадают", - "passwordLoginDisabled": "Имя пользователя/пароль в настоящее время отключены", - "sessionExpired": "Истек срок действия сессии - пожалуйста, войдите снова", - "totpRateLimited": "Оценить ограничено: Слишком много попыток проверки TOTP. Пожалуйста, повторите попытку позже.", - "totpRateLimitedWithTime": "Ставка ограничена: Слишком много попыток проверки TOTP. Пожалуйста, подождите {{time}} секунд, прежде чем повторить попытку.", - "resetCodeRateLimited": "Оценить ограничено: слишком много попыток проверки. Пожалуйста, повторите попытку позже.", - "resetCodeRateLimitedWithTime": "Оценить ограничено: слишком много попыток проверки. Пожалуйста, подождите {{time}} секунд, прежде чем повторить попытку.", - "authTokenSaveFailed": "Не удалось сохранить токен аутентификации", - "failedToLoadServer": "Не удалось загрузить сервер" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Регистрация новой учетной записи отключена администратором. Пожалуйста, войдите в систему или свяжитесь с администратором.", - "userNotAllowed": "Ваша учетная запись не авторизована для регистрации. Пожалуйста, свяжитесь с администратором.", - "databaseConnectionFailed": "Не удалось подключиться к серверу базы данных", - "resetCodeSent": "Код сброса отправлен в логи Docker", - "codeVerified": "Код успешно подтвержден", - "passwordResetSuccess": "Пароль успешно сброшен", - "loginSuccess": "Вход выполнен", - "registrationSuccess": "Регистрация прошла успешно" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Местные настольные туннели, нацеленные на настроенные хосты SSH.", - "c2sTunnelPresets": "Пресеты клиентского туннеля", - "c2sTunnelPresetsDesc": "Сохраните список локальных туннелей этого клиента в качестве именованного шаблона сервера или загрузите пресет обратно в этот клиент.", - "c2sTunnelPresetsUnavailable": "Пресеты Клиентского туннеля доступны только в клиенте для рабочего стола.", - "c2sPresetName": "Имя шаблона", - "c2sPresetNamePlaceholder": "Имя шаблона клиента", - "c2sPresetToLoad": "Пресет для загрузки", - "c2sNoPresetSelected": "Пресет не выбран", - "c2sNoPresets": "Пресеты не сохранены", - "c2sLoadPreset": "Нагрузка", - "c2sCurrentLocalConfig": "Конфигурация локального клиентского туннеля(ов) {{count}} на этом рабочем столе.", - "c2sPresetSyncNote": "Пресеты являются явными снимками; загрузка одной заменяет список локальных клиентских туннелей этого клиента.", - "c2sPresetSaved": "Предустановка туннеля клиента сохранена", - "c2sPresetLoaded": "Конфигурация туннеля клиента загружена локально", - "c2sPresetRenamed": "Предустановка туннеля клиента переименована", - "c2sPresetDeleted": "Предустановка туннеля клиента удалена", - "c2sPresetLoadError": "Не удалось загрузить пресеты туннеля клиента" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Язык", - "keyPassword": "пароль ключа", - "pastePrivateKey": "Вставьте ваш закрытый ключ здесь...", - "localListenerHost": "127.0.0.1 (слушать локально)", - "localTargetHost": "127.0.0.1 (цель на этом компьютере)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Введите ваш пароль", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Панель", - "loading": "Загрузка панели управления...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Поддержка", + "support": "Support", "discord": "Discord", - "serverOverview": "Обзор сервера", - "version": "Версии", - "upToDate": "Актуально", - "updateAvailable": "Доступно обновление", - "beta": "Бета", - "uptime": "Время работы", - "database": "База данных", - "healthy": "Исправен", - "error": "Ошибка", - "totalHosts": "Всего хостов", - "totalTunnels": "Всего туннелей", - "totalCredentials": "Всего учетных данных", - "recentActivity": "Недавняя активность", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Загрузка недавней активности...", - "noRecentActivity": "Нет недавних действий", - "quickActions": "Быстрые действия", - "addHost": "Добавить хост", - "addCredential": "Добавить учетные данные", - "adminSettings": "Админ настройки", - "userProfile": "Профиль пользователя", - "serverStats": "Статистика сервера", - "loadingServerStats": "Загрузка статистики сервера...", - "noServerData": "Нет доступных данных сервера", - "cpu": "ЦП", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Настроить панель", - "dashboardSettings": "Настройки панели инструментов", - "enableDisableCards": "Включить/выключить карты", - "resetLayout": "Сброс по умолчанию", - "serverOverviewCard": "Обзор сервера", - "recentActivityCard": "Недавняя активность", - "networkGraphCard": "Сетевой график", - "networkGraph": "Сетевой график", - "quickActionsCard": "Быстрые действия", - "serverStatsCard": "Статистика сервера", - "panelMain": "Главный", - "panelSide": "Грань", - "justNow": "только что" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "СТАБИЛЬНЫЙ", - "hostsOnline": "Хосты онлайн", - "activeTunnels": "Активные туннели", - "registerNewServer": "Зарегистрировать новый сервер", - "storeSshKeysOrPasswords": "Хранить ключи или пароли SSH", - "manageUsersAndRoles": "Управление пользователями и ролями", - "manageYourAccount": "Управление аккаунтом", - "hostStatus": "Состояние хоста", - "noHostsConfigured": "Хосты не настроены", - "online": "ОНЛАЙН", - "offline": "ОФЛАЙН", - "onlineLower": "Онлайн", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Добавить:", - "commandPalette": "Палитра команд", - "done": "Готово", - "editModeInstructions": "Перетащите карты для изменения порядка · Перетащите разделитель столбцов в размер столбцов · Перетащите нижний край карты, чтобы изменить его высоту · Корзина, чтобы удалить", - "empty": "Пусто", - "clear": "Очистить" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Добавить хост", - "addGroup": "Добавить группу", - "addLink": "Добавить ссылку", - "zoomIn": "Увеличить", - "zoomOut": "Уменьшить", - "resetView": "Сбросить вид", - "selectHost": "Выберите хост", - "chooseHost": "Выберите хост...", - "parentGroup": "Родительская группа", - "noGroup": "Нет группы", - "groupName": "Название группы", - "color": "Цвет", - "source": "Источник", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Переместить в группу", - "selectGroup": "Выберите группу...", - "addConnection": "Добавить соединение", - "hostDetails": "Детали хоста", - "removeFromGroup": "Удалить из группы", - "addHostHere": "Добавить узел здесь", - "editGroup": "Изменить группу", - "delete": "Удалить", - "add": "Добавить", - "create": "Создать", - "move": "Переместить", - "connect": "Подключиться", - "createGroup": "Создать группу", - "selectSourcePlaceholder": "Выберите источник...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Недопустимый файл", - "hostAlreadyExists": "Хост уже находится в топологии", - "connectionExists": "Соединение уже существует", - "unknown": "Неизвестен", - "name": "Наименование", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Статус", - "failedToAddNode": "Не удалось добавить узел", - "sourceDifferentFromTarget": "Источник и цель должны отличаться", - "exportJSON": "Экспорт JSON", - "importJSON": "Импорт JSON", - "terminal": "Терминал", - "fileManager": "Файловый менеджер", - "tunnel": "Туннель", - "docker": "Докер", - "serverStats": "Статистика сервера", - "noNodes": "Пока нет узлов" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker не включен для этого узла", - "validating": "Проверка докера...", - "connecting": "Подключение...", - "error": "Ошибка", - "version": "Докер {{version}}", - "connectionFailed": "Не удалось подключиться к Docker", - "containerStarted": "Запущен контейнер {{name}}", - "failedToStartContainer": "Не удалось запустить контейнер {{name}}", - "containerStopped": "Контейнер {{name}} остановлен", - "failedToStopContainer": "Не удалось остановить контейнер {{name}}", - "containerRestarted": "Контейнер {{name}} перезапущен", - "failedToRestartContainer": "Не удалось перезапустить контейнер {{name}}", - "containerPaused": "Контейнер {{name}} приостановлен", - "containerUnpaused": "Контейнер {{name}} снят на паузе", - "failedToTogglePauseContainer": "Не удалось переключить состояние паузы для контейнера {{name}}", - "containerRemoved": "Контейнер {{name}} удален", - "failedToRemoveContainer": "Не удалось удалить контейнер {{name}}", - "image": "Изображение", - "ports": "Порты", - "noPorts": "Нет портов", - "start": "Начать", - "confirmRemoveContainer": "Вы уверены, что хотите удалить контейнер '{{name}}'? Это действие нельзя отменить.", - "runningContainerWarning": "Предупреждение: Этот контейнер в настоящее время запущен. Удаление его сначала остановит контейнер.", - "loadingContainers": "Загрузка контейнеров...", - "manager": "Менеджер Docker", - "autoRefresh": "Автообновление", - "timestamps": "Метки времени", - "lines": "Строки", - "filterLogs": "Фильтровать журналы...", - "refresh": "Обновить", - "download": "Скачать", - "clear": "Очистить", - "logsDownloaded": "Журналы успешно загружены", - "last50": "Последние 50", - "last100": "Последние 100", - "last500": "Последние 500", - "last1000": "Последние 1000", - "allLogs": "Все журналы", - "noLogsMatching": "Нет журналов по запросу \"{{query}}\"", - "noLogsAvailable": "Нет доступных журналов", - "noContainersFound": "Контейнеры не найдены", - "noContainersFoundHint": "На этом узле нет доступных контейнеров Docker", - "searchPlaceholder": "Поиск контейнеров...", - "allStatuses": "Все статусы", - "stateRunning": "Выполняется", - "statePaused": "Пауза", - "stateExited": "Выход", - "stateRestarting": "Перезапуск", - "noContainersMatchFilters": "Нет контейнеров, соответствующих вашим фильтрам", - "noContainersMatchFiltersHint": "Попробуйте изменить критерии поиска или фильтра", - "failedToFetchStats": "Не удалось получить статистику контейнера", - "containerNotRunning": "Контейнер не запущен", - "startContainerToViewStats": "Запустите контейнер для просмотра статистики", - "loadingStats": "Загрузка статистики...", - "errorLoadingStats": "Ошибка загрузки статистики", - "noStatsAvailable": "Нет доступной статистики", - "cpuUsage": "Загрузка ЦП", - "current": "Текущее", - "memoryUsage": "Использование памяти", - "networkIo": "Сеть I/O", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Вывод", - "blockIo": "Блок I/O", - "read": "Чтение", - "write": "Написать", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "Информация о контейнере", - "name": "Наименование", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Состояние", - "containerMustBeRunning": "Контейнер должен быть запущен для доступа к консоли", - "verificationCodePrompt": "Введите код подтверждения", - "totpVerificationFailed": "Ошибка проверки TOTP. Пожалуйста, попробуйте еще раз.", - "warpgateVerificationFailed": "Ошибка аутентификации Warpgate. Пожалуйста, попробуйте еще раз.", - "connectedTo": "Подключено к {{containerName}}", - "disconnected": "Отключено", - "consoleError": "Ошибка консоли", - "errorMessage": "Ошибка: {{message}}", - "failedToConnect": "Не удалось подключиться к контейнеру", - "console": "Консоль", - "selectShell": "Выберите оболочку", - "bash": "Баш", - "sh": "сч", - "ash": "пепел", - "connect": "Подключиться", - "disconnect": "Отключиться", - "notConnected": "Не подключен", - "clickToConnect": "Нажмите \"Подключиться\", чтобы начать сеанс оболочки", - "connectingTo": "Подключение к {{containerName}}...", - "containerNotFound": "Контейнер не найден", - "backToList": "Назад к списку", - "logs": "Логи", - "stats": "Статистика", - "consoleTab": "Консоль", - "startContainerToAccess": "Запустите контейнер для доступа к консоли" + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Общие положения", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Пользователи", - "sectionSessions": "Сессии", - "sectionRoles": "Роли", - "sectionDatabase": "База данных", - "sectionApiKeys": "Ключи API", - "allowRegistration": "Разрешить регистрацию пользователей", - "allowRegistrationDesc": "Позволить новым пользователям самостоятельно регистрироваться", - "allowPasswordLogin": "Разрешить вход в пароль", - "allowPasswordLoginDesc": "Имя пользователя/пароль", - "oidcAutoProvision": "OIDC Автообеспечение", - "oidcAutoProvisionDesc": "Автоматическое создание учетных записей для пользователей OIDC, даже если регистрация отключена", - "allowPasswordReset": "Разрешить сброс пароля", - "allowPasswordResetDesc": "Сбросить код через логи Docker", - "sessionTimeout": "Таймаут сессии", - "hours": "часов", - "sessionTimeoutRange": "Мин. 1ч · Макс 720 ч", - "monitoringDefaults": "Контроль по умолчанию", - "statusCheck": "Проверка статуса", - "metrics": "Метрики", - "sec": "сек", - "logLevel": "Уровень журнала", - "enableGuacamole": "Включить Guacamole", - "enableGuacamoleDesc": "Удаленный рабочий стол RDP/VNC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "Настроить OpenID Connect для SSO. Поля, отмеченные * обязательны.", - "oidcClientId": "ID клиента", - "oidcClientSecret": "Секрет клиента", - "oidcAuthUrl": "URL авторизации", - "oidcIssuerUrl": "URL эмитента", - "oidcTokenUrl": "URL токена", - "oidcUserIdentifier": "Путь к идентификатору пользователя", - "oidcDisplayName": "Отображаемый путь", - "oidcScopes": "Области", - "oidcUserinfoUrl": "Переопределить URL пользовательской информации", - "oidcAllowedUsers": "Разрешенные пользователи", - "oidcAllowedUsersDesc": "Один адрес электронной почты на строку. Оставьте пустым, чтобы разрешить всем.", - "removeOidc": "Удалить", - "usersCount": "{{count}} пользователей", - "createUser": "Создать", - "newRole": "Новая роль", - "roleName": "Наименование", - "roleDisplayName": "Показать имя", - "roleDescription": "Описание", - "rolesCount": "{{count}} ролей", - "createRole": "Создать", - "creating": "Создание...", - "exportDatabase": "Экспорт базы данных", - "exportDatabaseDesc": "Скачать резервную копию всех хостов, учетных данных и настроек", - "export": "Экспорт", - "exporting": "Экспортируется...", - "importDatabase": "Импорт базы данных", - "importDatabaseDesc": "Восстановить из файла резервной копии .sqlite", - "importDatabaseSelected": "Выбрано: {{name}}", - "selectFile": "Выбрать файл", - "changeFile": "Изменить", - "import": "Импорт", - "importing": "Импортируем...", - "apiKeysCount": "{{count}} ключи", - "newApiKey": "Новый ключ API", - "apiKeyCreatedWarning": "Ключ создан - скопируйте его сейчас, он не будет отображаться снова.", - "apiKeyName": "Наименование", - "apiKeyUser": "Пользователь", - "apiKeySelectUser": "Выберите пользователя...", - "apiKeyExpiresAt": "Истекает", - "createKey": "Создать ключ", - "apiKeyNoExpiry": "Нет срока действия", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Двойная авторизация", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Локальный", - "adminStatusAdministrator": "Администратор", - "adminStatusRegularUser": "Обычный пользователь", - "adminBadge": "АДМИН", - "systemBadge": "СИС", - "customBadge": "КЛИЕНТЫ", - "youBadge": "ВЫ", - "sessionsActive": "{{count}} активен", - "sessionActive": "Активно: {{time}}", - "sessionExpires": "Эксп: {{time}}", - "revokeAll": "Все", - "revokeAllSessionsSuccess": "Все сессии для пользователя отозваны", - "revokeAllSessionsFailed": "Не удалось отозвать сессии", - "revokeSessionFailed": "Не удалось отозвать сессию", - "addRole": "Добавить роль", - "noCustomRoles": "Не определены пользовательские роли", - "removeRoleFailed": "Не удалось удалить роль", - "assignRoleFailed": "Не удалось назначить роль", - "deleteRoleFailed": "Не удалось удалить роль", - "userAdminAccess": "Администратор", - "userAdminAccessDesc": "Полный доступ ко всем настройкам администратора", - "userRoles": "Роли", - "revokeAllUserSessions": "Отозвать все сессии", - "revokeAllUserSessionsDesc": "Принудительный повторный вход на всех устройствах", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Удаление этого пользователя необратимо.", - "deleteUser": "Удалить {{username}}", - "deleting": "Удаление...", - "deleteUserFailed": "Не удалось удалить пользователя", - "deleteUserSuccess": "Пользователь \"{{username}}\" удален", - "deleteRoleSuccess": "Роль \"{{name}}\" удалена", - "revokeKeySuccess": "Ключ \"{{name}}\" отозван", - "revokeKeyFailed": "Не удалось отозвать ключ", - "copiedToClipboard": "Скопировано в буфер обмена", - "done": "Готово", - "createUserTitle": "Создать пользователя", - "createUserDesc": "Создать новый локальный аккаунт.", - "createUserUsername": "Имя пользователя", - "createUserPassword": "Пароль", - "createUserPasswordHint": "Минимум 6 символов.", - "createUserEnterUsername": "Введите имя пользователя", - "createUserEnterPassword": "Введите пароль", - "createUserSubmit": "Создать пользователя", - "editUserTitle": "Управление пользователем: {{username}}", - "editUserDesc": "Изменение ролей, статуса администратора, сессий и настроек учетной записи.", - "editUserUsername": "Имя пользователя", - "editUserAuthType": "Тип аутентификации", - "editUserAdminStatus": "Статус админа", - "editUserUserId": "ID пользователя", - "linkAccountTitle": "Привязать OIDC к учетной записи паролей", - "linkAccountDesc": "Объединить OIDC аккаунт {{username}} с существующей локальной учетной записью.", - "linkAccountWarningTitle": "Это будет:", - "linkAccountEffect1": "Удалить учетную запись только OIDC", - "linkAccountEffect2": "Добавить OIDC логин в целевую учетную запись", - "linkAccountEffect3": "Разрешить вход в OIDC и пароль", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Введите имя пользователя локального аккаунта для ссылки на", - "linkAccounts": "Связать аккаунты", - "linkAccountSuccess": "Учетная запись OIDC связана с \"{{username}}\"", - "linkAccountFailed": "Не удалось связать учетную запись OIDC.", - "linkAccountInProgress": "Связывание...", - "saving": "Сохранение...", - "updateRegistrationFailed": "Не удалось обновить регистрацию", - "updatePasswordLoginFailed": "Не удалось обновить настройку входа в пароль", - "updateOidcAutoProvisionFailed": "Не удалось обновить автонастройку OIDC", - "updatePasswordResetFailed": "Не удалось обновить настройку сброса пароля", - "sessionTimeoutRange2": "Таймаут сессии должен быть от 1 до 720 часов", - "sessionTimeoutSaved": "Таймаут сессии сохранен", - "sessionTimeoutSaveFailed": "Не удалось сохранить тайм-аут сессии", - "monitoringIntervalInvalid": "Неверные значения интервала", - "monitoringSaved": "Настройки мониторинга сохранены", - "monitoringSaveFailed": "Не удалось сохранить настройки мониторинга", - "guacamoleSaved": "Настройки Guacamole сохранены", - "guacamoleSaveFailed": "Не удалось сохранить настройки Guacamole", - "guacamoleUpdateFailed": "Не удалось обновить настройки Guacamole", - "logLevelUpdateFailed": "Не удалось обновить уровень журнала", - "oidcSaved": "Конфигурация OIDC сохранена", - "oidcSaveFailed": "Не удалось сохранить настройки OIDC", - "oidcRemoved": "Настройки OIDC удалены", - "oidcRemoveFailed": "Не удалось удалить конфигурацию OIDC", - "createUserRequired": "Требуется имя пользователя и пароль", - "createUserPasswordTooShort": "Пароль должен содержать не менее 6 символов", - "createUserSuccess": "Пользователь \"{{username}}\" создан", - "createUserFailed": "Не удалось создать пользователя", - "updateAdminStatusFailed": "Не удалось обновить статус администратора", - "allSessionsRevoked": "Все сессии отозваны", - "revokeSessionsFailed": "Не удалось отозвать сессии", - "createRoleRequired": "Имя и отображаемое имя обязательны", - "createRoleSuccess": "Создана роль \"{{name}}\"", - "createRoleFailed": "Не удалось создать роль", - "apiKeyNameRequired": "Требуется имя ключа", - "apiKeyUserRequired": "Требуется ID пользователя", - "apiKeyCreatedSuccess": "API ключ \"{{name}}\" создан", - "apiKeyCreateFailed": "Не удалось создать ключ API", - "exportSuccess": "База данных успешно экспортирована", - "exportFailed": "Не удалось экспортировать базу данных", - "importSelectFile": "Сначала выберите файл", - "importCompleted": "Импорт завершен: {{total}} элементов импортировано, {{skipped}} пропущено", - "importFailed": "Ошибка импорта: {{error}}", - "importError": "Не удалось импортировать базу данных" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Хост", - "hostPlaceholder": "192.168.1.1 или example.com", - "portLabel": "Порт", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Имя пользователя", - "usernamePlaceholder": "имя пользователя", - "authLabel": "Авто", - "passwordLabel": "Пароль", - "passwordPlaceholder": "пароль", - "privateKeyLabel": "Приватный ключ", - "privateKeyPlaceholder": "Вставить приватный ключ...", - "credentialLabel": "Полномочия", - "credentialPlaceholder": "Выберите сохраненные учетные данные", - "connectToTerminal": "Подключиться к терминалу", - "connectToFiles": "Подключение к файлам" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Терминал не выбран", - "noTerminalSelectedHint": "Откройте вкладку SSH terminal для просмотра истории команд", - "searchPlaceholder": "Поиск истории...", - "clearAll": "Очистить все", - "noHistoryEntries": "Нет записей в истории", - "trackingDisabled": "Отслеживание истории отключено", - "trackingDisabledHint": "Включите его в настройках вашего профиля для записи команд." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Запись ключа", - "recordToTerminals": "Запись в терминалы", - "selectAll": "Все", - "selectNone": "Нет", - "noTerminalTabsOpen": "Нет открытых вкладок терминала", - "selectTerminalsAbove": "Выберите терминалы выше", - "broadcastInputPlaceholder": "Введите здесь, чтобы транслировать клавиши...", - "stopRecording": "Остановить запись", - "startRecording": "Начать запись", - "settingsTitle": "Настройки", - "enableRightClickCopyPaste": "Включить копирование/вставка правой кнопкой мыши" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Макет", - "selectLayoutAbove": "Выберите макет выше", - "selectLayoutHint": "Выберите количество панелей для отображения", - "panesTitle": "Панели", - "openTabsTitle": "Открытые вкладки", - "dragTabsHint": "Перетащите вкладки в панели выше или воспользуйтесь функцией быстрого назначения.", - "dropHere": "Перетащите сюда", - "emptyPane": "Пусто", - "dashboard": "Панель", - "clearSplitScreen": "Очистить разделение экрана", - "quickAssign": "Быстрое назначение", - "alreadyAssigned": "Панель {{index}}", - "splitTab": "Разделить вкладку", - "addToSplit": "Добавить в раздел", - "removeFromSplit": "Удалить из Сплита", - "assignToPane": "Назначить на панель" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Создать сниппет", - "createSnippetDescription": "Создать новую команду сниппета для быстрого выполнения", - "nameLabel": "Наименование", - "namePlaceholder": "например, перезагрузите Nginx", - "descriptionLabel": "Описание", - "descriptionPlaceholder": "Дополнительное описание", - "optional": "Опционально", - "folderLabel": "Папка", - "noFolder": "Нет папки (без категории)", - "commandLabel": "Команда", - "commandPlaceholder": "например, nginx перезапуск sudo systemctl", - "cancel": "Отмена", - "createSnippetButton": "Создать сниппет", - "createFolderTitle": "Создать папку", - "createFolderDescription": "Организовать ваши сниппеты в папках", - "folderNameLabel": "Имя папки", - "folderNamePlaceholder": "например, Системные команды, Docker скрипты", - "folderColorLabel": "Цвет папки", - "folderIconLabel": "Иконка папки", - "previewLabel": "Предпросмотр", - "folderNameFallback": "Имя папки", - "createFolderButton": "Создать папку", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Все", - "selectNone": "Нет", - "noTerminalTabsOpen": "Нет открытых вкладок терминала", - "searchPlaceholder": "Поиск сниппетов...", - "newSnippet": "Новый сниппет", - "newFolder": "Новая папка", - "run": "Запустить", - "noSnippetsInFolder": "В этой папке нет фрагментов", - "uncategorized": "Без категории", - "editSnippetTitle": "Редактировать сниппет", - "editSnippetDescription": "Обновить эту команду сниппета", - "saveSnippetButton": "Сохранить изменения", - "createSuccess": "Сниппет успешно создан", - "createFailed": "Не удалось создать сниппет", - "updateSuccess": "Сниппет успешно обновлен", - "updateFailed": "Не удалось обновить сниппет", - "deleteFailed": "Не удалось удалить сниппет", - "folderCreateSuccess": "Папка успешно создана", - "folderCreateFailed": "Не удалось создать папку", - "editFolderTitle": "Изменить папку", - "editFolderDescription": "Переименовать или изменить внешний вид этой папки", - "saveFolderButton": "Сохранить изменения", - "editFolder": "Изменить папку", - "deleteFolder": "Удалить папку", - "folderDeleteSuccess": "Папка \"{{name}}\" удалена", - "folderDeleteFailed": "Не удалось удалить папку", - "folderEditSuccess": "Папка успешно обновлена", - "folderEditFailed": "Не удалось обновить папку", - "confirmRunMessage": "Запустить \"{{name}}\"?", - "confirmRunButton": "Бегать", - "runSuccess": "Ранг \"{{name}}\" в терминалах {{count}}", - "copySuccess": "Скопировано \"{{name}}\" в буфер обмена", - "shareTitle": "Поделиться сниппетом", - "shareUser": "Пользователь", - "shareRole": "Роль", - "selectUser": "Выберите пользователя...", - "selectRole": "Выберите роль...", - "shareSuccess": "Сниппет успешно поделился", - "shareFailed": "Не удалось поделиться сниппетом", - "revokeSuccess": "Доступ отозван", - "revokeFailed": "Не удалось отменить доступ", - "currentAccess": "Текущий доступ", - "shareLoadError": "Не удалось загрузить данные общего доступа", - "loading": "Загрузка...", - "close": "Закрыть", - "reorderFailed": "Не удалось сохранить порядок фрагментов." + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Аккаунт", - "sectionAppearance": "Внешний вид", - "sectionSecurity": "Безопасность", - "sectionApiKeys": "Ключи API", - "sectionC2sTunnels": "Туннели C2S", - "usernameLabel": "Имя пользователя", - "roleLabel": "Роль", - "roleAdministrator": "Администратор", - "authMethodLabel": "Метод аутентификации", - "authMethodLocal": "Локальный", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "На", - "twoFaOff": "Выкл", - "versionLabel": "Версии", - "deleteAccount": "Удалить аккаунт", - "deleteAccountDescription": "Окончательно удалить свой аккаунт", - "deleteButton": "Удалить", - "deleteAccountPermanent": "Это действие является постоянным и не может быть отменено.", - "deleteAccountWarning": "Все сессии, хосты, учетные данные и настройки будут удалены навсегда.", - "confirmPasswordDeletePlaceholder": "Введите ваш пароль для подтверждения", - "languageLabel": "Язык", - "themeLabel": "Тема", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Цвет акцента", - "settingsTerminal": "Терминал", - "commandAutocomplete": "Автозаполнение команды", - "commandAutocompleteDesc": "Показывать автозаполнение при вводе текста", - "historyTracking": "История отслеживания", - "historyTrackingDesc": "Отслеживать команды терминалов", - "syntaxHighlighting": "Подсветка синтаксиса", - "syntaxHighlightingDesc": "Выделить вывод терминала", - "commandPalette": "Палитра команд", - "commandPaletteDesc": "Включить горячую клавишу", - "reopenTabsOnLogin": "Открывайте вкладки повторно при входе в систему.", - "reopenTabsOnLoginDesc": "Восстанавливайте открытые вкладки при входе в систему или обновлении страницы, даже с другого устройства.", - "confirmTabClose": "Подтвердить закрытие вкладки", - "confirmTabCloseDesc": "Спрашивать перед закрытием вкладок терминала", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Показать теги хоста", - "showHostTagsDesc": "Отображать теги в списке узлов", - "hostTrayOnClick": "Нажмите, чтобы развернуть действия с хостом.", - "hostTrayOnClickDesc": "Всегда отображайте кнопки подключения; щелкните по ним, чтобы развернуть параметры управления, вместо того чтобы наводить курсор на них.", - "pinAppRail": "Панель приложений Pin", - "pinAppRailDesc": "Левая боковая панель приложения всегда должна быть развернута, а не расширяться при наведении курсора.", - "settingsSnippets": "Сниппеты", - "foldersCollapsed": "Папки свернуты", - "foldersCollapsedDesc": "Свернуть папки по умолчанию", - "confirmExecution": "Подтвердить исполнение", - "confirmExecutionDesc": "Подтвердить перед запуском сниппетов", - "settingsUpdates": "Обновления", - "disableUpdateChecks": "Отключить проверки обновлений", - "disableUpdateChecksDesc": "Остановить проверку обновлений", - "totpAuthenticator": "TOTP аутентификатор", - "totpEnabled": "2FA включено", - "totpDisabled": "Добавить дополнительную безопасность входа", - "disable": "Отключено", - "enable": "Включить", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Отсканируйте QR-код или введите секретный код в приложении для аутентификации, а затем введите 6-значный код", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Подтвердить", - "changePassword": "Изменить пароль", - "currentPasswordLabel": "Текущий пароль", - "currentPasswordPlaceholder": "Текущий пароль", - "newPasswordLabel": "Новый пароль", - "newPasswordPlaceholder": "Новый пароль", - "confirmPasswordLabel": "Подтвердите новый пароль", - "confirmPasswordPlaceholder": "Подтвердите новый пароль", - "updatePassword": "Обновить пароль", - "createApiKeyTitle": "Создать ключ API", - "createApiKeyDescription": "Генерировать новый ключ API для доступа к программам.", - "apiKeyNameLabel": "Наименование", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "опционально", - "cancel": "Отмена", - "createKey": "Создать ключ", - "apiKeyCount": "{{count}} ключи", - "newKey": "Новый ключ", - "noApiKeys": "Пока нет ключей API.", - "apiKeyActive": "Активный", - "apiKeyUsageHint": "Включить ваш ключ в", - "apiKeyUsageHintHeader": "заголовок.", - "apiKeyPermissionsHint": "Ключи наследуют права создаваемого пользователя.", - "roleUser": "Пользователь", - "authMethodDual": "Двойная авторизация", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Не удалось запустить настройку TOTP", - "totpEnter6Digits": "Введите 6-значный код", - "totpEnabledSuccess": "Двухфакторная аутентификация включена", - "totpInvalidCode": "Неверный код, попробуйте еще раз", - "totpDisableInputRequired": "Введите код TOTP или пароль", - "totpDisabledSuccess": "Двухфакторная аутентификация отключена", - "totpDisableFailed": "Не удалось отключить 2FA", - "totpDisableTitle": "Отключить 2FA", - "totpDisablePlaceholder": "Введите код TOTP или пароль", - "totpDisableConfirm": "Отключить 2FA", - "totpContinueVerify": "Продолжить проверку", - "totpVerifyTitle": "Проверить код", - "totpBackupTitle": "Резервные коды", - "totpDownloadBackup": "Скачать резервные коды", - "done": "Готово", - "secretCopied": "Секрет скопирован в буфер обмена", - "apiKeyNameRequired": "Требуется имя ключа", - "apiKeyCreated": "API ключ \"{{name}}\" создан", - "apiKeyCreateFailed": "Не удалось создать ключ API", - "apiKeyUser": "Пользователь", - "apiKeyExpires": "Истекает", - "apiKeyRevoked": "API ключ \"{{name}}\" отозван", - "apiKeyRevokeFailed": "Не удалось отозвать ключ API", - "passwordFieldsRequired": "Требуется текущий и новый пароль", - "passwordMismatch": "Пароли не совпадают", - "passwordTooShort": "Пароль должен содержать не менее 6 символов", - "passwordUpdated": "Пароль успешно обновлен", - "passwordUpdateFailed": "Не удалось обновить пароль", - "deletePasswordRequired": "Требуется пароль для удаления вашей учетной записи", - "deleteFailed": "Не удалось удалить аккаунт", - "deleting": "Удаление...", - "colorPickerTooltip": "Открыть выбор цвета", - "themeSystem": "Система", - "themeLight": "Светлая", - "themeDark": "Тёмная", - "themeDracula": "Дракула", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Соляризованный", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Один темный", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/sr_SP.json b/src/ui/locales/translated/sr_SP.json index 5eaf8982..9bc5af32 100644 --- a/src/ui/locales/translated/sr_SP.json +++ b/src/ui/locales/translated/sr_SP.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Фасцикле", - "folder": "Фасцикла", - "password": "Лозинка", - "key": "Кључ", - "sshPrivateKey": "SSH приватни кључ", - "upload": "Отпреми", - "keyPassword": "Кључна лозинка", - "sshKey": "SSH кључ", - "uploadPrivateKeyFile": "Отпреми датотеку приватног кључа", - "searchCredentials": "Претражите акредитиве...", - "addCredential": "Додај акредитив", - "caCertificate": "CA сертификат (-cert.pub)", - "caCertificateDescription": "Опционо: Отпремите или налепите датотеку сертификата потписану од стране CA (нпр. id_ed25519-cert.pub). Обавезно када ваш SSH сервер користи ауторизацију засновану на сертификату.", - "uploadCertFile": "Отпреми датотеку -cert.pub", - "clearCert": "Јасно", - "certLoaded": "Сертификат је учитан", - "certPublicKeyLabel": "CA сертификат", - "certTypeLabel": "Тип сертификата", - "pasteOrUploadCert": "Налепите или отпремите -cert.pub сертификат...", - "hasCaCert": "Има CA сертификат", - "noCaCert": "Нема CA сертификата" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Учитавање обавештења није успело", - "failedToDismissAlert": "Одбацивање упозорења није успело" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Конфигурација сервера", - "description": "Конфигуришите URL адресу Termix сервера да бисте се повезали са вашим бекенд сервисима", - "serverUrl": "URL сервера", - "enterServerUrl": "Молимо вас да унесете URL адресу сервера", - "saveFailed": "Чување конфигурације није успело", - "saveError": "Грешка при чувању конфигурације", - "saving": "Чување...", - "saveConfig": "Сачувај конфигурацију", - "helpText": "Унесите URL адресу на којој ради ваш Termix сервер (нпр. http://localhost:30001 или https://your-server.com)", - "changeServer": "Промени сервер", - "mustIncludeProtocol": "URL сервера мора да почиње са http:// или https://", - "allowInvalidCertificate": "Дозволи неважећи сертификат", - "allowInvalidCertificateDesc": "Користите само за поуздане самохостоване сервере са самопотписаним сертификатима или сертификатима за IP адресу.", - "useEmbedded": "Користи локални сервер", - "embeddedDesc": "Покрените Termix са уграђеним локалним сервером (није потребан удаљени сервер)", - "embeddedConnecting": "Повезивање са локалним сервером...", - "embeddedNotReady": "Локални сервер још није спреман. Молимо сачекајте тренутак и покушајте поново.", - "localServer": "Локални сервер" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Грешка при провери верзије", - "checkFailed": "Провера ажурирања није успела", - "upToDate": "Апликација је ажурирана", - "currentVersion": "Користите верзију {{version}}", - "updateAvailable": "Ажурирање је доступно", - "newVersionAvailable": "Нова верзија је доступна! Користите {{current}}, али је доступан {{latest}}.", - "betaVersion": "Бета верзија", - "betaVersionDesc": "Користите {{current}}, што је новије од најновије стабилне верзије {{latest}}.", - "releasedOn": "Објављено {{date}}", - "downloadUpdate": "Преузми ажурирање", - "checking": "Провера ажурирања...", - "checkUpdates": "Провери ажурирања", - "checkingUpdates": "Провера ажурирања...", - "updateRequired": "Потребно је ажурирање" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Затвори", - "minimize": "Минимизирај", - "online": "Онлајн", - "offline": "Офлајн", - "continue": "Настави", - "maintenance": "Одржавање", - "degraded": "Деградирано", - "error": "Грешка", - "warning": "Упозорење", - "unsavedChanges": "Несачуване промене", - "dismiss": "Одбаци", - "loading": "Учитавање...", - "optional": "Опционо", - "connect": "Повежи се", - "copied": "Копирано", - "connecting": "Повезивање...", - "updateAvailable": "Ажурирање је доступно", - "appName": "Термикс", - "openInNewTab": "Отвори у новој картици", - "noReleases": "Нема издања", - "updatesAndReleases": "Ажурирања и издања", - "newVersionAvailable": "Доступна је нова верзија ({{version}}).", - "failedToFetchUpdateInfo": "Није успело преузимање информација о ажурирању", - "preRelease": "Претходно издање", - "noReleasesFound": "Није пронађено ниједно издање.", - "cancel": "Откажи", - "username": "Корисничко име", - "login": "Пријава", - "register": "Региструј се", - "password": "Лозинка", - "confirmPassword": "Потврдите лозинку", - "back": "Назад", - "save": "Сачувај", - "saving": "Чување...", - "delete": "Обриши", - "rename": "Преименуј", - "edit": "Измени", - "add": "Додај", - "confirm": "Потврди", - "no": "Не", - "or": "ИЛИ", - "next": "Следеће", - "previous": "Претходно", - "refresh": "Освежи", - "language": "Језик", - "checking": "Провера...", - "checkingDatabase": "Провера везе са базом података...", - "checkingAuthentication": "Провера аутентификације...", - "backendReconnected": "Веза са сервером је враћена", - "connectionDegraded": "Веза са сервером је прекинута, враћање везе…", - "reload": "Поново учитај", - "remove": "Уклони", - "create": "Креирај", - "update": "Ажурирање", - "copy": "Копија", - "copyFailed": "Копирање у међуспремник није успело", - "maximize": "Максимизирај", - "restore": "Врати", - "of": "од" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Дом", - "terminal": "Терминал", - "docker": "Докер", - "tunnels": "Тунели", - "fileManager": "Менаџер датотека", - "serverStats": "Статистика сервера", - "admin": "Администратор", - "userProfile": "Кориснички профил", - "splitScreen": "Подељени екран", - "confirmClose": "Да ли желите да затворите ову активну сесију?", - "close": "Затвори", - "cancel": "Откажи", - "sshManager": "SSH менаџер", - "cannotSplitTab": "Није могуће поделити ову картицу", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Копирај лозинку", - "copySudoPassword": "Копирај Судо лозинку", - "passwordCopied": "Лозинка је копирана у међуспремник", - "noPasswordAvailable": "Није доступна лозинка", - "failedToCopyPassword": "Копирање лозинке није успело", - "refreshTab": "Освежи везу", - "openFileManager": "Отвори менаџер датотека", - "dashboard": "Контролна табла", - "networkGraph": "Мрежни графикон", - "quickConnect": "Брзо повезивање", - "sshTools": "SSH алати", - "history": "Историја", - "hosts": "Домаћини", - "snippets": "Исечци", - "hostManager": "Менаџер домаћина", - "credentials": "Акредитиви", - "connections": "Везе", - "roleAdministrator": "Администратор", - "roleUser": "Корисник" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Домаћини", - "noHosts": "Нема SSH хостова", - "retry": "Покушај поново", - "refresh": "Освежи", - "optional": "Опционо", - "downloadSample": "Преузми узорак", - "failedToDeleteHost": "Брисање {{name}} није успело", - "importSkipExisting": "Увези (прескочи постојеће)", - "connectionDetails": "Детаљи везе", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", - "telnet": "Телнет", - "remoteDesktop": "Удаљена радна површина", - "port": "Лука", - "username": "Корисничко име", - "folder": "Фасцикла", - "tags": "Ознаке", - "pin": "Закачи", - "addHost": "Додај хоста", - "editHost": "Измени хоста", - "cloneHost": "Клонирај хост", - "enableTerminal": "Омогући терминал", - "enableTunnel": "Омогући тунел", - "enableFileManager": "Омогући менаџер датотека", - "enableDocker": "Омогући Докер", - "defaultPath": "Подразумевана путања", - "connection": "Веза", - "upload": "Отпреми", - "authentication": "Аутентификација", - "password": "Лозинка", - "key": "Кључ", - "credential": "Акредитив", - "none": "Ниједан", - "sshPrivateKey": "SSH приватни кључ", - "keyType": "Тип кључа", - "uploadFile": "Отпреми датотеку", - "tabGeneral": "Опште", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", - "tabRdp": "РДП", + "tabTerminal": "Terminal", + "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Тунели", - "tabDocker": "Докер", - "tabFiles": "Датотеке", - "tabStats": "Статистика", - "tabTelnet": "Телнет", - "tabSharing": "Дељење", - "tabAuthentication": "Аутентификација", - "terminal": "Терминал", - "tunnel": "Тунел", - "fileManager": "Менаџер датотека", - "serverStats": "Статистика сервера", - "status": "Статус", - "folderRenamed": "Фасцикла „{{oldName}}“ је успешно преименована у „{{newName}}“", - "failedToRenameFolder": "Преименовање фасцикле није успело", - "movedToFolder": "Премештено у „{{folder}}“", - "editHostTooltip": "Измени хоста", - "statusChecks": "Провере статуса", - "metricsCollection": "Колекција метрика", - "metricsInterval": "Интервал прикупљања метрика", - "metricsIntervalDesc": "Колико често прикупљати статистику сервера (5 с - 1 сат)", - "behavior": "Понашање", - "themePreview": "Преглед теме", - "theme": "Тема", - "fontFamily": "Породица фонтова", - "fontSize": "Величина фонта", - "letterSpacing": "Размак између слова", - "lineHeight": "Висина линије", - "cursorStyle": "Стил курсора", - "cursorBlink": "Трептање курсора", - "scrollbackBuffer": "Бафер за померање уназад", - "bellStyle": "Стил звона", - "rightClickSelectsWord": "Десни клик бира реч", - "fastScrollModifier": "Модификатор брзог скроловања", - "fastScrollSensitivity": "Осетљивост брзог скроловања", - "sshAgentForwarding": "Прослеђивање SSH агента", - "backspaceMode": "Режим брзе плоче", - "startupSnippet": "Исечак за покретање", - "selectSnippet": "Изаберите исечак", - "forceKeyboardInteractive": "Присилна интерактивност тастатуре", - "overrideCredentialUsername": "Замени корисничко име за акредитиве", - "overrideCredentialUsernameDesc": "Користите корисничко име наведено горе уместо корисничког имена акредитива", - "jumpHostChain": "Скок ланца хоста", - "portKnocking": "Лук Нокинг", - "addKnock": "Додај порт", - "addProxyNode": "Додај чвор", - "proxyNode": "Прокси чвор", - "proxyType": "Тип проксија", - "quickActions": "Брзе акције", - "sudoPasswordAutoFill": "Аутоматско попуњавање лозинке за Судо", - "sudoPassword": "Судо лозинка", - "keepaliveInterval": "Интервал одржавања активности (ms)", - "moshCommand": "MOSH команда", - "environmentVariables": "Променљиве окружења", - "addVariable": "Додај променљиву", - "docker": "Докер", - "copyTerminalUrl": "Копирај URL терминала", - "copyFileManagerUrl": "Копирај URL менаџера датотека", - "copyRemoteDesktopUrl": "Копирај URL адресу удаљене радне површине", - "failedToConnect": "Повезивање са конзолом није успело", - "connect": "Повежи се", - "disconnect": "Прекини везу", - "start": "Почетак", - "enableStatusCheck": "Омогући проверу статуса", - "enableMetrics": "Омогући метрике", - "bulkUpdateFailed": "Групно ажурирање није успело", - "selectAll": "Изабери све", - "deselectAll": "Поништи избор свих", - "protocols": "Протоколи", - "secureShell": "Безбедна шкољка", - "virtualNetwork": "Виртуелна мрежа", - "unencryptedShell": "Нешифрована љуска", - "addressIp": "Адреса / ИП", - "friendlyName": "Пријатељско име", - "folderAndAdvanced": "Фолдер и напредно", - "privateNotes": "Приватне белешке", - "privateNotesPlaceholder": "Детаљи о овом серверу...", - "pinToTop": "Закачи на врх", - "pinToTopDesc": "Увек приказуј овог хоста на врху листе", - "portKnockingSequence": "Секвенца куцања порта", - "addKnockBtn": "Додај куцање", - "noPortKnocking": "Није конфигурисано пребацивање портова на другу страну (port knocking).", - "knockPort": "Нок Порт", - "protocol": "Протокол", - "delayAfterMs": "Кашњење после (ms)", - "useSocks5Proxy": "Користите SOCKS5 прокси", - "useSocks5ProxyDesc": "Усмерите везу преко прокси сервера", - "proxyHost": "Прокси хост", - "proxyPort": "Прокси порт", - "proxyUsername": "Корисничко име проксија", - "proxyPassword": "Лозинка проксија", - "proxySingleMode": "Један прокси", - "proxyChainMode": "Прокси ланац", - "you": "Ти", - "jumpHostChainLabel": "Скок ланца хоста", - "addJumpBtn": "Додај скок", - "noJumpHosts": "Није конфигурисан ниједан хост за прелазак.", - "selectAServer": "Изаберите сервер...", - "sshPort": "SSH порт", - "authMethod": "Метода овлашћења", - "storedCredential": "Сачувани акредитив", - "selectACredential": "Изаберите акредитив...", - "keyTypeLabel": "Тип кључа", - "keyTypeAuto": "Аутоматско откривање", - "keyPasteTab": "Залепи", - "keyUploadTab": "Отпреми", - "keyFileLoaded": "Кључна датотека је учитана", - "keyUploadClick": "Кликните да бисте отпремили .pem / .key / .ppk", - "clearKey": "Обриши тастер", - "keySaved": "SSH кључ је сачуван", - "keyReplaceNotice": "Налепите нови кључ испод да бисте га заменили", - "keyPassphraseSaved": "Лозинка је сачувана, унесите текст да бисте је променили", - "replaceKey": "Замените кључ", - "forceKeyboardInteractiveLabel": "Присилна интерактивност тастатуре", - "forceKeyboardInteractiveShortDesc": "Присилите ручни унос лозинке чак и ако су кључеви присутни", - "terminalAppearance": "Изглед терминала", - "colorTheme": "Тема боја", - "fontFamilyLabel": "Породица фонтова", - "fontSizeLabel": "Величина фонта", - "cursorStyleLabel": "Стил курсора", - "letterSpacingPx": "Размак између слова (px)", - "lineHeightLabel": "Висина линије", - "bellStyleLabel": "Стил звона", - "backspaceModeLabel": "Режим брзе плоче", - "cursorBlinking": "Курсор трепери", - "cursorBlinkingDesc": "Омогући трепћућу анимацију за курсор терминала", - "rightClickSelectsWordLabel": "Десни клик бира реч", - "rightClickSelectsWordShortDesc": "Изаберите реч испод курсора кликом десним тастером миша", - "behaviorAndAdvanced": "Понашање и напредно", - "scrollbackBufferLabel": "Бафер за померање уназад", - "scrollbackMaxLines": "Максималан број линија сачуваних у историји", - "sshAgentForwardingLabel": "Прослеђивање SSH агента", - "sshAgentForwardingShortDesc": "Проследите своје локалне SSH кључеве овом хосту", - "enableAutoMosh": "Омогући аутоматско покретање", - "enableAutoMoshDesc": "Преферирај Mosh у односу на SSH ако је доступан", - "enableAutoTmux": "Омогући аутоматски Tmux", - "enableAutoTmuxDesc": "Аутоматски покрени или прикључи се tmux сесији", - "sudoPasswordAutoFillLabel": "Аутоматско попуњавање лозинке за Sudo", - "sudoPasswordAutoFillShortDesc": "Аутоматски унесите лозинку за судо када се то затражи", - "sudoPasswordLabel": "Судо лозинка", - "environmentVariablesLabel": "Променљиве окружења", - "addVariableBtn": "Додај променљиву", - "noEnvVars": "Ниједна променљива окружења није конфигурисана.", - "fastScrollModifierLabel": "Модификатор брзог скроловања", - "fastScrollSensitivityLabel": "Осетљивост брзог скроловања", - "moshCommandLabel": "Мош команда", - "startupSnippetLabel": "Исечак за покретање", - "keepaliveIntervalLabel": "Интервал одржавања активности (секунде)", - "maxKeepaliveMisses": "Максимални промашаји у Keepalive-у", - "tunnelSettings": "Подешавања тунела", - "enableTunneling": "Омогући тунелирање", - "enableTunnelingDesc": "Омогући функционалност SSH тунела за овај хост", - "serverTunnelsSection": "Серверски тунели", - "addTunnelBtn": "Додај тунел", - "noTunnelsConfigured": "Није конфигурисан ниједан тунел.", - "tunnelLabel": "Тунел {{number}}", - "tunnelType": "Тип тунела", - "tunnelModeLocalDesc": "Прослеђује локални порт на порт на удаљеном серверу (или хосту који је доступан са њега).", - "tunnelModeRemoteDesc": "Проследите порт на удаљеном серверу назад на локални порт на вашој машини.", - "tunnelModeDynamicDesc": "Направите SOCKS5 прокси на локалном порту за динамичко прослеђивање портова.", - "sameHost": "Овај хост (директни тунел)", - "endpointHost": "Крајњи хост", - "endpointPort": "Порт крајње тачке", - "bindHost": "Повежи хост", - "sourcePort": "Изворни порт", - "maxRetries": "Максималан број поновних покушаја", - "retryIntervalS": "Интервал поновног покушаја (s)", - "autoStartLabel": "Аутоматско покретање", - "autoStartDesc": "Аутоматски повежи овај тунел када се хост учита", - "tunnelConnecting": "Тунел који се повезује...", - "tunnelDisconnected": "Тунел је искључен", - "failedToConnectTunnel": "Повезивање није успело", - "failedToDisconnectTunnel": "Прекид везе није успео", - "dockerIntegration": "Интеграција Докера", - "enableDockerMonitor": "Омогући Докер", - "enableDockerMonitorDesc": "Пратите и управљајте контејнерима на овом хосту путем Докера", - "enableFileManagerMonitor": "Омогући менаџер датотека", - "enableFileManagerMonitorDesc": "Прегледајте и управљајте датотекама на овом хосту преко SFTP-а", - "defaultPathLabel": "Подразумевана путања", - "fileManagerPathHint": "Директоријум који треба отворити када се покрене менаџер датотека за овај хост.", - "statusChecksLabel": "Провере статуса", - "enableStatusChecks": "Омогући провере статуса", - "enableStatusChecksDesc": "Периодично пингујте овај хост да бисте проверили доступност", - "useGlobalInterval": "Користи глобални интервал", - "useGlobalIntervalDesc": "Замени интервал провере статуса на нивоу сервера", - "checkIntervalS": "Интервал провере(е)", - "checkIntervalDesc": "Секунде између сваког пинга за повезивање", - "metricsCollectionLabel": "Колекција метрика", - "enableMetricsLabel": "Омогући метрике", - "enableMetricsDesc": "Прикупљајте податке о коришћењу процесора, RAM-а, диска и мреже са овог хоста", - "useGlobalMetrics": "Користи глобални интервал", - "useGlobalMetricsDesc": "Замени интервалом метрика на нивоу сервера", - "metricsIntervalS": "Интервал(и) метрика", - "metricsIntervalDesc2": "Секунде између снимака метрика", - "visibleWidgets": "Видљиви виџети", - "cpuUsageLabel": "Искоришћеност процесора", - "cpuUsageDesc": "Проценат процесора, просечно оптерећење, графикон са блистер кривином", - "memoryLabel": "Коришћење меморије", - "memoryDesc": "Коришћење РАМ-а, размена, кеширана меморија", - "storageLabel": "Искоришћеност диска", - "storageDesc": "Коришћење диска по тачки монтирања", - "networkLabel": "Мрежни интерфејси", - "networkDesc": "Листа интерфејса и пропусни опсег", - "uptimeLabel": "Време непрекидног рада", - "uptimeDesc": "Време рада система и време покретања", - "systemInfoLabel": "Информације о систему", - "systemInfoDesc": "ОС, језгро, име хоста, архитектура", - "recentLoginsLabel": "Недавне пријаве", - "recentLoginsDesc": "Успешни и неуспешни догађаји пријављивања", - "topProcessesLabel": "Најважнији процеси", - "topProcessesDesc": "ПИД, ЦПУ%, МЕМОРИЈА%, команда", - "listeningPortsLabel": "Портови за слушање", - "listeningPortsDesc": "Отворени портови са процесом и стањем", - "firewallLabel": "Заштитни зид (фајервол)", - "firewallDesc": "Статус заштитног зида, AppArmor-а, SELinux-а", - "quickActionsLabel": "Брзе акције", - "quickActionsToolbar": "Брзе акције се појављују као дугмад у траци са алаткама Статистика сервера за извршавање команди једним кликом.", - "noQuickActions": "Још нема брзих акција.", - "buttonLabel": "Ознака дугмета", - "selectSnippetPlaceholder": "Изаберите исечак...", - "addActionBtn": "Додај акцију", - "hostSharedSuccessfully": "Хост је успешно дељен", - "failedToShareHost": "Дељење хоста није успело", - "accessRevoked": "Приступ је опозван", - "failedToRevokeAccess": "Опозивање приступа није успело", - "cancelBtn": "Откажи", - "savingBtn": "Чување...", - "addHostBtn": "Додај хоста", - "hostUpdated": "Хост је ажуриран", - "hostCreated": "Хост је креиран", - "failedToSave": "Чување хоста није успело", - "credentialUpdated": "Акредитив је ажуриран", - "credentialCreated": "Акредитив је креиран", - "failedToSaveCredential": "Чување акредитива није успело", - "backToHosts": "Назад на хостове", - "backToCredentials": "Назад на акредитиве", - "pinned": "Закачено", - "noHostsFound": "Нису пронађени хостови", - "tryDifferentTerm": "Пробајте други термин", - "addFirstHost": "Додајте свог првог хоста да бисте започели", - "noCredentialsFound": "Нису пронађени акредитиви", - "addCredentialBtn": "Додај акредитив", - "updateCredentialBtn": "Ажурирај акредитив", - "features": "Карактеристике", - "noFolder": "(Нема фасцикле)", - "deleteSelected": "Обриши", - "exitSelection": "Излаз из селекције", - "importSkip": "Увези (прескочи постојеће)", - "importOverwrite": "Увези (препиши)", - "collapseBtn": "Склопи", - "importExportBtn": "Увоз / Извоз", - "hostStatusesRefreshed": "Статуси хоста освежени", - "failedToRefreshHosts": "Освежавање хостова није успело", - "movedHostTo": "Премештено {{host}} у „{{folder}}“", - "failedToMoveHost": "Премештање хоста није успело", - "folderRenamedTo": "Фасцикла је преименована у „{{name}}“", - "deletedFolder": "Обрисана фасцикла „{{name}}“", - "failedToDeleteFolder": "Брисање фасцикле није успело", - "deleteAllInFolder": "Обрисати све хостове у „{{name}}“? Ово се не може поништити.", - "deletedHost": "Обрисано {{name}}", - "copiedToClipboard": "Копирано у међуспремник", - "terminalUrlCopied": "URL терминала је копиран", - "fileManagerUrlCopied": "URL менаџера датотека је копиран", - "tunnelUrlCopied": "URL тунела је копиран", - "dockerUrlCopied": "URL адреса Докера је копирана", - "serverStatsUrlCopied": "URL за статистику сервера је копиран", - "rdpUrlCopied": "RDP URL је копиран", - "vncUrlCopied": "VNC URL је копиран", - "telnetUrlCopied": "УРЛ адреса Телнета је копирана", - "remoteDesktopUrlCopied": "URL удаљене радне површине копиран", - "expandActions": "Прошири акције", - "collapseActions": "Скупи радње", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Магични пакет послат на {{name}}", - "wakeOnLanError": "Слање магичног пакета није успело", - "cloneHostAction": "Клонирај хост", - "copyAddress": "Копирај адресу", - "copyLink": "Копирај линк", - "copyTerminalUrlAction": "Копирај URL терминала", - "copyFileManagerUrlAction": "Копирај URL менаџера датотека", - "copyTunnelUrlAction": "Копирај URL тунела", - "copyDockerUrlAction": "Копирај Докер УРЛ", - "copyServerStatsUrlAction": "Копирај URL статистике сервера", - "copyRdpUrlAction": "Копирај RDP URL", - "copyVncUrlAction": "Копирај VNC URL", - "copyTelnetUrlAction": "Копирај Telnet URL", - "copyRemoteDesktopUrlAction": "Копирај URL адресу удаљене радне површине", - "deleteCredentialConfirm": "Обришите акредитив „{{name}}“?", - "deletedCredential": "Обрисано {{name}}", - "deploySSHKeyTitle": "Примена SSH кључа", - "deployingBtn": "Распоређивање...", - "deployBtn": "Распореди", - "failedToDeployKey": "Примена кључа није успела", - "deleteHostsConfirm": "Обриши {{count}} хост{{plural}}? Ово се не може поништити.", - "movedToRoot": "Премештено у корен", - "failedToMoveHosts": "Премештање хостова није успело", - "enableTerminalFeature": "Омогући терминал", - "disableTerminalFeature": "Онемогући терминал", - "enableFilesFeature": "Омогући датотеке", - "disableFilesFeature": "Онемогући датотеке", - "enableTunnelsFeature": "Омогући тунеле", - "disableTunnelsFeature": "Онемогући тунеле", - "enableDockerFeature": "Омогући Докер", - "disableDockerFeature": "Онемогући Докер", - "addTagsPlaceholder": "Додај ознаке...", - "authDetails": "Детаљи аутентификације", - "credType": "Тип", - "generateKeyPairDesc": "Генеришите нови пар кључева, и приватни и јавни кључеви ће бити аутоматски попуњени.", - "generatingKey": "Генерисање...", - "generateLabel": "Генериши {{label}}", - "uploadFileBtn": "Отпреми датотеку", - "keyPassphraseOptional": "Кључна лозинка (опционо)", - "sshPublicKeyOptional": "SSH јавни кључ (опционо)", - "publicKeyGenerated": "Јавни кључ генерисан", - "failedToGeneratePublicKey": "Извођење јавног кључа није успело", - "publicKeyCopied": "Јавни кључ је копиран", - "keyPairGenerated": "{{label}} генерисан пар кључева", - "failedToGenerateKeyPair": "Генерисање пара кључева није успело", - "searchHostsPlaceholder": "Претрага хостова, адреса, ознака…", - "searchCredentialsPlaceholder": "Претрага акредитива…", - "refreshBtn": "Освежи", - "addTag": "Додај ознаке...", - "deleteConfirmBtn": "Обриши", - "tunnelRequirementsText": "SSH сервер мора имати подешена подешавања за GatewayPorts, AllowTcpForwarding и PermitRootLogin у /etc/ssh/sshd_config.", - "deleteHostConfirm": "Обрисати „{{name}}“?", - "enableAtLeastOneProtocol": "Омогућите барем један протокол изнад да бисте конфигурисали подешавања аутентификације и повезивања.", - "keyPassphrase": "Кључна лозинка", - "connectBtn": "Повежи се", - "disconnectBtn": "Прекини везу", - "basicInformation": "Основне информације", - "authDetailsSection": "Детаљи аутентификације", - "credTypeLabel": "Тип", - "hostsTab": "Домаћини", - "credentialsTab": "Акредитиви", - "selectMultiple": "Изаберите више", - "selectHosts": "Изаберите хостове", - "connectionLabel": "Веза", - "authenticationLabel": "Аутентификација", - "generateKeyPairTitle": "Генериши пар кључева", - "generateKeyPairDescription": "Генеришите нови пар кључева, и приватни и јавни кључеви ће бити аутоматски попуњени.", - "generateFromPrivateKey": "Генериши из приватног кључа", - "refreshBtn2": "Освежи", - "exitSelectionTitle": "Излаз из селекције", - "exportAll": "Извези све", - "addHostBtn2": "Додај хоста", - "addCredentialBtn2": "Додај акредитив", - "checkingHostStatuses": "Провера статуса хоста...", - "pinnedSection": "Закачено", - "hostsExported": "Хостови су успешно извезени", - "exportFailed": "Извоз хостова није успео", - "sampleDownloaded": "Пример датотеке је преузет", - "failedToDeleteCredential2": "Брисање акредитива није успело", - "noFolderOption": "(Нема фасцикле)", - "nSelected": "{{count}} изабрано", - "featuresMenu": "Карактеристике", - "moveMenu": "Помери", - "cancelSelection": "Откажи", - "deployDialogDesc": "Распореди {{name}} на authorized_keys хоста.", - "targetHostLabel": "Циљни хост", - "selectHostOption": "Изаберите хост...", - "keyDeployedSuccess": "Кључ је успешно распоређен", - "failedToDeployKey2": "Примена кључа није успела", - "deletedCount": "Обрисани {{count}} хостови", - "failedToDeleteCount": "Брисање {{count}} хостова није успело", - "duplicatedHost": "Дуплирано „{{name}}“", - "failedToDuplicateHost": "Дуплирање хоста није успело", - "updatedCount": "Ажурирани {{count}} хостови", - "friendlyNameLabel": "Пријатељско име", - "descriptionLabel": "Опис", - "loadingHost": "Учитавање хоста...", - "loadingHosts": "Учитавање хостова...", - "loadingCredentials": "Учитавање акредитива...", - "noHostsYet": "Још нема домаћина", - "noHostsMatchSearch": "Ниједан хост не одговара вашој претрази", - "hostNotFound": "Хост није пронађен", - "searchHosts": "Претражи хостове...", - "sortHosts": "Сортирај хостове", - "sortDefault": "Подразумевани редослед", - "sortNameAsc": "Име (А → Ш)", - "sortNameDesc": "Име (Z → A)", - "sortIpAsc": "IP адреса (узлазно)", - "sortIpDesc": "IP адреса (опис)", - "sortOnlineFirst": "Прво онлајн", - "sortOfflineFirst": "Прво офлајн", - "sortPinnedFirst": "Прво закачено", - "filterHosts": "Филтрирај хостове", - "filterClearAll": "Обриши филтере", - "filterStatusGroup": "Статус", - "filterOnline": "Онлајн", - "filterOffline": "Офлајн", - "filterPinned": "Закачено", - "filterAuthGroup": "Тип овлашћења", - "filterAuthPassword": "Лозинка", - "filterAuthKey": "SSH кључ", - "filterAuthCredential": "Акредитив", - "filterAuthNone": "Ниједан", - "filterAuthOpkssh": "ОПКШ", - "filterProtocolGroup": "Протокол", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "РДП", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Телнет", - "filterFeaturesGroup": "Карактеристике", - "filterFeatureTerminal": "Терминал", - "filterFeatureFileManager": "Менаџер датотека", - "filterFeatureTunnel": "Тунел", - "filterFeatureDocker": "Докер", - "filterTagsGroup": "Ознаке", - "shareHost": "Дели хост", - "shareHostTitle": "Подели: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Овај хост мора да користи акредитив да би омогућио дељење. Прво измените хост и доделите акредитив." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Веза", - "authentication": "Аутентификација", - "connectionSettings": "Подешавања везе", - "displaySettings": "Подешавања екрана", - "audioSettings": "Подешавања звука", - "rdpPerformance": "Перформансе РДП-а", - "deviceRedirection": "Преусмеравање уређаја", - "session": "Сесија", - "gateway": "Капија", - "remoteApp": "Удаљена апликација", - "clipboard": "Међуспремник", - "sessionRecording": "Снимање сесије", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC подешавања", - "terminalSettings": "Подешавања терминала", - "rdpPort": "РДП порт", - "username": "Корисничко име", - "password": "Лозинка", - "domain": "Домен", - "securityMode": "Безбедносни режим", - "colorDepth": "Дубина боје", - "width": "Ширина", - "height": "Висина", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Метод промене величине", - "clientName": "Име клијента", - "initialProgram": "Почетни програм", - "serverLayout": "Распоред сервера", - "timezone": "Временска зона", - "gatewayHostname": "Име хоста мрежног пролаза", - "gatewayPort": "Порт за пролаз", - "gatewayUsername": "Корисничко име на гејтвеју", - "gatewayPassword": "Лозинка за пролаз", - "gatewayDomain": "Домен капије", - "remoteAppProgram": "Програм RemoteApp", - "workingDirectory": "Радни директоријум", - "arguments": "Аргументи", - "normalizeLineEndings": "Нормализуј завршетке линија", - "recordingPath": "Путања снимања", - "recordingName": "Назив снимка", - "macAddress": "MAC адреса", - "broadcastAddress": "Адреса за емитовање", - "udpPort": "UDP порт", - "waitTimeS": "Време чекања (s)", - "driveName": "Назив диска", - "drivePath": "Путања диска", - "ignoreCertificate": "Игнориши сертификат", - "ignoreCertificateDesc": "Дозволи везе са хостовима са самопотписаним сертификатима", - "forceLossless": "Присилно без губитака", - "forceLosslessDesc": "Присилно кодирање слике без губитака (виши квалитет, већи пропусни опсег)", - "disableAudio": "Онемогући звук", - "disableAudioDesc": "Искључите сав звук из удаљене сесије", - "enableAudioInput": "Омогући аудио улаз (микрофон)", - "enableAudioInputDesc": "Прослеђивање локалног микрофона на удаљену сесију", - "wallpaper": "Позадина", - "wallpaperDesc": "Прикажи позадину радне површине (онемогућавање побољшава перформансе)", - "theming": "Темирање", - "themingDesc": "Омогући визуелне теме и стилове", - "fontSmoothing": "Изглађивање фонта", - "fontSmoothingDesc": "Омогући приказивање ClearType фонта", - "fullWindowDrag": "Превлачење преко целог прозора", - "fullWindowDragDesc": "Приказ садржаја прозора током превлачења", - "desktopComposition": "Композиција на радној површини", - "desktopCompositionDesc": "Омогући ефекте Aero Glass-а", - "menuAnimations": "Анимације менија", - "menuAnimationsDesc": "Омогући анимације постепеног постепеног померања менија и слајдова", - "disableBitmapCaching": "Онемогући кеширање битмапа", - "disableBitmapCachingDesc": "Искључите кеш битмапа (може помоћи код грешака)", - "disableOffscreenCaching": "Онемогући кеширање ван екрана", - "disableOffscreenCachingDesc": "Искључите кеш меморију ван екрана", - "disableGlyphCaching": "Онемогући кеширање глифова", - "disableGlyphCachingDesc": "Искључите кеш глифова", - "enableGfx": "Омогући графичке ефекте", - "enableGfxDesc": "Користите графички цевовод RemoteFX", - "enablePrinting": "Омогући штампање", - "enablePrintingDesc": "Преусмерите локалне штампаче на удаљену сесију", - "enableDriveRedirection": "Омогући преусмеравање диска", - "enableDriveRedirectionDesc": "Мапирајте локалну фасциклу као диск у удаљеној сесији", - "createDrivePath": "Креирај путању диска", - "createDrivePathDesc": "Аутоматски креирај фасциклу ако не постоји", - "disableDownload": "Онемогући преузимање", - "disableDownloadDesc": "Спречите преузимање датотека са удаљене сесије", - "disableUpload": "Онемогући отпремање", - "disableUploadDesc": "Спречите отпремање датотека на удаљену сесију", - "enableTouch": "Омогући додир", - "enableTouchDesc": "Омогући преусмеравање уноса додиром", - "consoleSession": "Конзолна сесија", - "consoleSessionDesc": "Повежите се са конзолом (сесија 0) уместо нове сесије", - "sendWolPacket": "Пошаљи WOL пакет", - "sendWolPacketDesc": "Пошаљите магични пакет да пробудите овај хост пре повезивања", - "disableCopy": "Онемогући копирање", - "disableCopyDesc": "Спречите копирање текста из удаљене сесије", - "disablePaste": "Онемогући лепљење", - "disablePasteDesc": "Спречите лепљење текста у удаљену сесију", - "createPathIfMissing": "Креирај путању ако недостаје", - "createPathIfMissingDesc": "Аутоматски креирајте директоријум за снимање", - "excludeOutput": "Искључи излаз", - "excludeOutputDesc": "Не снимај излаз екрана (само метаподаци)", - "excludeMouse": "Искључи миш", - "excludeMouseDesc": "Не снимај покрете миша", - "includeKeystrokes": "Укључи притиске на тастатури", - "includeKeystrokesDesc": "Снимање сирових притиска тастера поред излаза на екран", - "vncPort": "VNC порт", - "vncPassword": "VNC лозинка", - "vncUsernameOptional": "Корисничко име (опционо)", - "vncLeaveBlank": "Оставите празно ако није потребно", - "cursorMode": "Режим курсора", - "swapRedBlue": "Замена црвене/плаве", - "swapRedBlueDesc": "Замена црвених и плавих канала боја (решава неке проблеме са бојама)", - "readOnly": "Само за читање", - "readOnlyDesc": "Погледајте удаљени екран без слања било каквог уноса", - "telnetPort": "Телнет порт", - "terminalType": "Тип терминала", - "fontName": "Назив фонта", - "fontSize": "Величина фонта", - "colorScheme": "Шема боја", - "backspaceKey": "Тастер Backspace", - "saveHostFirst": "Прво сачувајте домаћина.", - "sharingOptionsAfterSave": "Опције дељења су доступне након што је хост сачуван.", - "sharingLoadError": "Учитавање података за дељење није успело. Проверите везу и покушајте поново.", - "shareHostSection": "Дели хост", - "shareWithUser": "Подели са корисником", - "shareWithRole": "Дели са улогом", - "selectUser": "Изаберите корисника", - "selectRole": "Изаберите улогу", - "selectUserOption": "Изаберите корисника...", - "selectRoleOption": "Изаберите улогу...", - "permissionLevel": "Ниво дозволе", - "expiresInHours": "Истиче за (сати)", - "noExpiryPlaceholder": "Оставите празно ако нема рока истека", - "shareBtn": "Дели", - "currentAccess": "Тренутни приступ", - "typeHeader": "Тип", - "targetHeader": "Циљ", - "permissionHeader": "Дозвола", - "grantedByHeader": "Доделио/ла", - "expiresHeader": "Истиче", - "noAccessEntries": "Још нема уноса за приступ.", - "expiredLabel": "Истекло", - "neverLabel": "Никад", - "revokeBtn": "Поништи", - "cancelBtn": "Откажи", - "savingBtn": "Чување...", - "updateHostBtn": "Ажурирај хост", - "addHostBtn": "Додај хоста" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Претражите хостове, команде или подешавања...", - "quickActions": "Брзе акције", - "hostManager": "Менаџер домаћина", - "hostManagerDesc": "Управљајте, додајте или измените хостове", - "addNewHost": "Додај новог хоста", - "addNewHostDesc": "Региструјте новог хоста", - "adminSettings": "Администраторска подешавања", - "adminSettingsDesc": "Конфигуришите системске поставке и кориснике", - "userProfile": "Кориснички профил", - "userProfileDesc": "Управљајте својим налогом и подешавањима", - "addCredential": "Додај акредитив", - "addCredentialDesc": "Чувајте SSH кључеве или лозинке", - "recentActivity": "Недавне активности", - "serversAndHosts": "Сервери и хостови", - "noHostsFound": "Није пронађен ниједан хост који одговара „{{search}}“", - "links": "Линкови", - "navigate": "Навигација", - "select": "Изаберите", - "toggleWith": "Укључи/искључи" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Окно {{index}} - празно", - "noTabAssigned": "Није додељен таб", - "focusedPane": "Активни панел" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Нема веза", - "noConnectionsDesc": "Отворите терминал, менаџер датотека или удаљену радну површину да бисте видели везе овде", - "connectedFor": "Повезано за {{duration}}", - "connected": "Повезано", - "disconnected": "Искључено", - "closeTab": "Затвори картицу", - "closeConnection": "Затвори везу", - "forgetTab": "Заборави", - "removeBackground": "Уклони", - "reconnect": "Поново се повежите", - "reopenTab": "Поново отвори", - "sectionOpen": "Отворено", - "sectionBackground": "Позадина", - "backgroundDesc": "Сесије трају 30 минута након прекида везе и могу се поново повезати.", - "persisted": "Истрајало у позадини", - "expiresIn": "Истиче за {{duration}}", - "search": "Претражи везе...", - "noSearchResults": "Ниједна веза не одговара вашој претрази" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Повезивање са {{type}} сесијом...", - "connectionError": "Грешка у повезивању", - "connectionFailed": "Повезивање није успело", - "failedToConnect": "Није успело добијање токена за повезивање", - "hostNotFound": "Хост није пронађен", - "noHostSelected": "Није изабран домаћин", - "reconnect": "Поново се повежите", - "retry": "Покушај поново", - "guacdUnavailable": "Услуга удаљеног рачунара (guacd) није доступна. Молимо вас да се уверите да је guacd покренут, доступан и правилно конфигурисан у администраторским подешавањима.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Закључавање екрана)", - "winKey": "Windows тастер", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "Алтернативно", - "shift": "Померање", - "win": "Победа", - "stickyActive": "{{key}} (закључано - кликните да бисте отпустили)", - "stickyInactive": "{{key}} (кликните да бисте закључали)", - "esc": "Бег", - "tab": "Таб", - "home": "Дом", - "end": "Крај", - "pageUp": "Страница нагоре", - "pageDown": "Страница надоле", - "arrowUp": "Стрелица нагоре", - "arrowDown": "Стрелица надоле", - "arrowLeft": "Стрелица лево", - "arrowRight": "Стрелица десно", - "fnToggle": "Функцијски тастери", - "reconnect": "Поновно повезивање сесије", - "collapse": "Савиј траку са алаткама", - "expand": "Прошири траку са алаткама", - "dragHandle": "Превуците да бисте променили положај" + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Повежи се са хостом", - "clear": "Јасно", - "paste": "Залепи", - "reconnect": "Поново се повежите", - "connectionLost": "Веза је прекинута", - "connected": "Повезано", - "clipboardWriteFailed": "Копирање у међуспремник није успело. Уверите се да се страница приказује преко HTTPS-а или localhost-а.", - "clipboardReadFailed": "Читање из међуспремника није успело. Уверите се да су дозволе за међуспремник одобрене.", - "clipboardHttpWarning": "Лепљење захтева HTTPS. Користите Ctrl+Shift+V или послужите Termix преко HTTPS-а.", - "unknownError": "Дошло је до непознате грешке", - "websocketError": "Грешка при повезивању са WebSocket-ом", - "connecting": "Повезивање...", - "noHostSelected": "Није изабран домаћин", - "reconnecting": "Поновно повезивање... ({{attempt}}/{{max}})", - "reconnected": "Поново повезано успешно", - "tmuxSessionCreated": "tmux сесија креирана: {{name}}", - "tmuxSessionAttached": "приложена tmux сесија: {{name}}", - "tmuxUnavailable": "tmux није инсталиран на удаљеном хосту, враћа се на стандардну љуску", - "tmuxSessionPickerTitle": "tmux сесије", - "tmuxSessionPickerDesc": "Постојеће tmux сесије су пронађене на овом хосту. Изаберите једну да бисте је поново повезали или креирали нову сесију.", - "tmuxWindows": "Прозори", - "tmuxWindowCount": "{{count}} прозор", - "tmuxAttached": "Прикључени клијенти", - "tmuxAttachedCount": "{{count}} приложено", - "tmuxLastActivity": "Последња активност", - "tmuxTimeJustNow": "управо сада", - "tmuxTimeMinutes": "{{count}}пре мин", - "tmuxTimeHours": "{{count}}пре сати", - "tmuxTimeDays": "{{count}}пре д", - "tmuxCreateNew": "Започни нову сесију", - "tmuxCopyHint": "Прилагодите избор и притисните Ентер да бисте копирали у међуспремник", - "tmuxDetach": "Одвоји се од tmux сесије", - "tmuxDetached": "Одвојено од tmux сесије", - "maxReconnectAttemptsReached": "Достигнут је максималан број покушаја поновног повезивања", - "closeTab": "Затвори", - "connectionTimeout": "Временско ограничење везе", - "terminalTitle": "Терминал - {{host}}", - "terminalWithPath": "Терминал - {{host}}:{{path}}", - "runTitle": "Покреће се {{command}} - {{host}}", - "totpRequired": "Потребна је двофакторска аутентификација", - "totpCodeLabel": "Верификациони код", - "totpVerify": "Верификуј", - "warpgateAuthRequired": "Потребна је аутентификација Варпгејта", - "warpgateSecurityKey": "Безбедносни кључ", - "warpgateAuthUrl": "URL адреса за аутентификацију", - "warpgateOpenBrowser": "Отвори у прегледачу", - "warpgateContinue": "Завршио/ла сам аутентификацију", - "opksshAuthRequired": "Потребна је аутентификација OPKSSH", - "opksshAuthDescription": "Завршите аутентификацију у прегледачу да бисте наставили. Ова сесија ће остати важећа 24 сата.", - "opksshOpenBrowser": "Отворите прегледач за аутентификацију", - "opksshWaitingForAuth": "Чекање аутентификације у прегледачу...", - "opksshAuthenticating": "Обрада аутентификације...", - "opksshTimeout": "Временско ограничење за аутентификацију је истекло. Молимо покушајте поново.", - "opksshAuthFailed": "Аутентификација није успела. Проверите своје акредитиве и покушајте поново.", - "opksshSignInWith": "Пријавите се са {{provider}}", - "sudoPasswordPopupTitle": "Унети лозинку?", - "websocketAbnormalClose": "Веза је неочекивано прекинута. Ово може бити због проблема са обрнутим проксијем или SSL конфигурацијом. Молимо вас да проверите логове сервера.", - "connectionLogTitle": "Дневник везе", - "connectionLogCopy": "Копирајте логове у међуспремник", - "connectionLogEmpty": "Још нема евиденција веза", - "connectionLogWaiting": "Чекање евиденције везе...", - "connectionLogCopied": "Дневници веза копирани у међуспремник", - "connectionLogCopyFailed": "Копирање логова у међуспремник није успело", - "connectionRejected": "Сервер је одбио везу. Проверите аутентификацију и конфигурацију мреже.", - "hostKeyRejected": "Верификација SSH кључа хоста је одбијена. Веза је отказана.", - "sessionTakenOver": "Сесија је отворена у другој картици. Поновно повезивање..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Није изабран домаћин", - "initializingEditor": "Иницијализација уређивача...", - "file": "Датотека", - "folder": "Фасцикла", - "uploadFile": "Отпреми датотеку", - "downloadFile": "Преузми", - "extractArchive": "Издвоји архиву", - "extractingArchive": "Издвајање {{name}}...", - "archiveExtractedSuccessfully": "{{name}} је успешно извучено", - "extractFailed": "Издвајање није успело", - "compressFile": "Компресуј датотеку", - "compressFiles": "Компресуј датотеке", - "compressFilesDesc": "Компресуј {{count}} ставки у архиву", - "archiveName": "Назив архиве", - "enterArchiveName": "Унесите име архиве...", - "compressionFormat": "Формат компресије", - "selectedFiles": "Изабране датотеке", - "andMoreFiles": "и још {{count}}...", - "compress": "Компресуј", - "compressingFiles": "Компресија {{count}} ставки у {{name}}...", - "filesCompressedSuccessfully": "{{name}} је успешно креирано", - "compressFailed": "Компресија није успела", - "edit": "Измени", - "preview": "Преглед", - "previous": "Претходно", - "next": "Следеће", - "pageXOfY": "Страница {{current}} од {{total}}", - "zoomOut": "Умањи", - "zoomIn": "Увећај", - "newFile": "Нова датотека", - "newFolder": "Нова фасцикла", - "rename": "Преименуј", - "uploading": "Отпремање...", - "uploadingFile": "Отпремање {{name}}...", - "fileName": "Име датотеке", - "folderName": "Назив фасцикле", - "fileUploadedSuccessfully": "Датотека „{{name}}“ је успешно отпремљена", - "failedToUploadFile": "Отпремање датотеке није успело", - "fileDownloadedSuccessfully": "Датотека „{{name}}“ је успешно преузета", - "failedToDownloadFile": "Преузимање датотеке није успело", - "fileCreatedSuccessfully": "Датотека „{{name}}“ је успешно креирана", - "folderCreatedSuccessfully": "Фасцикла „{{name}}“ је успешно креирана", - "failedToCreateItem": "Није успело креирање ставке", - "operationFailed": "Операција {{operation}} није успела за {{name}}: {{error}}", - "failedToResolveSymlink": "Није успело решавање симболичке везе", - "itemsDeletedSuccessfully": "{{count}} ставке су успешно обрисане", - "failedToDeleteItems": "Брисање ставки није успело", - "sudoPasswordRequired": "Потребна је администраторска лозинка", - "enterSudoPassword": "Унесите лозинку за судо да бисте наставили ову операцију", - "sudoPassword": "Sudo лозинка", - "sudoOperationFailed": "Sudo операција није успела", - "sudoAuthFailed": "Судо аутентификација није успела", - "dragFilesToUpload": "Отпустите датотеке овде да бисте их отпремили", - "emptyFolder": "Ова фасцикла је празна", - "searchFiles": "Претражи датотеке...", - "upload": "Отпреми", - "selectHostToStart": "Изаберите хост да бисте започели управљање датотекама", - "sshRequiredForFileManager": "Менаџер датотека захтева SSH. Овај хост нема омогућен SSH.", - "failedToConnect": "Повезивање са SSH-ом није успело", - "failedToLoadDirectory": "Учитавање директоријума није успело", - "noSSHConnection": "SSH веза није доступна", - "copy": "Копија", - "cut": "Исеци", - "paste": "Залепи", - "copyPath": "Копирај путању", - "copyPaths": "Копирај путање", - "delete": "Обриши", - "properties": "Некретнине", - "refresh": "Освежи", - "downloadFiles": "Преузмите {{count}} датотека у прегледач", - "copyFiles": "Копирај {{count}} ставке", - "cutFiles": "Исеци {{count}} ставки", - "deleteFiles": "Обриши {{count}} ставки", - "filesCopiedToClipboard": "{{count}} ставке копиране у међуспремник", - "filesCutToClipboard": "{{count}} ставке исечене у међуспремник", - "pathCopiedToClipboard": "Путања је копирана у међуспремник", - "pathsCopiedToClipboard": "{{count}} путање копиране у међуспремник", - "failedToCopyPath": "Копирање путање у међуспремник није успело", - "movedItems": "Премештено {{count}} ставки", - "failedToDeleteItem": "Брисање ставке није успело", - "itemRenamedSuccessfully": "{{type}} је успешно преименован", - "failedToRenameItem": "Преименовање ставке није успело", - "download": "Преузми", - "permissions": "Дозволе", - "size": "Величина", - "modified": "Измењено", - "path": "Путања", - "confirmDelete": "Да ли сте сигурни да желите да обришете {{name}}?", - "permissionDenied": "Дозвола одбијена", - "serverError": "Грешка сервера", - "fileSavedSuccessfully": "Датотека је успешно сачувана", - "failedToSaveFile": "Чување датотеке није успело", - "confirmDeleteSingleItem": "Да ли сте сигурни да желите трајно да избришете „{{name}}“?", - "confirmDeleteMultipleItems": "Да ли сте сигурни да желите трајно да избришете {{count}} ставке?", - "confirmDeleteMultipleItemsWithFolders": "Да ли сте сигурни да желите трајно да избришете {{count}} ставке? Ово укључује фасцикле и њихов садржај.", - "confirmDeleteFolder": "Да ли сте сигурни да желите трајно да избришете фасциклу „{{name}}“ и сав њен садржај?", - "permanentDeleteWarning": "Ова радња се не може поништити. Ставка(е) ће бити трајно избрисане са сервера.", - "recent": "Недавно", - "pinned": "Закачено", - "folderShortcuts": "Пречице за фасцикле", - "failedToReconnectSSH": "Поновно повезивање SSH сесије није успело", - "openTerminalHere": "Отвори терминал овде", - "run": "Трчи", - "openTerminalInFolder": "Отвори терминал у овој фасцикли", - "openTerminalInFileLocation": "Отвори терминал на локацији датотеке", - "runningFile": "Трчање - {{file}}", - "onlyRunExecutableFiles": "Може покренути само извршне датотеке", - "directories": "Директоријуми", - "removedFromRecentFiles": "Уклоњено је „{{name}}“ из недавних датотека", - "removeFailed": "Уклањање није успело", - "unpinnedSuccessfully": "Откачено „{{name}}“ успешно", - "unpinFailed": "Откачивање није успело", - "removedShortcut": "Уклоњена је пречица „{{name}}“", - "removeShortcutFailed": "Уклањање пречице није успело", - "clearedAllRecentFiles": "Обрисани су сви недавно објављени фајлови", - "clearFailed": "Брисање није успело", - "removeFromRecentFiles": "Уклони из недавних датотека", - "clearAllRecentFiles": "Обриши све недавно објављене датотеке", - "unpinFile": "Откачи датотеку", - "removeShortcut": "Уклони пречицу", - "pinFile": "Закачи датотеку", - "addToShortcuts": "Додај у пречице", - "pasteFailed": "Лепљење није успело", - "noUndoableActions": "Нема радњи које се могу поништити", - "undoCopySuccess": "Поништена операција копирања: Обрисано {{count}} копираних датотека", - "undoCopyFailedDelete": "Поништавање није успело: Није могуће обрисати ниједну копирану датотеку", - "undoCopyFailedNoInfo": "Поништавање није успело: Нису пронађене информације о копираној датотеци", - "undoMoveSuccess": "Поништена операција премештања: Премештено {{count}} датотека назад на оригиналну локацију", - "undoMoveFailedMove": "Поништавање није успело: Није могуће вратити датотеке", - "undoMoveFailedNoInfo": "Поништавање није успело: Нису пронађене информације о премештеној датотеци", - "undoDeleteNotSupported": "Брисање се не може поништити: Датотеке су трајно обрисане са сервера", - "undoTypeNotSupported": "Неподржани тип операције поништавања", - "undoOperationFailed": "Поништавање није успело", - "unknownError": "Непозната грешка", - "confirm": "Потврди", - "find": "Пронађи...", - "replace": "Замени", - "downloadInstead": "Преузми уместо тога", - "keyboardShortcuts": "Пречице на тастатури", - "searchAndReplace": "Претражи и замени", - "editing": "Уређивање", - "search": "Претрага", - "findNext": "Пронађи следеће", - "findPrevious": "Пронађи претходно", - "save": "Сачувај", - "selectAll": "Изабери све", - "undo": "Поништи", - "redo": "Понови", - "moveLineUp": "Помери ред нагоре", - "moveLineDown": "Помери ред надоле", - "toggleComment": "Укључи/искључи коментар", - "autoComplete": "Аутоматско довршавање", - "imageLoadError": "Учитавање слике није успело", - "startTyping": "Почни да куцаш...", - "unknownSize": "Непозната величина", - "fileIsEmpty": "Датотека је празна", - "largeFileWarning": "Упозорење о великој датотеци", - "largeFileWarningDesc": "Величина ове датотеке је {{size}} , што може изазвати проблеме са перформансама када се отвори као текст.", - "fileNotFoundAndRemoved": "Датотека „{{name}}“ није пронађена и уклоњена је из недавно коришћених/закачених датотека", - "failedToLoadFile": "Учитавање датотеке није успело: {{error}}", - "serverErrorOccurred": "Дошло је до грешке сервера. Молимо покушајте поново касније.", - "autoSaveFailed": "Аутоматско чување није успело", - "fileAutoSaved": "Датотека је аутоматски сачувана", - "moveFileFailed": "Премештање није успело {{name}}", - "moveOperationFailed": "Операција премештања није успела", - "canOnlyCompareFiles": "Могуће је упоредити само две датотеке", - "comparingFiles": "Поређење датотека: {{file1}} и {{file2}}", - "dragFailed": "Превлачење није успело", - "filePinnedSuccessfully": "Датотека „{{name}}“ је успешно закачена", - "pinFileFailed": "Качење датотеке није успело", - "fileUnpinnedSuccessfully": "Датотека „{{name}}“ је успешно откачена", - "unpinFileFailed": "Откачивање датотеке није успело", - "shortcutAddedSuccessfully": "Пречица за фасциклу „{{name}}“ је успешно додата", - "addShortcutFailed": "Додавање пречице није успело", - "operationCompletedSuccessfully": "{{operation}} {{count}} ставке успешно", - "operationCompleted": "{{operation}} {{count}} ставке", - "downloadFileSuccess": "Датотека {{name}} је успешно преузета", - "downloadFileFailed": "Преузимање није успело", - "moveTo": "Премести на {{name}}", - "diffCompareWith": "Разлика у поређењу са {{name}}", - "dragOutsideToDownload": "Превуците спољашњост прозора да бисте преузели ({{count}} датотека)", - "newFolderDefault": "Нови фолдер", - "newFileDefault": "НоваДатотека.txt", - "successfullyMovedItems": "Успешно премештено {{count}} ставки у {{target}}", - "move": "Помери", - "searchInFile": "Претражи у датотеци (Ctrl+F)", - "showKeyboardShortcuts": "Прикажи пречице на тастатури", - "startWritingMarkdown": "Почните да пишете свој садржај у markdown формату...", - "loadingFileComparison": "Учитавање поређења датотека...", - "reload": "Поново учитај", - "compare": "Упореди", - "sideBySide": "Један поред другог", - "inline": "Уграђено", - "fileComparison": "Поређење датотека: {{file1}} наспрам {{file2}}", - "fileTooLarge": "Датотека је превелика: {{error}}", - "sshConnectionFailed": "SSH веза није успела. Молимо вас да проверите везу са {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Учитавање датотеке није успело: {{error}}", - "connecting": "Повезивање...", - "connectedSuccessfully": "Успешно повезано", - "totpVerificationFailed": "Верификација TOTP-а није успела", - "warpgateVerificationFailed": "Аутентификација Варпгејта није успела", - "authenticationFailed": "Аутентификација није успела", - "verificationCodePrompt": "Верификациони код:", - "changePermissions": "Промени дозволе", - "currentPermissions": "Тренутне дозволе", - "owner": "Власник", - "group": "Група", - "others": "Други", - "read": "Читај", - "write": "Пиши", - "execute": "Изврши", - "permissionsChangedSuccessfully": "Дозволе су успешно промењене", - "failedToChangePermissions": "Промена дозвола није успела", - "name": "Име", - "sortByName": "Име", - "sortByDate": "Датум измене", - "sortBySize": "Величина", - "ascending": "Растуће", - "descending": "Опадајући", - "root": "Корен", - "new": "Ново", - "sortBy": "Сортирај по", - "items": "Ставке", - "selected": "Изабрано", - "editor": "Уредник", - "octal": "Октално", - "storage": "Складиштење", - "disk": "Диск", - "used": "Коришћено", - "of": "од", - "toggleSidebar": "Укључи/искључи бочну траку", - "cannotLoadPdf": "Није могуће учитати PDF", - "pdfLoadError": "Дошло је до грешке при учитавању ове PDF датотеке.", - "loadingPdf": "Учитавање PDF-а...", - "loadingPage": "Учитавање странице..." + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Копирај на хост…", - "moveToHost": "Премести на хост…", - "copyItemsToHost": "Копирај {{count}} ставке за хостовање…", - "moveItemsToHost": "Премести {{count}} ставке на хост…", - "noHostsConnected": "Нема других доступних хостова за управљање датотекама.", - "noHostsConnectedHint": "Додајте још један SSH хост са омогућеним File Manager-ом у Host Manager-у.", - "selectDestinationHost": "Изаберите одредишни хост", - "destinationPath": "Путања одредишта", - "recentDestinations": "Недавне дестинације", - "collapseRecentDestinations": "Скупи недавна одредишта", - "expandRecentDestinations": "Прошири недавна одредишта", - "browseFolders": "Прегледајте одредишне фасцикле", - "browseDestination": "Прегледајте или унесите путању", - "confirmCopy": "Копија", - "confirmMove": "Помери", - "transferring": "Пренос…", - "compressing": "Компресија…", - "extracting": "Издвајање…", - "transferringItems": "Пренос {{current}} од {{total}} ставки…", - "transferSuccess": "Пренос завршен", - "transferError": "Пренос није успео", - "transferPartial": "Пренос завршен са {{count}} грешака", - "transferPartialHint": "Није могуће пренети: {{paths}}", - "itemsSummary": "{{count}} ставке", - "destMustBeDirectory": "Одредиште мора бити директоријум за преносе више ставки", - "selectThisFolder": "Изаберите ову фасциклу", - "browsePathWillBeCreated": "Ова фасцикла још увек не постоји. Биће креирана када пренос почне.", - "browsePathError": "Није могуће отворити ову путању на одредишном хосту.", - "goUp": "Иди горе", - "copyFolderToHost": "Копирај фасциклу на хост…", - "moveFolderToHost": "Премести фасциклу на хост…", - "hostReady": "Спреман", - "hostConnecting": "Повезивање…", - "hostDisconnected": "Није повезано", - "hostAuthRequired": "Потребна је аутентификација — прво отворите File Manager на овом хосту", - "hostConnectionFailed": "Повезивање није успело", - "metricsTitle": "Времена трансфера", - "metricsPrepare": "Припреми одредиште: {{duration}}", - "metricsCompress": "Компресуј на изворном коду: {{duration}}", - "metricsHopSourceRead": "Извор → сервер: {{throughput}}", - "metricsHopDestSftpWrite": "Сервер → одредиште (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Сервер → одредиште (локално): {{throughput}}", - "metricsTransfer": "Од краја до краја: {{throughput}} ({{duration}})", - "metricsExtract": "Издвајање на одредишту: {{duration}}", - "metricsSourceDelete": "Уклони из извора: {{duration}}", - "metricsTotal": "Укупно: {{duration}}", - "progressCompressing": "Компресија на изворном хосту…", - "progressExtracting": "Распакује се на одредишту…", - "progressTransferring": "Пренос података…", - "progressReconnecting": "Поновно повезивање…", - "parallelSegmentsLabel": "Паралелне траке за прелазак", - "parallelSegmentsOption": "{{count}} траке", - "parallelSegmentsHint": "Велике датотеке су подељене на делове од 256 MB. Вишеструке траке користе одвојене везе (као што је покретање неколико преноса) за већи укупни проток.", - "progressTotalSpeed": "{{speed}} укупно ({{lanes}} трака)", - "progressTransferringItems": "Пренос датотека ({{current}} од {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} датотеке", - "sourceNotDeletedPartial": "Изворне датотеке сачуване (делимични пренос)", - "jumpHostLimitation": "Оба хоста морају бити доступна са Termix сервера. Директно рутирање од хоста до хоста није подржано.", - "cancel": "Откажи", - "methodLabel": "Метод преноса", - "methodAuto": "Аутоматски", - "methodTar": "Тар архива", - "methodItemSftp": "SFTP по датотеци", - "methodAutoHint": "Бира tar или SFTP за сваку датотеку на основу броја датотека, величине и компресибилности. Појединачне датотеке увек користе SFTP за стримовање.", - "methodTarHint": "Компресуј на изворном коду, пренеси једну архиву, распакуј на одредишту. Потребан је tar на оба Unix хоста.", - "methodItemSftpHint": "Пренесите сваку датотеку појединачно преко SFTP-а. Ради на свим хостовима, укључујући Windows.", - "methodPreviewLoading": "Израчунавање методе преноса…", - "methodPreviewError": "Није могуће прегледати метод преноса. Сервер ће ипак изабрати метод када покренете.", - "methodPreviewWillUseTar": "Користиће се: Тар архива", - "methodPreviewWillUseItemSftp": "Користиће се: SFTP за сваку датотеку", - "methodPreviewScanSummary": "{{fileCount}} датотека, {{totalSize}} укупно (скенирано на изворном хосту).", - "methodItemSftpLimitation": "Свака датотека користи исти SFTP ток као и копија појединачних датотека, једна за другом. Напредак се комбинује за све датотеке, тако да се трака помера споро током великих датотека.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Изабрали сте SFTP за сваку датотеку.", - "user_tar": "Изабрали сте tar архиву.", - "tar_unavailable": "Тар није доступан на једном или оба хоста — уместо тога ће се користити SFTP протокол за сваку датотеку.", - "windows_host": "Укључен је Windows хост — tar се не користи.", - "auto_multi_large": "Аутоматски: више датотека укључујући велику датотеку ({{largestSize}}) са компресибилним подацима — tar пакети у један пренос.", - "auto_single_large_in_archive": "Аутоматски: једна велика датотека ({{largestSize}}) у овом скупу — SFTP за сваку датотеку.", - "auto_many_incompressible": "Аутоматски: углавном несемпљиви подаци — SFTP по датотеци.", - "auto_many_files": "Аутоматски: много датотека ({{fileCount}}) — tar смањује оптерећење по датотеци.", - "auto_default": "Аутоматски: SFTP за сваку датотеку за овај скуп." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Откажи", - "progressCancelling": "Отказивање…", - "progressStalled": "Заустављено", - "resumedHint": "Поново повезано са активним преносом започетим у другом прозору.", - "transferCancelled": "Трансфер отказан", - "transferCancelledCopyHint": "Делимични фајлови могу остати на одредишту.", - "transferCancelledMoveHint": "Делимични фајлови могу остати на одредишту. Изворни фајлови нису уклоњени.", - "cleanupDestFiles": "Очистите одредиште", - "cleanupDestFilesSuccess": "Уклоњене су делимичне датотеке са одредишта", - "cleanupDestFilesPartial": "Неке делимичне датотеке нису могле бити уклоњене", - "cleanupDestFilesNothing": "Нема ништа за чишћење на одредишту", - "cleanupDestFilesError": "Чишћење није успело", - "retryTransfer": "Покушај поново", - "retryTransferError": "Поновни покушај није успео", - "transferFailedRetryHint": "Делимични подаци су сачувани на одредишту. Покушај ће бити настављен када се веза поново успостави." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Нема SSH тунела", - "createFirstTunnelMessage": "Још увек нисте креирали ниједан SSH тунел. Конфигуришите тунелске везе у Host Manager-у да бисте започели.", - "connected": "Повезано", - "disconnected": "Искључено", - "connecting": "Повезивање...", - "error": "Грешка", - "canceling": "Отказивање...", - "connect": "Повежи се", - "disconnect": "Прекини везу", - "cancel": "Откажи", - "port": "Лука", - "localPort": "Локални порт", - "remotePort": "Удаљени порт", - "currentHostPort": "Тренутни порт хоста", - "endpointPort": "Порт крајње тачке", - "bindIp": "Локална ИП адреса", - "endpointSshConfig": "SSH конфигурација крајње тачке", - "endpointSshHost": "SSH хост крајње тачке", - "endpointSshHostPlaceholder": "Изаберите конфигурисани хост", - "endpointSshHostRequired": "Изаберите SSH хост крајње тачке за сваки клијентски тунел.", - "attempt": "Покушај {{current}} од {{max}}", - "nextRetryIn": "Следећи поновни покушај за {{seconds}} секунди", - "clientTunnels": "Клијентски тунели", - "clientTunnel": "Тунел клијента", - "addClientTunnel": "Додај клијентски тунел", - "noClientTunnels": "Ниједан клијентски тунел није конфигурисан на овој радној површини.", - "tunnelName": "Назив тунела", - "remoteHost": "Удаљени хост", - "autoStart": "Аутоматски старт", - "clientAutoStartDesc": "Покреће се када се овај десктоп клијент отвори и остане повезан.", - "clientManualStartDesc": "Користите „Старт“ и „Стоп“ из овог реда. Termix га неће аутоматски отворити.", - "clientRemoteServerNote": "Даљинско преусмеравање може захтевати AllowTcpForwarding и GatewayPorts на SSH серверу крајње тачке. Удаљени порт се затвара када се ова радна површина искључи.", - "clientTunnelStarted": "Тунел клијента је покренут", - "clientTunnelStopped": "Тунел клијента је заустављен", - "tunnelTestSucceeded": "Тест тунела је успешан", - "tunnelTestFailed": "Тест тунела није успео", - "localSaved": "Тунели клијента сачувани", - "localSaveError": "Чување локалних клијентских тунела није успело", - "invalidBindIp": "Локална IP адреса мора бити важећа IPv4 адреса.", - "invalidLocalTargetIp": "Локална циљна IP адреса мора бити важећа IPv4 адреса.", - "invalidLocalPort": "Локални порт мора бити између 1 и 65535.", - "invalidRemotePort": "Удаљени порт мора бити између 1 и 65535.", - "invalidLocalTargetPort": "Локални циљни порт мора бити између 1 и 65535.", - "invalidEndpointPort": "Порт крајње тачке мора бити између 1 и 65535.", - "duplicateAutoStartBind": "Само један аутоматски покренути клијентски тунел може да користи {{bind}}.", - "manualControlError": "Ажурирање стања тунела није успело.", - "active": "Активно", - "start": "Почетак", - "stop": "Заустави", - "test": "Тест", - "type": "Тип тунела", - "typeLocal": "Локално (-Л)", - "typeRemote": "Даљински (-R)", - "typeDynamic": "Динамички (-Д)", - "typeServerLocalDesc": "Тренутни хост до крајње тачке.", - "typeServerRemoteDesc": "Крајња тачка назад на тренутни хост.", - "typeClientLocalDesc": "Локални рачунар до крајње тачке.", - "typeClientRemoteDesc": "Крајња тачка назад на локални рачунар.", - "typeClientDynamicDesc": "SOCKS на локалном рачунару.", - "typeDynamicDesc": "Прослеђивање SOCKS5 CONNECT саобраћаја преко SSH-а", - "forwardDescriptionServerLocal": "Тренутни хост {{sourcePort}} → крајња тачка {{endpointPort}}.", - "forwardDescriptionServerRemote": "Крајња тачка {{endpointPort}} → тренутни хост {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS на тренутном хосту {{sourcePort}}.", - "forwardDescriptionClientLocal": "Локално {{sourcePort}} → удаљено {{endpointPort}}.", - "forwardDescriptionClientRemote": "Удаљено {{sourcePort}} → локално {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS на локалном порту {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → ЧАРАПЕ преко {{endpoint}}", - "autoNameClientLocal": "Локално {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → локално {{localPort}}", - "autoNameClientDynamic": "ЧАРАПЕ {{localPort}} преко {{endpoint}}", - "route": "Рута:", - "lastStarted": "Последњи пут започет", - "lastTested": "Последње тестирање", - "lastError": "Последња грешка", - "maxRetries": "Максималан број поновних покушаја", - "maxRetriesDescription": "Максималан број поновних покушаја.", - "retryInterval": "Интервал поновног покушаја (секунде)", - "retryIntervalDescription": "Време чекања између поновних покушаја.", - "local": "Локално", - "remote": "Даљински", - "destination": "Одредиште", - "host": "Домаћин", - "mode": "Режим", - "noHostSelected": "Није изабран домаћин", - "working": "Ради се..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "Процесор", - "memory": "Памћење", - "disk": "Диск", - "network": "Мрежа", - "uptime": "Време непрекидног рада", - "processes": "Процеси", - "available": "Доступно", - "free": "Бесплатно", - "connecting": "Повезивање...", - "connectionFailed": "Повезивање са сервером није успело", - "naCpus": "Н/Д процесор(и)", - "cpuCores_one": "{{count}} Језгро", - "cpuCores_other": "{{count}} Језгра", - "cpuUsage": "Искоришћеност процесора", - "memoryUsage": "Коришћење меморије", - "diskUsage": "Искоришћеност диска", - "failedToFetchHostConfig": "Није успело преузимање конфигурације хоста", - "serverOffline": "Сервер ван мреже", - "cannotFetchMetrics": "Није могуће преузети метрике са офлајн сервера", - "totpFailed": "Верификација TOTP-а није успела", - "noneAuthNotSupported": "Статистика сервера не подржава тип аутентификације „ниједан“.", - "load": "Учитај", - "systemInfo": "Системске информације", - "hostname": "Име хоста", - "operatingSystem": "Оперативни систем", - "kernel": "Језгро", - "seconds": "секунде", - "networkInterfaces": "Мрежни интерфејси", - "noInterfacesFound": "Није пронађен ниједан мрежни интерфејс", - "noProcessesFound": "Није пронађен ниједан процес", - "loginStats": "Статистика SSH пријава", - "noRecentLoginData": "Нема скорашњих података за пријављивање", - "executingQuickAction": "Извршава се {{name}}...", - "quickActionSuccess": "{{name}} успешно завршено", - "quickActionFailed": "{{name}} није успело", - "quickActionError": "Није успело извршавање {{name}}", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Портови за слушање", - "protocol": "Протокол", - "port": "Лука", - "address": "Адреса", - "process": "Процес", - "noData": "Нема података о портовима за слушање" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Заштитни зид (фајервол)", - "inactive": "Неактивно", - "policy": "Политика", - "rules": "правила", - "noData": "Нема доступних података о заштитном зиду", - "action": "Акција", - "protocol": "Прото", - "port": "Лука", - "source": "Извор", - "anywhere": "Било где" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Учитај просек", - "swap": "Замена", - "architecture": "Архитектура", - "refresh": "Освежи", - "retry": "Покушај поново" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Самостално хостовано SSH и управљање удаљеном радном површином", - "loginTitle": "Пријавите се на Termix", - "registerTitle": "Направи налог", - "forgotPassword": "Заборавили сте лозинку?", - "rememberMe": "Запамти уређај 30 дана (укључује TOTP)", - "noAccount": "Немате налог?", - "hasAccount": "Већ имате налог?", - "twoFactorAuth": "Двофакторска аутентификација", - "enterCode": "Унесите верификациони код", - "backupCode": "Или користите резервни код", - "verifyCode": "Верификујте код", - "redirectingToApp": "Преусмеравање на апликацију...", - "sshAuthenticationRequired": "Потребна је SSH аутентификација", - "sshNoKeyboardInteractive": "Интерактивна аутентификација преко тастатуре није доступна", - "sshAuthenticationFailed": "Аутентификација није успела", - "sshAuthenticationTimeout": "Временско ограничење аутентификације", - "sshNoKeyboardInteractiveDescription": "Сервер не подржава интерактивну аутентификацију помоћу тастатуре. Молимо вас да унесете лозинку или SSH кључ.", - "sshAuthFailedDescription": "Унети акредитиви су били нетачни. Молимо вас да покушате поново са важећим акредитивима.", - "sshTimeoutDescription": "Временско ограничење покушаја аутентификације је истекло. Молимо покушајте поново.", - "sshProvideCredentialsDescription": "Молимо вас да наведете своје SSH акредитиве да бисте се повезали са овим сервером.", - "sshPasswordDescription": "Унесите лозинку за ову SSH везу.", - "sshKeyPasswordDescription": "Ако је ваш SSH кључ шифрован, унесите лозинку овде.", - "passphraseRequired": "Потребна је лозинка", - "passphraseRequiredDescription": "SSH кључ је шифрован. Унесите лозинку да бисте га откључали.", - "back": "Назад", - "firstUser": "Први корисник", - "firstUserMessage": "Ви сте први корисник и бићете постављени за администратора. Подешавања администратора можете видети у падајућем менију корисника на бочној траци. Ако мислите да је ово грешка, проверите логове Docker-а или креирајте проблем на GitHub-у.", - "external": "Спољни", - "loginWithExternal": "Пријавите се са спољним добављачем", - "loginWithExternalDesc": "Пријавите се користећи свог конфигурисаног спољног добављача идентитета", - "externalNotSupportedInElectron": "Спољна аутентификација још увек није подржана у апликацији Electron. Молимо вас да користите веб верзију за пријаву путем OIDC-а.", - "resetPasswordButton": "Ресетуј лозинку", - "sendResetCode": "Пошаљи код за ресетовање", - "resetCodeDesc": "Унесите своје корисничко име да бисте добили код за ресетовање лозинке. Код ће бити забележен у логовима Docker контејнера.", - "resetCode": "Ресетуј код", - "verifyCodeButton": "Верификујте код", - "enterResetCode": "Унесите 6-цифрени код из логова Docker контејнера за корисника:", - "newPassword": "Нова лозинка", - "confirmNewPassword": "Потврдите лозинку", - "enterNewPassword": "Унесите нову лозинку за корисника:", - "signUp": "Региструј се", - "desktopApp": "Десктоп апликација", - "loggingInToDesktopApp": "Пријављивање у апликацију за десктоп", - "loadingServer": "Учитавање сервера...", - "dataLossWarning": "Ресетовањем лозинке на овај начин избрисаћете све сачуване SSH хостове, акредитиве и друге шифроване податке. Ова радња се не може поништити. Користите ово само ако сте заборавили лозинку и нисте пријављени.", - "authenticationDisabled": "Аутентификација је онемогућена", - "authenticationDisabledDesc": "Све методе аутентификације су тренутно онемогућене. Контактирајте администратора.", - "attemptsRemaining": "{{count}} преосталих покушаја" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Проверите SSH кључ хоста", - "keyChangedWarning": "SSH кључ хоста је промењен", - "firstConnectionTitle": "Прво повезивање са овим хостом", - "firstConnectionDescription": "Аутентичност овог хоста не може бити утврђена. Проверите да ли отисак прста одговара очекивањима.", - "keyChangedDescription": "Кључ хоста за овај сервер се променио од ваше последње везе. Ово би могло да указује на безбедносни проблем.", - "previousKey": "Претходни кључ", - "newFingerprint": "Нови отисак прста", - "fingerprint": "Отисак прста", - "verifyInstructions": "Ако верујете овом хосту, кликните на „Прихвати“ да бисте наставили и сачували овај отисак прста за будуће везе.", - "securityWarning": "Безбедносно упозорење", - "acceptAndContinue": "Прихвати и настави", - "acceptNewKey": "Прихвати нови кључ и настави" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Није могуће повезати се са базом података", - "unknownError": "Непозната грешка", - "loginFailed": "Пријављивање није успело", - "failedPasswordReset": "Покретање ресетовања лозинке није успело", - "failedVerifyCode": "Није успело потврђивање кода за ресетовање", - "failedCompleteReset": "Ресетовање лозинке није успело", - "invalidTotpCode": "Неважећи TOTP код", - "failedOidcLogin": "Није успело покретање OIDC пријаве", - "silentSigninOidcUnavailable": "Затражено је тихо пријављивање, али пријављивање преко OIDC-а није доступно.", - "failedUserInfo": "Није успело добијање корисничких података након пријаве", - "oidcAuthFailed": "OIDC аутентификација није успела", - "invalidAuthUrl": "Неважећи URL за ауторизацију примљен од бекенда", - "requiredField": "Ово поље је обавезно", - "minLength": "Минимална дужина је {{min}}", - "passwordMismatch": "Лозинке се не подударају", - "passwordLoginDisabled": "Пријава корисничким именом/лозинком је тренутно онемогућена", - "sessionExpired": "Сесија је истекла - молимо вас да се поново пријавите", - "totpRateLimited": "Ограничена брзина: Превише покушаја верификације TOTP-а. Молимо покушајте поново касније.", - "totpRateLimitedWithTime": "Ограничена брзина: Превише покушаја верификације TOTP-а. Молимо сачекајте {{time}} секунди пре него што покушате поново.", - "resetCodeRateLimited": "Ограничена брзина: Превише покушаја верификације. Молимо покушајте поново касније.", - "resetCodeRateLimitedWithTime": "Ограничена брзина: Превише покушаја верификације. Молимо сачекајте {{time}} секунди пре него што покушате поново.", - "authTokenSaveFailed": "Чување токена за аутентификацију није успело", - "failedToLoadServer": "Учитавање сервера није успело" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Регистрација новог налога је тренутно онемогућена од стране администратора. Молимо вас да се пријавите или контактирате администратора.", - "userNotAllowed": "Ваш налог није овлашћен за регистрацију. Молимо контактирајте администратора.", - "databaseConnectionFailed": "Није успело повезивање са сервером базе података", - "resetCodeSent": "Ресетујте код послат у Docker логове", - "codeVerified": "Код је успешно верификован", - "passwordResetSuccess": "Ресетовање лозинке је успешно", - "loginSuccess": "Пријава је успешна", - "registrationSuccess": "Регистрација је успешна" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Локални тунели за рачунаре усмерени на конфигурисане SSH хостове.", - "c2sTunnelPresets": "Унапред подешена подешавања клијентског тунела", - "c2sTunnelPresetsDesc": "Сачувајте локалну листу тунела овог десктоп клијента као именовани пресет сервера или поново учитајте пресет у овог клијента.", - "c2sTunnelPresetsUnavailable": "Унапред подешена подешавања тунела клијента доступна су само у десктоп клијенту.", - "c2sPresetName": "Име пресета", - "c2sPresetNamePlaceholder": "Назив унапред подешеног подешавања клијента", - "c2sPresetToLoad": "Унапред подешено за учитавање", - "c2sNoPresetSelected": "Није изабран ниједан унапред подешени подешавања", - "c2sNoPresets": "Нема сачуваних предефинисаних подешавања", - "c2sLoadPreset": "Учитај", - "c2sCurrentLocalConfig": "{{count}} локалних клијентских тунела конфигурисаних на овој радној површини.", - "c2sPresetSyncNote": "Унапред подешена подешавања су експлицитни снимци; учитавање једног замењује листу локалних тунела клијената овог десктоп клијента.", - "c2sPresetSaved": "Унапред подешено подешавање тунела клијента је сачувано", - "c2sPresetLoaded": "Унапред унапред учитан тунел клијента", - "c2sPresetRenamed": "Преименован је унапред одређен тунел клијента", - "c2sPresetDeleted": "Унапред подешено подешавање тунела клијента је избрисано", - "c2sPresetLoadError": "Није успело учитавање унапред подешених подешавања клијентског тунела" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Језик", - "keyPassword": "кључна лозинка", - "pastePrivateKey": "Налепите свој приватни кључ овде...", - "localListenerHost": "127.0.0.1 (слушај локално)", - "localTargetHost": "127.0.0.1 (циљ на овом рачунару)", - "socksListenerHost": "127.0.0.1 (SOCKS слушалац)", - "enterPassword": "Унесите лозинку", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Контролна табла", - "loading": "Учитавање контролне табле...", - "github": "ГитХаб", - "support": "Подршка", - "discord": "Неслагање", - "serverOverview": "Преглед сервера", - "version": "Верзија", - "upToDate": "Ажурирано", - "updateAvailable": "Ажурирање је доступно", - "beta": "Бета", - "uptime": "Време непрекидног рада", - "database": "База података", - "healthy": "Здраво", - "error": "Грешка", - "totalHosts": "Укупан број домаћина", - "totalTunnels": "Укупан број тунела", - "totalCredentials": "Укупан број акредитива", - "recentActivity": "Недавне активности", - "reset": "Ресетуј", - "loadingRecentActivity": "Учитавање недавних активности...", - "noRecentActivity": "Нема скорашњих активности", - "quickActions": "Брзе акције", - "addHost": "Додај хоста", - "addCredential": "Додај акредитив", - "adminSettings": "Администраторска подешавања", - "userProfile": "Кориснички профил", - "serverStats": "Статистика сервера", - "loadingServerStats": "Учитавање статистике сервера...", - "noServerData": "Нема доступних података са сервера", - "cpu": "Процесор", - "ram": "РАМ меморија", - "customizeLayout": "Прилагоди контролну таблу", - "dashboardSettings": "Подешавања контролне табле", - "enableDisableCards": "Омогући/онемогући картице", - "resetLayout": "Ресетуј на подразумевано", - "serverOverviewCard": "Преглед сервера", - "recentActivityCard": "Недавне активности", - "networkGraphCard": "Мрежни графикон", - "networkGraph": "Мрежни графикон", - "quickActionsCard": "Брзе акције", - "serverStatsCard": "Статистика сервера", - "panelMain": "Главни", - "panelSide": "Страна", - "justNow": "управо сада" + "title": "Dashboard", + "loading": "Loading dashboard...", + "github": "GitHub", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "СТАБИЛНО", - "hostsOnline": "Домаћини на мрежи", - "activeTunnels": "Активни тунели", - "registerNewServer": "Региструјте нови сервер", - "storeSshKeysOrPasswords": "Чувајте SSH кључеве или лозинке", - "manageUsersAndRoles": "Управљајте корисницима и улогама", - "manageYourAccount": "Управљајте својим налогом", - "hostStatus": "Статус домаћина", - "noHostsConfigured": "Ниједан хост није конфигурисан", - "online": "ОНЛАЈН", - "offline": "ОФЛАЈН", - "onlineLower": "Онлајн", - "nodes": "{{count}} чворова", - "add": "Додај:", - "commandPalette": "Палета команди", - "done": "Готово", - "editModeInstructions": "Превуците картице да бисте променили редослед · Превуците разделник колона да бисте променили величину колона · Превуците доњу ивицу картице да бисте променили величину њене висине · Отпад да бисте уклонили", - "empty": "Празно", - "clear": "Јасно" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Додај хоста", - "addGroup": "Додај групу", - "addLink": "Додај линк", - "zoomIn": "Увећај", - "zoomOut": "Умањи", - "resetView": "Ресетуј приказ", - "selectHost": "Изаберите хост", - "chooseHost": "Изаберите домаћина...", - "parentGroup": "Матична група", - "noGroup": "Нема групе", - "groupName": "Назив групе", - "color": "Боја", - "source": "Извор", - "target": "Циљ", - "moveToGroup": "Премести у групу", - "selectGroup": "Изаберите групу...", - "addConnection": "Додај везу", - "hostDetails": "Детаљи домаћина", - "removeFromGroup": "Уклони из групе", - "addHostHere": "Додај хост овде", - "editGroup": "Уреди групу", - "delete": "Обриши", - "add": "Додај", - "create": "Креирај", - "move": "Помери", - "connect": "Повежи се", - "createGroup": "Направи групу", - "selectSourcePlaceholder": "Изаберите извор...", - "selectTargetPlaceholder": "Изаберите циљ...", - "invalidFile": "Неважећа датотека", - "hostAlreadyExists": "Хост је већ у топологији", - "connectionExists": "Веза већ постоји", - "unknown": "Непознато", - "name": "Име", - "ip": "ИП", - "status": "Статус", - "failedToAddNode": "Додавање чвора није успело", - "sourceDifferentFromTarget": "Извор и циљ морају бити различити", - "exportJSON": "Извези JSON", - "importJSON": "Увези JSON", - "terminal": "Терминал", - "fileManager": "Менаџер датотека", - "tunnel": "Тунел", - "docker": "Докер", - "serverStats": "Статистика сервера", - "noNodes": "Још нема чворова" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Докер није омогућен за овај хост", - "validating": "Валидација Докера...", - "connecting": "Повезивање...", - "error": "Грешка", - "version": "Докер {{version}}", - "connectionFailed": "Повезивање са Докером није успело", - "containerStarted": "Контејнер {{name}} је покренут", - "failedToStartContainer": "Покретање контејнера {{name}} није успело", - "containerStopped": "Контејнер {{name}} је заустављен", - "failedToStopContainer": "Заустављање контејнера {{name}} није успело", - "containerRestarted": "Контејнер {{name}} поново покренут", - "failedToRestartContainer": "Поновно покретање контејнера није успело {{name}}", - "containerPaused": "Контејнер {{name}} је паузиран", - "containerUnpaused": "Паузирање контејнера {{name}} је прекинуто", - "failedToTogglePauseContainer": "Није успело пребацивање стања паузе за контејнер {{name}}", - "containerRemoved": "Контејнер {{name}} је уклоњен", - "failedToRemoveContainer": "Није успело уклањање контејнера {{name}}", - "image": "Слика", - "ports": "Портови", - "noPorts": "Нема портова", - "start": "Почетак", - "confirmRemoveContainer": "Да ли сте сигурни да желите да уклоните контејнер „{{name}}“? Ова радња се не може поништити.", - "runningContainerWarning": "Упозорење: Овај контејнер је тренутно покренут. Његово уклањање ће прво зауставити рад контејнера.", - "loadingContainers": "Учитавање контејнера...", - "manager": "Докер менаџер", - "autoRefresh": "Аутоматско освежавање", - "timestamps": "Временске ознаке", - "lines": "Линије", - "filterLogs": "Филтрирај логове...", - "refresh": "Освежи", - "download": "Преузми", - "clear": "Јасно", - "logsDownloaded": "Дневници су успешно преузети", - "last50": "Последњих 50", - "last100": "Последњих 100", - "last500": "Последњих 500", - "last1000": "Последњих 1000", - "allLogs": "Сви дневници", - "noLogsMatching": "Нема логова који одговарају „{{query}}“", - "noLogsAvailable": "Нема доступних логова", - "noContainersFound": "Нису пронађени контејнери", - "noContainersFoundHint": "На овом хосту нису доступни Docker контејнери", - "searchPlaceholder": "Претражи контејнере...", - "allStatuses": "Сви статуси", - "stateRunning": "Трчање", - "statePaused": "Паузирано", - "stateExited": "Изашао/ла", - "stateRestarting": "Поновно покретање", - "noContainersMatchFilters": "Ниједан контејнер не одговара вашим филтерима", - "noContainersMatchFiltersHint": "Покушајте да прилагодите критеријуме претраге или филтрирања", - "failedToFetchStats": "Није успело преузимање статистике контејнера", - "containerNotRunning": "Контејнер се не покреће", - "startContainerToViewStats": "Покрените контејнер да бисте видели статистику", - "loadingStats": "Учитавање статистике...", - "errorLoadingStats": "Грешка при учитавању статистике", - "noStatsAvailable": "Нема доступних статистика", - "cpuUsage": "Искоришћеност процесора", - "current": "Тренутни", - "memoryUsage": "Коришћење меморије", - "networkIo": "Мрежни улаз/излаз", - "input": "Унос", - "output": "Излаз", - "blockIo": "Блок У/И", - "read": "Читај", - "write": "Пиши", - "pids": "ПИД-ови", - "containerInformation": "Информације о контејнеру", - "name": "Име", - "id": "ИД", - "state": "Држава", - "containerMustBeRunning": "Контејнер мора бити покренут да би се приступило конзоли", - "verificationCodePrompt": "Унесите верификациони код", - "totpVerificationFailed": "Верификација TOTP-а није успела. Молимо покушајте поново.", - "warpgateVerificationFailed": "Аутентификација преко Ворпгејта није успела. Молимо покушајте поново.", - "connectedTo": "Повезано са {{containerName}}", - "disconnected": "Искључено", - "consoleError": "Грешка конзоле", - "errorMessage": "Грешка: {{message}}", - "failedToConnect": "Повезивање са контејнером није успело", - "console": "Конзола", - "selectShell": "Изаберите љуску", - "bash": "Баш", - "sh": "ш", - "ash": "пепео", - "connect": "Повежи се", - "disconnect": "Прекини везу", - "notConnected": "Није повезано", - "clickToConnect": "Кликните на повезивање да бисте покренули сесију шкољке", - "connectingTo": "Повезивање са {{containerName}}...", - "containerNotFound": "Контејнер није пронађен", - "backToList": "Назад на листу", - "logs": "Дневници", - "stats": "Статистика", - "consoleTab": "Конзола", - "startContainerToAccess": "Покрените контејнер да бисте приступили конзоли" + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Опште", - "sectionOidc": "ОИДЦ", - "sectionUsers": "Корисници", - "sectionSessions": "Сесије", - "sectionRoles": "Улоге", - "sectionDatabase": "База података", - "sectionApiKeys": "API кључеви", - "allowRegistration": "Дозволи регистрацију корисника", - "allowRegistrationDesc": "Дозволите новим корисницима да се сами региструју", - "allowPasswordLogin": "Дозволи пријаву лозинком", - "allowPasswordLoginDesc": "Пријава корисничким именом/лозинком", - "oidcAutoProvision": "Аутоматско обезбеђивање OIDC-а", - "oidcAutoProvisionDesc": "Аутоматски креирајте налоге за OIDC кориснике чак и када је регистрација онемогућена", - "allowPasswordReset": "Дозволи ресетовање лозинке", - "allowPasswordResetDesc": "Ресетујте код путем Докер логова", - "sessionTimeout": "Временско ограничење сесије", - "hours": "сати", - "sessionTimeoutRange": "Мин. 1 сат · Макс. 720 сати", - "monitoringDefaults": "Праћење подразумеваних подешавања", - "statusCheck": "Провера статуса", - "metrics": "Метрике", - "sec": "сек", - "logLevel": "Ниво логова", - "enableGuacamole": "Омогући гуакамоле", - "enableGuacamoleDesc": "RDP/VNC удаљена радна површина", - "guacdUrl": "URL адреса guacd-а", - "oidcDescription": "Конфигуришите OpenID Connect за SSO. Поља означена * су обавезна.", - "oidcClientId": "ИД клијента", - "oidcClientSecret": "Тајна клијентска порука", - "oidcAuthUrl": "URL за ауторизацију", - "oidcIssuerUrl": "URL издаваоца", - "oidcTokenUrl": "URL адреса токена", - "oidcUserIdentifier": "Путања идентификатора корисника", - "oidcDisplayName": "Путања приказаног имена", - "oidcScopes": "Опсези", - "oidcUserinfoUrl": "Замени URL са корисничким информацијама", - "oidcAllowedUsers": "Дозвољени корисници", - "oidcAllowedUsersDesc": "Једна имејл адреса по реду. Оставите празно да бисте дозволили све.", - "removeOidc": "Уклони", - "usersCount": "{{count}} корисника", - "createUser": "Креирај", - "newRole": "Нова улога", - "roleName": "Име", - "roleDisplayName": "Приказано име", - "roleDescription": "Опис", - "rolesCount": "{{count}} улоге", - "createRole": "Креирај", - "creating": "Креирање...", - "exportDatabase": "Извоз базе података", - "exportDatabaseDesc": "Преузмите резервну копију свих хостова, акредитива и подешавања", - "export": "Извоз", - "exporting": "Извоз...", - "importDatabase": "Увоз базе података", - "importDatabaseDesc": "Враћање из .sqlite датотеке резервне копије", - "importDatabaseSelected": "Изабрано: {{name}}", - "selectFile": "Изаберите датотеку", - "changeFile": "Промена", - "import": "Увоз", - "importing": "Увоз...", - "apiKeysCount": "{{count}} тастери", - "newApiKey": "Нови API кључ", - "apiKeyCreatedWarning": "Кључ је креиран - копирајте га сада, неће се више приказивати.", - "apiKeyName": "Име", - "apiKeyUser": "Корисник", - "apiKeySelectUser": "Изаберите корисника...", - "apiKeyExpiresAt": "Истиче у", - "createKey": "Направи кључ", - "apiKeyNoExpiry": "Без истека", - "revokedBadge": "ПОНИШТЕНО", - "authTypeDual": "Двострука аутентификација", - "authTypeOidc": "ОИДЦ", - "authTypeLocal": "Локално", - "adminStatusAdministrator": "Администратор", - "adminStatusRegularUser": "Редован корисник", - "adminBadge": "АДМИНИСТРАТОР", - "systemBadge": "СИСТЕМ", - "customBadge": "ПРИЛАГОЂЕНО", - "youBadge": "ТИ", - "sessionsActive": "{{count}} активно", - "sessionActive": "Активно: {{time}}", - "sessionExpires": "Рок трајања: {{time}}", - "revokeAll": "Сви", - "revokeAllSessionsSuccess": "Све сесије за корисника су опозване", - "revokeAllSessionsFailed": "Опозивање сесија није успело", - "revokeSessionFailed": "Опозивање сесије није успело", - "addRole": "Додај улогу", - "noCustomRoles": "Нема дефинисаних прилагођених улога", - "removeRoleFailed": "Уклањање улоге није успело", - "assignRoleFailed": "Додељивање улоге није успело", - "deleteRoleFailed": "Брисање улоге није успело", - "userAdminAccess": "Администратор", - "userAdminAccessDesc": "Потпун приступ свим администраторским подешавањима", - "userRoles": "Улоге", - "revokeAllUserSessions": "Поништи све сесије", - "revokeAllUserSessionsDesc": "Присили поновно пријављивање на свим уређајима", - "revoke": "Поништи", - "deleteUserWarning": "Брисање овог корисника је трајно.", - "deleteUser": "Обриши {{username}}", - "deleting": "Брисање...", - "deleteUserFailed": "Брисање корисника није успело", - "deleteUserSuccess": "Корисник „{{username}}“ је обрисан", - "deleteRoleSuccess": "Улога „{{name}}“ је обрисана", - "revokeKeySuccess": "Кључ „{{name}}“ је опозван", - "revokeKeyFailed": "Опозивање кључа није успело", - "copiedToClipboard": "Копирано у међуспремник", - "done": "Готово", - "createUserTitle": "Креирај корисника", - "createUserDesc": "Направите нови локални налог.", - "createUserUsername": "Корисничко име", - "createUserPassword": "Лозинка", - "createUserPasswordHint": "Минимум 6 карактера.", - "createUserEnterUsername": "Унесите корисничко име", - "createUserEnterPassword": "Унесите лозинку", - "createUserSubmit": "Креирај корисника", - "editUserTitle": "Управљај корисником: {{username}}", - "editUserDesc": "Уредите улоге, администраторски статус, сесије и подешавања налога.", - "editUserUsername": "Корисничко име", - "editUserAuthType": "Тип овлашћења", - "editUserAdminStatus": "Статус администратора", - "editUserUserId": "ИД корисника", - "linkAccountTitle": "Повежите OIDC са налогом са лозинком", - "linkAccountDesc": "Спојите OIDC налог {{username}} са постојећим локалним налогом.", - "linkAccountWarningTitle": "Ово ће:", - "linkAccountEffect1": "Избришите само OIDC налог", - "linkAccountEffect2": "Додајте OIDC пријаву на циљни налог", - "linkAccountEffect3": "Дозволи пријаву и OIDC и лозинком", - "linkAccountTargetUsername": "Циљно корисничко име", - "linkAccountTargetPlaceholder": "Унесите корисничко име локалног налога за повезивање", - "linkAccounts": "Повежи налоге", - "linkAccountSuccess": "OIDC налог повезан са „{{username}}“", - "linkAccountFailed": "Повезивање OIDC налога није успело", - "linkAccountInProgress": "Повезивање...", - "saving": "Чување...", - "updateRegistrationFailed": "Ажурирање подешавања регистрације није успело", - "updatePasswordLoginFailed": "Ажурирање подешавања за пријаву лозинком није успело", - "updateOidcAutoProvisionFailed": "Ажурирање подешавања аутоматског обезбеђивања OIDC-а није успело", - "updatePasswordResetFailed": "Ажурирање подешавања за ресетовање лозинке није успело", - "sessionTimeoutRange2": "Временско ограничење сесије мора бити између 1 и 720 сати", - "sessionTimeoutSaved": "Временско ограничење сесије је сачувано", - "sessionTimeoutSaveFailed": "Чување временског ограничења сесије није успело", - "monitoringIntervalInvalid": "Неважеће вредности интервала", - "monitoringSaved": "Подешавања праћења су сачувана", - "monitoringSaveFailed": "Чување подешавања праћења није успело", - "guacamoleSaved": "Подешавања за гуакамоле су сачувана", - "guacamoleSaveFailed": "Чување подешавања за гуакамоле није успело", - "guacamoleUpdateFailed": "Ажурирање подешавања за гвакамоле није успело", - "logLevelUpdateFailed": "Ажурирање нивоа евиденције није успело", - "oidcSaved": "OIDC конфигурација је сачувана", - "oidcSaveFailed": "Чување OIDC конфигурације није успело", - "oidcRemoved": "OIDC конфигурација је уклоњена", - "oidcRemoveFailed": "Уклањање OIDC конфигурације није успело", - "createUserRequired": "Корисничко име и лозинка су обавезни", - "createUserPasswordTooShort": "Лозинка мора имати најмање 6 карактера", - "createUserSuccess": "Корисник „{{username}}“ је креирао", - "createUserFailed": "Није успело креирање корисника", - "updateAdminStatusFailed": "Ажурирање статуса администратора није успело", - "allSessionsRevoked": "Све сесије су опозване", - "revokeSessionsFailed": "Опозивање сесија није успело", - "createRoleRequired": "Име и име за приказ су обавезни", - "createRoleSuccess": "Улога „{{name}}“ је креирана", - "createRoleFailed": "Није успело креирање улоге", - "apiKeyNameRequired": "Назив кључа је обавезан", - "apiKeyUserRequired": "ИД корисника је обавезан", - "apiKeyCreatedSuccess": "API кључ „{{name}}“ је креиран", - "apiKeyCreateFailed": "Није успело креирање API кључа", - "exportSuccess": "База података је успешно извезена", - "exportFailed": "Извоз базе података није успео", - "importSelectFile": "Прво изаберите датотеку", - "importCompleted": "Увоз завршен: {{total}} ставки је увезено, {{skipped}} прескочено", - "importFailed": "Увоз није успео: {{error}}", - "importError": "Увоз базе података није успео" + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Домаћин", - "hostPlaceholder": "192.168.1.1 или example.com", - "portLabel": "Лука", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Корисничко име", - "usernamePlaceholder": "корисничко име", - "authLabel": "Овлашћење", - "passwordLabel": "Лозинка", - "passwordPlaceholder": "лозинка", - "privateKeyLabel": "Приватни кључ", - "privateKeyPlaceholder": "Налепи приватни кључ...", - "credentialLabel": "Акредитив", - "credentialPlaceholder": "Изаберите сачувани акредитив", - "connectToTerminal": "Повежи се са терминалом", - "connectToFiles": "Повежи се са датотекама" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Није изабран терминал", - "noTerminalSelectedHint": "Отворите картицу SSH терминала да бисте видели историју његових команди", - "searchPlaceholder": "Историја претраге...", - "clearAll": "Обриши све", - "noHistoryEntries": "Нема уноса у историји", - "trackingDisabled": "Праћење историје је онемогућено", - "trackingDisabledHint": "Омогућите то у подешавањима профила да бисте снимали команде." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Снимање кључа", - "recordToTerminals": "Снимање на терминале", - "selectAll": "Сви", - "selectNone": "Ниједан", - "noTerminalTabsOpen": "Нема отворених картица терминала", - "selectTerminalsAbove": "Изаберите терминале изнад", - "broadcastInputPlaceholder": "Куцајте овде да бисте емитовали притиске тастера...", - "stopRecording": "Заустави снимање", - "startRecording": "Почни снимање", - "settingsTitle": "Подешавања", - "enableRightClickCopyPaste": "Омогући копирање/лепљење десним кликом" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Распоред", - "selectLayoutAbove": "Изаберите распоред изнад", - "selectLayoutHint": "Изаберите колико панела ће се приказивати", - "panesTitle": "Оквири", - "openTabsTitle": "Отворене картице", - "dragTabsHint": "Превуците картице у горње окне или користите Брзо додељивање", - "dropHere": "Спусти овде", - "emptyPane": "Празно", - "dashboard": "Контролна табла", - "clearSplitScreen": "Обриши подељени екран", - "quickAssign": "Брзо додељивање", - "alreadyAssigned": "Окно {{index}}", - "splitTab": "Подели картицу", - "addToSplit": "Додај у Сплит", - "removeFromSplit": "Уклони из Сплита", - "assignToPane": "Додели окну" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Направи исечак", - "createSnippetDescription": "Направите нови исечак команде за брзо извршавање", - "nameLabel": "Име", - "namePlaceholder": "нпр. Рестартујте Nginx", - "descriptionLabel": "Опис", - "descriptionPlaceholder": "Опциони опис", - "optional": "Опционо", - "folderLabel": "Фасцикла", - "noFolder": "Нема фасцикле (Некатегоризовано)", - "commandLabel": "Команда", - "commandPlaceholder": "нпр. sudo systemctl рестарт nginx", - "cancel": "Откажи", - "createSnippetButton": "Направи исечак", - "createFolderTitle": "Направи фасциклу", - "createFolderDescription": "Организујте своје фрагменте у фасцикле", - "folderNameLabel": "Назив фасцикле", - "folderNamePlaceholder": "нпр. системске команде, Докер скрипте", - "folderColorLabel": "Боја фасцикле", - "folderIconLabel": "Икона фасцикле", - "previewLabel": "Преглед", - "folderNameFallback": "Назив фасцикле", - "createFolderButton": "Направи фасциклу", - "targetTerminals": "Циљни терминали", - "selectAll": "Сви", - "selectNone": "Ниједан", - "noTerminalTabsOpen": "Нема отворених картица терминала", - "searchPlaceholder": "Претражи фрагменте...", - "newSnippet": "Нови исечак", - "newFolder": "Нова фасцикла", - "run": "Трчи", - "noSnippetsInFolder": "Нема фрагмената у овој фасцикли", - "uncategorized": "Некатегоризовано", - "editSnippetTitle": "Измени исечак", - "editSnippetDescription": "Ажурирајте овај исечак команде", - "saveSnippetButton": "Сачувај измене", - "createSuccess": "Исечак је успешно креиран", - "createFailed": "Није успело креирање фрагмента", - "updateSuccess": "Исечак је успешно ажуриран", - "updateFailed": "Ажурирање исечка није успело", - "deleteFailed": "Брисање исечка није успело", - "folderCreateSuccess": "Фолдер је успешно креиран", - "folderCreateFailed": "Није успело креирање фасцикле", - "editFolderTitle": "Уреди фасциклу", - "editFolderDescription": "Преименујте или промените изглед ове фасцикле", - "saveFolderButton": "Сачувај измене", - "editFolder": "Уреди фасциклу", - "deleteFolder": "Обриши фасциклу", - "folderDeleteSuccess": "Фасцикла „{{name}}“ је обрисана", - "folderDeleteFailed": "Брисање фасцикле није успело", - "folderEditSuccess": "Фолдер је успешно ажуриран", - "folderEditFailed": "Ажурирање фасцикле није успело", - "confirmRunMessage": "Покрени „{{name}}“?", - "confirmRunButton": "Трчи", - "runSuccess": "Покренуо је „{{name}}“ у {{count}} терминалу(има)", - "copySuccess": "Копирано „{{name}}“ у међуспремник", - "shareTitle": "Дели исечак", - "shareUser": "Корисник", - "shareRole": "Улога", - "selectUser": "Изаберите корисника...", - "selectRole": "Изаберите улогу...", - "shareSuccess": "Исечак је успешно дељен", - "shareFailed": "Дељење фрагмента није успело", - "revokeSuccess": "Приступ је опозван", - "revokeFailed": "Опозивање приступа није успело", - "currentAccess": "Тренутни приступ", - "shareLoadError": "Учитавање података о дељењу није успело", - "loading": "Учитавање...", - "close": "Затвори", - "reorderFailed": "Чување редоследа исечака није успело" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Налог", - "sectionAppearance": "Изглед", - "sectionSecurity": "Безбедност", - "sectionApiKeys": "API кључеви", - "sectionC2sTunnels": "C2S тунели", - "usernameLabel": "Корисничко име", - "roleLabel": "Улога", - "roleAdministrator": "Администратор", - "authMethodLabel": "Метода овлашћења", - "authMethodLocal": "Локално", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Укључено", - "twoFaOff": "Искључено", - "versionLabel": "Верзија", - "deleteAccount": "Обриши налог", - "deleteAccountDescription": "Трајно избришите свој налог", - "deleteButton": "Обриши", - "deleteAccountPermanent": "Ова радња је трајна и не може се поништити.", - "deleteAccountWarning": "Све сесије, хостови, акредитиви и подешавања биће трајно избрисани.", - "confirmPasswordDeletePlaceholder": "Унесите лозинку да бисте потврдили", - "languageLabel": "Језик", - "themeLabel": "Тема", - "fontSizeLabel": "Величина фонта", - "accentColorLabel": "Акцентна боја", - "settingsTerminal": "Терминал", - "commandAutocomplete": "Аутоматско довршавање команди", - "commandAutocompleteDesc": "Прикажи аутоматско довршавање током куцања", - "historyTracking": "Праћење историје", - "historyTrackingDesc": "Праћење терминалних команди", - "syntaxHighlighting": "Истицање синтаксе", - "syntaxHighlightingDesc": "Истакните излаз терминала", - "commandPalette": "Палета команди", - "commandPaletteDesc": "Омогући пречицу на тастатури", - "reopenTabsOnLogin": "Поново отвори картице при пријављивању", - "reopenTabsOnLoginDesc": "Вратите отворене картице када се пријавите или освежите страницу, чак и са другог уређаја", - "confirmTabClose": "Потврди затварање картице", - "confirmTabCloseDesc": "Питај пре затварања картица терминала", - "settingsSidebar": "Бочна трака", - "showHostTags": "Прикажи ознаке хоста", - "showHostTagsDesc": "Приказ ознака на листи хостова", - "hostTrayOnClick": "Кликните да проширите Акције хоста", - "hostTrayOnClickDesc": "Увек приказуј дугмад за повезивање; кликните да бисте проширили опције управљања уместо да пређете показивачем миша изнад њих.", - "pinAppRail": "Закачи шину апликација", - "pinAppRailDesc": "Нека лева бочна трака апликације увек буде проширена уместо да се проширује при преласку мишем преко ње", - "settingsSnippets": "Исечци", - "foldersCollapsed": "Фасцикле су склопљене", - "foldersCollapsedDesc": "Сажми фасцикле подразумевано", - "confirmExecution": "Потврди извршење", - "confirmExecutionDesc": "Потврдите пре покретања фрагмената", - "settingsUpdates": "Ажурирања", - "disableUpdateChecks": "Онемогући провере ажурирања", - "disableUpdateChecksDesc": "Престани да провераваш ажурирања", - "totpAuthenticator": "TOTP аутентификатор", - "totpEnabled": "2FA је омогућена", - "totpDisabled": "Додајте додатну безбедност пријављивања", - "disable": "Онемогући", - "enable": "Омогући", - "setupTotp": "Подешавање TOTP-а", - "qrCode": "QR код", - "totpInstructions": "Скенирајте QR код или унесите тајни код у апликацију за аутентификацију, а затим унесите шестоцифрени код", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Верификуј", - "changePassword": "Промена лозинке", - "currentPasswordLabel": "Тренутна лозинка", - "currentPasswordPlaceholder": "Тренутна лозинка", - "newPasswordLabel": "Нова лозинка", - "newPasswordPlaceholder": "Нова лозинка", - "confirmPasswordLabel": "Потврдите нову лозинку", - "confirmPasswordPlaceholder": "Потврдите нову лозинку", - "updatePassword": "Ажурирај лозинку", - "createApiKeyTitle": "Креирај API кључ", - "createApiKeyDescription": "Генеришите нови API кључ за програмски приступ.", - "apiKeyNameLabel": "Име", - "apiKeyNamePlaceholder": "нпр. CI цевовод", - "expiryDateLabel": "Датум истека", - "optional": "опционо", - "cancel": "Откажи", - "createKey": "Направи кључ", - "apiKeyCount": "{{count}} тастери", - "newKey": "Нови кључ", - "noApiKeys": "Још нема API кључева.", - "apiKeyActive": "Активно", - "apiKeyUsageHint": "Укључите свој кључ у", - "apiKeyUsageHintHeader": "заглавље.", - "apiKeyPermissionsHint": "Кључеви наслеђују дозволе корисника који их креира.", - "roleUser": "Корисник", - "authMethodDual": "Двострука аутентификација", - "authMethodOidc": "ОИДЦ", - "totpSetupFailed": "Покретање подешавања TOTP-а није успело", - "totpEnter6Digits": "Унесите шестоцифрени код", - "totpEnabledSuccess": "Омогућена је двофакторска аутентификација", - "totpInvalidCode": "Неважећи код, покушајте поново", - "totpDisableInputRequired": "Унесите свој TOTP код или лозинку", - "totpDisabledSuccess": "Двофакторска аутентификација је онемогућена", - "totpDisableFailed": "Онемогућавање 2FA није успело", - "totpDisableTitle": "Онемогући 2FA", - "totpDisablePlaceholder": "Унесите TOTP код или лозинку", - "totpDisableConfirm": "Онемогући 2FA", - "totpContinueVerify": "Настави са верификацијом", - "totpVerifyTitle": "Верификујте код", - "totpBackupTitle": "Резервни кодови", - "totpDownloadBackup": "Преузмите резервне кодове", - "done": "Готово", - "secretCopied": "Тајни токен је копиран у међуспремник", - "apiKeyNameRequired": "Назив кључа је обавезан", - "apiKeyCreated": "API кључ „{{name}}“ је креиран", - "apiKeyCreateFailed": "Није успело креирање API кључа", - "apiKeyUser": "Корисник", - "apiKeyExpires": "Истиче", - "apiKeyRevoked": "API кључ „{{name}}“ је опозван", - "apiKeyRevokeFailed": "Опозивање API кључа није успело", - "passwordFieldsRequired": "Потребне су тренутна и нова лозинка", - "passwordMismatch": "Лозинке се не подударају", - "passwordTooShort": "Лозинка мора имати најмање 6 карактера", - "passwordUpdated": "Лозинка је успешно ажурирана", - "passwordUpdateFailed": "Ажурирање лозинке није успело", - "deletePasswordRequired": "За брисање налога потребна је лозинка", - "deleteFailed": "Брисање налога није успело", - "deleting": "Брисање...", - "colorPickerTooltip": "Отвори бирач боја", - "themeSystem": "Систем", - "themeLight": "Светло", - "themeDark": "Тамно", - "themeDracula": "Дракула", - "themeCatppuccin": "Катпучин", - "themeNord": "Норд", - "themeSolarized": "Соларизовано", - "themeTokyoNight": "Токијска ноћ", - "themeOneDark": "Једна тама", - "themeGruvbox": "Грувбокс" + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/sv_SE.json b/src/ui/locales/translated/sv_SE.json index 012cf333..9bc5af32 100644 --- a/src/ui/locales/translated/sv_SE.json +++ b/src/ui/locales/translated/sv_SE.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Mappar", - "folder": "Mapp", - "password": "Lösenord", - "key": "Nyckel", - "sshPrivateKey": "SSH privat nyckel", - "upload": "Ladda upp", - "keyPassword": "Nyckel Lösenord", - "sshKey": "SSH-nyckel", - "uploadPrivateKeyFile": "Ladda upp privat nyckelfil", - "searchCredentials": "Sök användaruppgifter...", - "addCredential": "Lägg till autentiseringsuppgifter", - "caCertificate": "Certifikat från CA (-cert.pub)", - "caCertificateDescription": "Valfritt: Ladda upp eller klistra in den CA-signerade certifikatfilen (t.ex. id_ed25519-cert.pub). Krävs när din SSH-server använder certifikatbaserad auktorisering.", - "uploadCertFile": "Ladda upp -cert.pub fil", - "clearCert": "Rensa", - "certLoaded": "Certifikat laddat", - "certPublicKeyLabel": "Certifikat från CA", - "certTypeLabel": "Typ av certifikat", - "pasteOrUploadCert": "Klistra in eller ladda upp ett -cert.pub certifikat...", - "hasCaCert": "Har CA-certifikat", - "noCaCert": "Inget CA-certifikat" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Det gick inte att ladda varningar", - "failedToDismissAlert": "Det gick inte att avfärda varningen" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Serverns konfiguration", - "description": "Konfigurera Termix server-URL för att ansluta till dina backend-tjänster", + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", "serverUrl": "Server URL", - "enterServerUrl": "Ange en server-URL", - "saveFailed": "Det gick inte att spara konfigurationen", - "saveError": "Fel vid sparande av konfiguration", - "saving": "Sparar...", - "saveConfig": "Spara konfiguration", - "helpText": "Ange den URL där din Termix server körs (t.ex., http://localhost:30001 eller https://your-server.com)", - "changeServer": "Ändra server", - "mustIncludeProtocol": "Server-URL måste börja med http:// eller https://", - "allowInvalidCertificate": "Tillåt ogiltigt certifikat", - "allowInvalidCertificateDesc": "Använd endast för betrodda självhostade servrar med självsignerade certifikat eller IP-adresscertifikat.", - "useEmbedded": "Använd lokal server", - "embeddedDesc": "Kör Termix med den inbyggda lokala servern (ingen fjärrserver behövs)", - "embeddedConnecting": "Ansluter till lokal server...", - "embeddedNotReady": "Lokal server är inte redo än. Vänligen vänta en stund och försök igen.", - "localServer": "Lokal server" + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Fel vid versionskontroll", - "checkFailed": "Det gick inte att söka efter uppdateringar", - "upToDate": "Appen är uppdaterad", - "currentVersion": "Du kör version {{version}}", - "updateAvailable": "Uppdatering tillgänglig", - "newVersionAvailable": "En ny version är tillgänglig! Du kör {{current}}, men {{latest}} är tillgänglig.", - "betaVersion": "Beta version", - "betaVersionDesc": "Du kör {{current}}som är nyare än den senaste stabila utgåvan {{latest}}.", - "releasedOn": "Släppt på {{date}}", - "downloadUpdate": "Ladda ner uppdatering", - "checking": "Söker efter uppdateringar...", - "checkUpdates": "Sök efter uppdateringar", - "checkingUpdates": "Söker efter uppdateringar...", - "updateRequired": "Uppdatering krävs" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Stäng", + "close": "Close", "minimize": "Minimize", "online": "Online", "offline": "Offline", - "continue": "Fortsätt", - "maintenance": "Underhåll", - "degraded": "Avbruten", - "error": "Fel", - "warning": "Varning", - "unsavedChanges": "Osparade ändringar", - "dismiss": "Avfärda", - "loading": "Laddar...", - "optional": "Valfri", - "connect": "Anslut", - "copied": "Kopierad", - "connecting": "Ansluter...", - "updateAvailable": "Uppdatering tillgänglig", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Öppna i ny flik", - "noReleases": "Inga releaser", - "updatesAndReleases": "Uppdateringar och utgåvor", - "newVersionAvailable": "En ny version ({{version}}) är tillgänglig.", - "failedToFetchUpdateInfo": "Det gick inte att hämta uppdateringsinformation", - "preRelease": "Försläpp", - "noReleasesFound": "Inga utgåvor hittades.", - "cancel": "Avbryt", - "username": "Användarnamn", - "login": "Inloggning", - "register": "Registrera", - "password": "Lösenord", - "confirmPassword": "Bekräfta lösenord", - "back": "Tillbaka", - "save": "Spara", - "saving": "Sparar...", - "delete": "Radera", - "rename": "Döp om", - "edit": "Redigera", - "add": "Lägg till", - "confirm": "Bekräfta", - "no": "Nej", - "or": "ELLER", - "next": "Nästa", - "previous": "Föregående", - "refresh": "Uppdatera", - "language": "Språk", - "checking": "Kontrollerar...", - "checkingDatabase": "Kontrollerar databasanslutning...", - "checkingAuthentication": "Kontrollerar autentisering...", - "backendReconnected": "Serveranslutning återställd", - "connectionDegraded": "Serveranslutning förlorad, återställer…", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Radera", - "create": "Skapa", - "update": "Uppdatera", - "copy": "Kopiera", - "copyFailed": "Det gick inte att kopiera till urklipp", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Återställ", - "of": "av" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Hem", + "home": "Home", "terminal": "Terminal", "docker": "Docker", - "tunnels": "Tunnlar", - "fileManager": "Filhanterare", - "serverStats": "Server statistik", - "admin": "Administratör", - "userProfile": "Användarprofil", - "splitScreen": "Delad skärm", - "confirmClose": "Stäng denna aktiva session?", - "close": "Stäng", - "cancel": "Avbryt", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", "sshManager": "SSH Manager", - "cannotSplitTab": "Kan inte dela den här fliken", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Kopiera lösenord", - "copySudoPassword": "Kopiera Sudo lösenord", - "passwordCopied": "Lösenord kopierat till urklipp", - "noPasswordAvailable": "Inget lösenord tillgängligt", - "failedToCopyPassword": "Det gick inte att kopiera lösenord", - "refreshTab": "Uppdatera anslutning", - "openFileManager": "Öppna filhanteraren", - "dashboard": "Instrumentpanel", - "networkGraph": "Nätverksgraf", - "quickConnect": "Snabb anslutning", - "sshTools": "SSH-verktyg", - "history": "Historik", - "hosts": "Värdar", - "snippets": "Textmoduler", - "hostManager": "Värdhanterare", - "credentials": "Användaruppgifter", - "connections": "Anslutningar", - "roleAdministrator": "Administratör", - "roleUser": "Användare" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Värdar", - "noHosts": "Inga SSH-värdar", - "retry": "Försök igen", - "refresh": "Uppdatera", - "optional": "Valfri", - "downloadSample": "Ladda ner exempel", - "failedToDeleteHost": "Det gick inte att ta bort {{name}}", - "importSkipExisting": "Importera (hoppa över befintliga)", - "connectionDetails": "Kontaktuppgifter", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Fjärrskrivbord", + "remoteDesktop": "Remote Desktop", "port": "Port", - "username": "Användarnamn", - "folder": "Mapp", - "tags": "Taggar", - "pin": "Fäst", - "addHost": "Lägg till värd", - "editHost": "Redigera värd", - "cloneHost": "Klona Värd", - "enableTerminal": "Aktivera Terminal", - "enableTunnel": "Aktivera tunnel", - "enableFileManager": "Aktivera filhanteraren", - "enableDocker": "Aktivera Docker", - "defaultPath": "Standard sökväg", - "connection": "Anslutning", - "upload": "Ladda upp", - "authentication": "Autentisering", - "password": "Lösenord", - "key": "Nyckel", - "credential": "Uppgifter", - "none": "Ingen", - "sshPrivateKey": "SSH privat nyckel", - "keyType": "Nyckel typ", - "uploadFile": "Ladda upp fil", - "tabGeneral": "Allmänt", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tunnlar", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Filer", - "tabStats": "Statistik", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Delning", - "tabAuthentication": "Autentisering", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", "terminal": "Terminal", "tunnel": "Tunnel", - "fileManager": "Filhanterare", - "serverStats": "Server statistik", + "fileManager": "File Manager", + "serverStats": "Host Metrics", "status": "Status", - "folderRenamed": "Mappen \"{{oldName}}\" bytt namn till \"{{newName}}\" lyckades", - "failedToRenameFolder": "Det gick inte att byta namn på mapp", - "movedToFolder": "Flyttade till \"{{folder}}\"", - "editHostTooltip": "Redigera värd", - "statusChecks": "Statuskontroller", - "metricsCollection": "Metrics samling", - "metricsInterval": "Intervall för Metrics samling", - "metricsIntervalDesc": "Hur ofta man samlar in serverstatistik (5s - 1h)", - "behavior": "Beteende", - "themePreview": "Förhandsgranska tema", - "theme": "Tema", - "fontFamily": "Teckensnittsfamilj", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Brev mellanrum", - "lineHeight": "Linje höjd", - "cursorStyle": "Markörens stil", - "cursorBlink": "Markör Blink", - "scrollbackBuffer": "Scrollback buffert", - "bellStyle": "Klockans stil", - "rightClickSelectsWord": "Högerklicka väljer ord", - "fastScrollModifier": "Snabb bläddringsmodifierare", - "fastScrollSensitivity": "Snabb rullningskänslighet", - "sshAgentForwarding": "SSH agentvidarebefordran", - "backspaceMode": "Läge för backsteg", - "startupSnippet": "Starta textmodulen", - "selectSnippet": "Välj snippet", - "forceKeyboardInteractive": "Tvinga interaktiv tangentbord", - "overrideCredentialUsername": "Åsidosätt Användarnamn", - "overrideCredentialUsernameDesc": "Använd det användarnamn som anges ovan istället för användarnamnet", - "jumpHostChain": "Hopp värdkedja", - "portKnocking": "Port knackning", - "addKnock": "Lägg till port", - "addProxyNode": "Lägg till nod", - "proxyNode": "Proxy nod", - "proxyType": "Typ av proxy", - "quickActions": "Snabba åtgärder", - "sudoPasswordAutoFill": "Sudo Lösenord Auto-Fill", - "sudoPassword": "Sudo Lösenord", - "keepaliveInterval": "Håll tidsintervall (ms)", - "moshCommand": "MOSH-kommando", - "environmentVariables": "Miljövariabler", - "addVariable": "Lägg till variabel", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Kopiera Terminal URL", - "copyFileManagerUrl": "Kopiera filhanterarens URL", - "copyRemoteDesktopUrl": "Kopiera URL för fjärrskrivbord", - "failedToConnect": "Det gick inte att ansluta till konsolen", - "connect": "Anslut", - "disconnect": "Koppla från", - "start": "Starta", - "enableStatusCheck": "Aktivera statuskontroll", - "enableMetrics": "Aktivera statistik", - "bulkUpdateFailed": "Bulkuppdatering misslyckades", - "selectAll": "Markera alla", - "deselectAll": "Avmarkera alla", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Säker skal", - "virtualNetwork": "Virtuellt nätverk", - "unencryptedShell": "Okrypterad skal", - "addressIp": "Adress / IP", - "friendlyName": "Vänligt namn", - "folderAndAdvanced": "Mapp & Avancerat", - "privateNotes": "Privata anteckningar", - "privateNotesPlaceholder": "Detaljer om denna server...", - "pinToTop": "Fäst högst upp", - "pinToTopDesc": "Visa alltid denna värd högst upp i listan", - "portKnockingSequence": "Portens knackande sekvens", - "addKnockBtn": "Lägg till knock", - "noPortKnocking": "Ingen portknackning konfigurerad.", - "knockPort": "Knacka port", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Fördröjning efter (ms)", - "useSocks5Proxy": "Använd SOCKS5-proxy", - "useSocks5ProxyDesc": "Ruttanslutning via en proxyserver", - "proxyHost": "Proxy värd", - "proxyPort": "Proxy port", - "proxyUsername": "Proxy användarnamn", - "proxyPassword": "Proxy Lösenord", - "proxySingleMode": "Enskild proxy", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", "proxyChainMode": "Proxy Chain", - "you": "Du", - "jumpHostChainLabel": "Hopp värdkedja", - "addJumpBtn": "Lägg till hopp", - "noJumpHosts": "Inga hopp värdar konfigurerade.", - "selectAServer": "Välj en server...", - "sshPort": "SSH-port", - "authMethod": "Auth metod", - "storedCredential": "Lagrad referens", - "selectACredential": "Välj en inloggning...", - "keyTypeLabel": "Nyckel typ", - "keyTypeAuto": "Identifiera automatiskt", - "keyPasteTab": "Klistra in", - "keyUploadTab": "Ladda upp", - "keyFileLoaded": "Nyckelfil laddad", - "keyUploadClick": "Klicka för att ladda upp .pem / .key / .ppk", - "clearKey": "Rensa nyckel", - "keySaved": "SSH-nyckel sparad", - "keyReplaceNotice": "klistra in en ny nyckel nedan för att ersätta den", - "keyPassphraseSaved": "Lösenfras sparad, skriv för att ändra", - "replaceKey": "Ersätt nyckel", - "forceKeyboardInteractiveLabel": "Tvinga interaktiv tangentbord", - "forceKeyboardInteractiveShortDesc": "Tvinga manuell lösenordsinmatning även om nycklar finns", - "terminalAppearance": "Utseende terminal", - "colorTheme": "Färg tema", - "fontFamilyLabel": "Teckensnittsfamilj", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Markörens stil", - "letterSpacingPx": "Brev mellanrum (px)", - "lineHeightLabel": "Linje höjd", - "bellStyleLabel": "Klockans stil", - "backspaceModeLabel": "Läge för backsteg", - "cursorBlinking": "Markör Blinkande", - "cursorBlinkingDesc": "Aktivera blinkande animering för terminalmarkören", - "rightClickSelectsWordLabel": "Högerklicka Väljer Word", - "rightClickSelectsWordShortDesc": "Välj ordet under markören vid högerklicka", - "behaviorAndAdvanced": "Beteende & Avancerat", - "scrollbackBufferLabel": "Scrollback buffert", - "scrollbackMaxLines": "Maximalt antal rader som hålls i historiken", - "sshAgentForwardingLabel": "SSH agentvidarebefordran", - "sshAgentForwardingShortDesc": "Passera dina lokala SSH-nycklar till denna värd", - "enableAutoMosh": "Aktivera Auto-Mosh", - "enableAutoMoshDesc": "Föredrar Mosh över SSH om tillgängligt", - "enableAutoTmux": "Aktivera Auto-Tmux", - "enableAutoTmuxDesc": "Starta eller bifoga automatiskt till tmux-sessionen", - "sudoPasswordAutoFillLabel": "Sudo Lösenord Autofyllning", - "sudoPasswordAutoFillShortDesc": "Ange automatiskt sudo lösenord när du blir ombedd", - "sudoPasswordLabel": "Sudo Lösenord", - "environmentVariablesLabel": "Miljövariabler", - "addVariableBtn": "Lägg till variabel", - "noEnvVars": "Inga miljövariabler konfigurerade.", - "fastScrollModifierLabel": "Snabb bläddringsmodifierare", - "fastScrollSensitivityLabel": "Snabb rullningskänslighet", - "moshCommandLabel": "Mosh Kommando", - "startupSnippetLabel": "Starta textmodulen", - "keepaliveIntervalLabel": "Keepalive-intervall (sekunder)", - "maxKeepaliveMisses": "Max antal missar", - "tunnelSettings": "Inställningar för tunneln", - "enableTunneling": "Aktivera tunnel", - "enableTunnelingDesc": "Aktivera SSH-tunnelfunktionen för denna värd", - "serverTunnelsSection": "Serverns tunnlar", - "addTunnelBtn": "Lägg till tunnel", - "noTunnelsConfigured": "Inga tunnlar konfigurerade.", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", "tunnelLabel": "Tunnel {{number}}", - "tunnelType": "Typ av tunnel", - "tunnelModeLocalDesc": "Vidarebefordra en lokal port till en port på fjärrservern (eller en värd som kan nås från den).", - "tunnelModeRemoteDesc": "Vidarebefordra en port på fjärrservern tillbaka till en lokal port på din maskin.", - "tunnelModeDynamicDesc": "Skapa en SOCKS5-proxy på en lokal port för dynamisk portvidarebefordran.", - "sameHost": "Denna värd (direkt tunnel)", - "endpointHost": "Slutpunkt värd", - "endpointPort": "Slutpunkt port", - "bindHost": "Bind värd", - "sourcePort": "Källans port", - "maxRetries": "Max antal försök", - "retryIntervalS": "Försök igen intervall (er)", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", "autoStartLabel": "Auto-start", - "autoStartDesc": "Anslut automatiskt denna tunnel när värden är laddad", - "tunnelConnecting": "Tunneln ansluter...", - "tunnelDisconnected": "Tunnel frånkopplad", - "failedToConnectTunnel": "Det gick inte att ansluta", - "failedToDisconnectTunnel": "Misslyckades att koppla från", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", "dockerIntegration": "Docker Integration", - "enableDockerMonitor": "Aktivera Docker", - "enableDockerMonitorDesc": "Övervaka och hantera behållare på denna värd via Docker", - "enableFileManagerMonitor": "Aktivera filhanteraren", - "enableFileManagerMonitorDesc": "Bläddra och hantera filer på denna värd via SFTP", - "defaultPathLabel": "Standard sökväg", - "fileManagerPathHint": "Katalogen att öppna när filhanteraren startar för denna värd.", - "statusChecksLabel": "Statuskontroller", - "enableStatusChecks": "Aktivera statuskontroller", - "enableStatusChecksDesc": "Ping denna värd regelbundet för att verifiera tillgängligheten", - "useGlobalInterval": "Använd globalt intervall", - "useGlobalIntervalDesc": "Åsidosätt med intervallet för statuskontroll över hela servern", - "checkIntervalS": "Kontrollera intervall (er)", - "checkIntervalDesc": "Sekunder mellan varje anslutning ping", - "metricsCollectionLabel": "Metrics samling", - "enableMetricsLabel": "Aktivera statistik", - "enableMetricsDesc": "Samla CPU, RAM, disk och nätverksanvändning från denna värd", - "useGlobalMetrics": "Använd globalt intervall", - "useGlobalMetricsDesc": "Åsidosätt med det serveromfattande intervallet", - "metricsIntervalS": "Metrics intervall (er)", - "metricsIntervalDesc2": "Sekunder mellan metriska ögonblicksbilder", - "visibleWidgets": "Synliga widgetar", - "cpuUsageLabel": "CPU användning", - "cpuUsageDesc": "CPU-procent, belastningsmedelvärden, sparkline graf", - "memoryLabel": "Minnesanvändning", - "memoryDesc": "RAM-användning, byte, cachelagrad", - "storageLabel": "Diskanvändning", - "storageDesc": "Diskanvändning per monteringspunkt", - "networkLabel": "Nätverksgränssnitt", - "networkDesc": "Gränssnittslista och bandbredd", - "uptimeLabel": "Drifttid", - "uptimeDesc": "Systemets drifttid och starttid", - "systemInfoLabel": "Systeminformation", - "systemInfoDesc": "OS, kärna, värdnamn, arkitektur", - "recentLoginsLabel": "Senaste inloggningar", - "recentLoginsDesc": "Lyckade och misslyckade inloggningshändelser", - "topProcessesLabel": "Topp processer", - "topProcessesDesc": "PID, CPU%, MEM%, kommando", - "listeningPortsLabel": "Lyssnar på portar", - "listeningPortsDesc": "Öppna portar med process och tillstånd", - "firewallLabel": "Brandvägg", - "firewallDesc": "Brandvägg, AppArmor, SELinux status", - "quickActionsLabel": "Snabba åtgärder", - "quickActionsToolbar": "Snabbåtgärder visas som knappar i serverstatistikens verktygsfält för ett klick kommandoutförande.", - "noQuickActions": "Inga snabba åtgärder ännu.", - "buttonLabel": "Knapp etikett", - "selectSnippetPlaceholder": "Välj snippet...", - "addActionBtn": "Lägg till åtgärd", - "hostSharedSuccessfully": "Värden delades framgångsrikt", - "failedToShareHost": "Misslyckades att dela värd", - "accessRevoked": "Åtkomst återkallad", - "failedToRevokeAccess": "Det gick inte att återkalla åtkomst", - "cancelBtn": "Avbryt", - "savingBtn": "Sparar...", - "addHostBtn": "Lägg till värd", - "hostUpdated": "Värd uppdaterad", - "hostCreated": "Värd skapad", - "failedToSave": "Det gick inte att spara värden", - "credentialUpdated": "Uppgifter uppdaterade", - "credentialCreated": "Uppgifter skapad", - "failedToSaveCredential": "Kunde inte spara autentiseringsuppgifter", - "backToHosts": "Tillbaka till värdar", - "backToCredentials": "Tillbaka till inloggningsuppgifter", - "pinned": "Klistrad", - "noHostsFound": "Inga värdar hittades", - "tryDifferentTerm": "Prova en annan term", - "addFirstHost": "Lägg till din första värd för att komma igång", - "noCredentialsFound": "Inga inloggningsuppgifter hittades", - "addCredentialBtn": "Lägg till autentiseringsuppgifter", - "updateCredentialBtn": "Uppdatera autentiseringsuppgifter", - "features": "Funktioner", - "noFolder": "(Ingen mapp)", - "deleteSelected": "Radera", - "exitSelection": "Avsluta markering", - "importSkip": "Importera (hoppa över befintliga)", - "importOverwrite": "Importera (skriv över)", - "collapseBtn": "Komprimera", - "importExportBtn": "Importera / Exportera", - "hostStatusesRefreshed": "Värdstatusar uppdaterade", - "failedToRefreshHosts": "Det gick inte att uppdatera värdar", - "movedHostTo": "Flyttade {{host}} till \"{{folder}}\"", - "failedToMoveHost": "Det gick inte att flytta värd", - "folderRenamedTo": "Mappen har bytt namn till \"{{name}}\"", - "deletedFolder": "Raderad mapp \"{{name}}\"", - "failedToDeleteFolder": "Kunde inte ta bort mapp", - "deleteAllInFolder": "Ta bort alla värdar i \"{{name}}\"? Detta kan inte ångras.", - "deletedHost": "Raderad {{name}}", - "copiedToClipboard": "Kopierad till urklipp", - "terminalUrlCopied": "Terminal URL kopierad", - "fileManagerUrlCopied": "Filhanterarens URL har kopierats", - "tunnelUrlCopied": "Tunnel URL kopierad", - "dockerUrlCopied": "Docker URL kopierad", - "serverStatsUrlCopied": "Server statistik-URL kopierad", - "rdpUrlCopied": "RDP-URL kopierad", - "vncUrlCopied": "VNC-URL kopierad", - "telnetUrlCopied": "Telnet URL kopierad", - "remoteDesktopUrlCopied": "URL för fjärrskrivbord har kopierats", - "expandActions": "Expandera åtgärder", - "collapseActions": "Komprimera åtgärder", - "wakeOnLanAction": "Vakna på LAN", - "wakeOnLanSuccess": "Magiskt paket skickat till {{name}}", - "wakeOnLanError": "Misslyckades med att skicka magiskt paket", - "cloneHostAction": "Klona Värd", - "copyAddress": "Kopiera adress", - "copyLink": "Kopiera länk", - "copyTerminalUrlAction": "Kopiera Terminal URL", - "copyFileManagerUrlAction": "Kopiera filhanterarens URL", - "copyTunnelUrlAction": "Kopiera tunnel URL", - "copyDockerUrlAction": "Kopiera Docker-URL", - "copyServerStatsUrlAction": "Kopiera serverstatistik URL", - "copyRdpUrlAction": "Kopiera RDP-URL", - "copyVncUrlAction": "Kopiera VNC-URL", - "copyTelnetUrlAction": "Kopiera Telnet URL", - "copyRemoteDesktopUrlAction": "Kopiera URL för fjärrskrivbord", - "deleteCredentialConfirm": "Ta bort autentiseringsuppgifter \"{{name}}\"?", - "deletedCredential": "Raderad {{name}}", - "deploySSHKeyTitle": "Distribuera SSH-nyckel", - "deployingBtn": "Utrustar...", - "deployBtn": "Distribuera", - "failedToDeployKey": "Det gick inte att distribuera nyckel", - "deleteHostsConfirm": "Ta bort {{count}} värd{{plural}}? Detta kan inte ångras.", - "movedToRoot": "Flyttad till rot", - "failedToMoveHosts": "Det gick inte att flytta värdar", - "enableTerminalFeature": "Aktivera Terminal", - "disableTerminalFeature": "Inaktivera Terminal", - "enableFilesFeature": "Aktivera filer", - "disableFilesFeature": "Inaktivera filer", - "enableTunnelsFeature": "Aktivera tunnlar", - "disableTunnelsFeature": "Inaktivera tunnlar", - "enableDockerFeature": "Aktivera Docker", - "disableDockerFeature": "Inaktivera Docker", - "addTagsPlaceholder": "Lägg till taggar...", - "authDetails": "Autentiseringsdetaljer", - "credType": "Typ", - "generateKeyPairDesc": "Skapa ett nytt nyckelpar, både privata och offentliga nycklar kommer att fyllas automatiskt.", - "generatingKey": "Genererar...", - "generateLabel": "Generera {{label}}", - "uploadFileBtn": "Ladda upp fil", - "keyPassphraseOptional": "Lösenfras (valfritt)", - "sshPublicKeyOptional": "Publik SSH-nyckel (valfritt)", - "publicKeyGenerated": "Offentlig nyckel genererad", - "failedToGeneratePublicKey": "Det gick inte att härleda publik nyckel", - "publicKeyCopied": "Publik nyckel kopierad", - "keyPairGenerated": "{{label}} nyckelpar genererade", - "failedToGenerateKeyPair": "Det gick inte att generera nyckelpar", - "searchHostsPlaceholder": "Sök värdar, adresser, taggar…", - "searchCredentialsPlaceholder": "Sök inloggningsuppgifter…", - "refreshBtn": "Uppdatera", - "addTag": "Lägg till taggar...", - "deleteConfirmBtn": "Radera", - "tunnelRequirementsText": "SSH-servern måste ha GatewayPorts ja, AllowTcpForwarding ja, och PermitRootLogin ja satt i /etc/ssh/sshd_config.", - "deleteHostConfirm": "Ta bort \"{{name}}\"?", - "enableAtLeastOneProtocol": "Aktivera minst ett protokoll ovan för att konfigurera autentisering och anslutningsinställningar.", - "keyPassphrase": "Lösenfras för nyckel", - "connectBtn": "Anslut", - "disconnectBtn": "Koppla från", - "basicInformation": "Grundläggande information", - "authDetailsSection": "Autentiseringsdetaljer", - "credTypeLabel": "Typ", - "hostsTab": "Värdar", - "credentialsTab": "Användaruppgifter", - "selectMultiple": "Välj flera", - "selectHosts": "Välj värdar", - "connectionLabel": "Anslutning", - "authenticationLabel": "Autentisering", - "generateKeyPairTitle": "Generera nyckelpar", - "generateKeyPairDescription": "Skapa ett nytt nyckelpar, både privata och offentliga nycklar kommer att fyllas automatiskt.", - "generateFromPrivateKey": "Generera från privat nyckel", - "refreshBtn2": "Uppdatera", - "exitSelectionTitle": "Avsluta markering", - "exportAll": "Exportera alla", - "addHostBtn2": "Lägg till värd", - "addCredentialBtn2": "Lägg till autentiseringsuppgifter", - "checkingHostStatuses": "Kontrollerar värdstatus...", - "pinnedSection": "Klistrad", - "hostsExported": "Värdar exporterade framgångsrikt", - "exportFailed": "Misslyckades med att exportera värdar", - "sampleDownloaded": "Exempelfil hämtad", - "failedToDeleteCredential2": "Kunde inte ta bort autentiseringsuppgifter", - "noFolderOption": "(Ingen mapp)", - "nSelected": "{{count}} vald", - "featuresMenu": "Funktioner", - "moveMenu": "Flytta", - "cancelSelection": "Avbryt", - "deployDialogDesc": "Distribuera {{name}} till en värds authorized_keys.", - "targetHostLabel": "Målets värd", - "selectHostOption": "Välj en värd...", - "keyDeployedSuccess": "Nyckeln har installerats", - "failedToDeployKey2": "Det gick inte att distribuera nyckel", - "deletedCount": "Raderade {{count}} värdar", - "failedToDeleteCount": "Det gick inte att ta bort {{count}} värdar", - "duplicatedHost": "Duplicerad \"{{name}}\"", - "failedToDuplicateHost": "Det gick inte att duplicera värd", - "updatedCount": "Uppdaterade {{count}} värdar", - "friendlyNameLabel": "Vänligt namn", - "descriptionLabel": "Beskrivning", - "loadingHost": "Laddar värd...", - "loadingHosts": "Laddar värdar...", - "loadingCredentials": "Laddar referenser...", - "noHostsYet": "Ännu inga värdar", - "noHostsMatchSearch": "Inga värdar matchar din sökning", - "hostNotFound": "Värden hittades inte", - "searchHosts": "Sök värdar...", - "sortHosts": "Sortera värdar", - "sortDefault": "Standardbeställning", - "sortNameAsc": "Namn (A → Ö)", - "sortNameDesc": "Namn (Ö → A)", - "sortIpAsc": "IP-adress (stigande)", - "sortIpDesc": "IP-adress (beskrivning)", - "sortOnlineFirst": "Online först", - "sortOfflineFirst": "Offline först", - "sortPinnedFirst": "Fäst först", - "filterHosts": "Filtrera värdar", - "filterClearAll": "Rensa filter", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", "filterStatusGroup": "Status", "filterOnline": "Online", - "filterOffline": "Off-line", - "filterPinned": "Fäst", - "filterAuthGroup": "Aut.typ", - "filterAuthPassword": "Lösenord", - "filterAuthKey": "SSH-nyckel", - "filterAuthCredential": "Referenser", - "filterAuthNone": "Ingen", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokoll", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "Landsbygdsutveckling", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Drag", + "filterFeaturesGroup": "Features", "filterFeatureTerminal": "Terminal", - "filterFeatureFileManager": "Filhanteraren", + "filterFeatureFileManager": "File Manager", "filterFeatureTunnel": "Tunnel", - "filterFeatureDocker": "Hamnarbetare", - "filterTagsGroup": "Taggar", - "shareHost": "Dela värd", - "shareHostTitle": "Dela: {{name}}", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Denna värd måste använda en inloggning för att aktivera delning. Redigera värden och tilldela en inloggning först." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Anslutning", - "authentication": "Autentisering", - "connectionSettings": "Inställningar för anslutning", - "displaySettings": "Visa inställningar", - "audioSettings": "Inställningar för ljud", - "rdpPerformance": "RDP prestanda", - "deviceRedirection": "Omdirigering av enhet", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", "session": "Session", - "gateway": "Portgång", - "remoteApp": "Fjärrapp", - "clipboard": "Urklipp", - "sessionRecording": "Sessionsinspelning", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC inställningar", - "terminalSettings": "Terminalinställningar", - "rdpPort": "RDP-port", - "username": "Användarnamn", - "password": "Lösenord", - "domain": "Domän", - "securityMode": "Säkerhetsläge", - "colorDepth": "Färg djup", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Höjd", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Ändra storlek", - "clientName": "Klientens namn", - "initialProgram": "Initialt program", - "serverLayout": "Serverns layout", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Gateway port", - "gatewayUsername": "Gateway användarnamn", - "gatewayPassword": "Gateway Lösenord", - "gatewayDomain": "Gateway domän", - "remoteAppProgram": "Fjärrapp Program", - "workingDirectory": "Arbetar katalog", - "arguments": "Argument", - "normalizeLineEndings": "Normalisera radslut", - "recordingPath": "Sökväg för inspelning", - "recordingName": "Inspelningsnamn", - "macAddress": "MAC adress", - "broadcastAddress": "Sändnings adress", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Väntetid (er)", - "driveName": "Enhetens namn", - "drivePath": "Sökväg för enhet", - "ignoreCertificate": "Ignorera certifikat", - "ignoreCertificateDesc": "Tillåt anslutningar till värdar med självsignerade certifikat", - "forceLossless": "Tvinga förlustfri", - "forceLosslessDesc": "Tvinga förlustfri bildkodning (högre kvalitet, mer bandbredd)", - "disableAudio": "Inaktivera ljud", - "disableAudioDesc": "Stäng av allt ljud från fjärrsessionen", - "enableAudioInput": "Aktivera ljudinmatning (mikrofon)", - "enableAudioInputDesc": "Vidarebefordra lokal mikrofon till fjärrsessionen", - "wallpaper": "Bakgrund", - "wallpaperDesc": "Visa skrivbordsunderlägg (inaktivera förbättrar prestanda)", - "theming": "Tema", - "themingDesc": "Aktivera visuella teman och stilar", - "fontSmoothing": "Utjämning av typsnitt", - "fontSmoothingDesc": "Aktivera ClearType-teckensnittsrendering", - "fullWindowDrag": "Helfönstret drar", - "fullWindowDragDesc": "Visa fönsterinnehåll medan du drar", - "desktopComposition": "Skrivbordssammansättning", - "desktopCompositionDesc": "Aktivera Aero-glaseffekter", - "menuAnimations": "Menyanimationer", - "menuAnimationsDesc": "Aktivera menytoning och bildspel", - "disableBitmapCaching": "Inaktivera Bitmap Caching", - "disableBitmapCachingDesc": "Stäng av bitmap cache (kan hjälpa till med buggar)", - "disableOffscreenCaching": "Inaktivera cachning av Offscreen", - "disableOffscreenCachingDesc": "Stäng av offscreen-cache", - "disableGlyphCaching": "Inaktivera Glyph Caching", - "disableGlyphCachingDesc": "Stäng av glyfcache", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Använd RemoteFX grafik pipeline", - "enablePrinting": "Aktivera utskrift", - "enablePrintingDesc": "Omdirigera lokala skrivare till fjärrsessionen", - "enableDriveRedirection": "Aktivera omdirigering av enhet", - "enableDriveRedirectionDesc": "Karta en lokal mapp som en enhet i fjärrsessionen", - "createDrivePath": "Skapa sökväg", - "createDrivePathDesc": "Skapa mappen automatiskt om den inte existerar", - "disableDownload": "Inaktivera nedladdning", - "disableDownloadDesc": "Förhindra nedladdning av filer från fjärrsessionen", - "disableUpload": "Inaktivera uppladdning", - "disableUploadDesc": "Förhindra uppladdning av filer till fjärrsessionen", - "enableTouch": "Aktivera tryck", - "enableTouchDesc": "Aktivera vidarebefordran av tryckindata", - "consoleSession": "Konsolens session", - "consoleSessionDesc": "Anslut till konsolen (session 0) istället för en ny session", - "sendWolPacket": "Skicka WOL-paket", - "sendWolPacketDesc": "Skicka ett magiskt paket för att väcka denna värd innan du ansluter", - "disableCopy": "Inaktivera kopiering", - "disableCopyDesc": "Förhindra kopiering av text från fjärrsessionen", - "disablePaste": "Inaktivera klistra in", - "disablePasteDesc": "Förhindra att klistra in text i fjärrsessionen", - "createPathIfMissing": "Skapa sökväg om det saknas", - "createPathIfMissingDesc": "Skapa automatiskt inspelningsmappen", - "excludeOutput": "Exkludera utdata", - "excludeOutputDesc": "Spela inte in skärmutmatning (endast metadata)", - "excludeMouse": "Exkludera mus", - "excludeMouseDesc": "Spela inte in musrörelser", - "includeKeystrokes": "Inkludera tangenttryckningar", - "includeKeystrokesDesc": "Spela in råa tangenttryckningar förutom skärmutmatning", - "vncPort": "VNC-port", - "vncPassword": "VNC Lösenord", - "vncUsernameOptional": "Användarnamn (valfritt)", - "vncLeaveBlank": "Lämna tomt om det inte krävs", - "cursorMode": "Markörens läge", - "swapRedBlue": "Byt röd/blå", - "swapRedBlueDesc": "Byt ut de röda och blå färgkanalerna (fixar vissa färgfrågor)", - "readOnly": "Skrivskyddad", - "readOnlyDesc": "Visa fjärrskärmen utan att skicka någon ingång", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", "telnetPort": "Telnet Port", - "terminalType": "Terminal typ", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Färgschema", - "backspaceKey": "Nyckel för backsteg", - "saveHostFirst": "Spara värden först.", - "sharingOptionsAfterSave": "Delningsalternativ är tillgängliga efter att värden har sparats.", - "sharingLoadError": "Det gick inte att ladda delningsdata. Kontrollera din anslutning och försök igen.", - "shareHostSection": "Dela värd", - "shareWithUser": "Dela med användare", - "shareWithRole": "Dela med roll", - "selectUser": "Välj användare", - "selectRole": "Välj roll", - "selectUserOption": "Välj en användar...", - "selectRoleOption": "Välj en roll...", - "permissionLevel": "Behörighetsnivå", - "expiresInHours": "Förfaller om (timmar)", - "noExpiryPlaceholder": "Lämna tomt för inget utgångsdatum", - "shareBtn": "Dela", - "currentAccess": "Aktuell åtkomst", - "typeHeader": "Typ", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Behörighet", - "grantedByHeader": "Beviljad av", - "expiresHeader": "Förfaller", - "noAccessEntries": "Inga accessposter ännu.", - "expiredLabel": "Förfallen", - "neverLabel": "Aldrig", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Avbryt", - "savingBtn": "Sparar...", - "updateHostBtn": "Uppdatera värd", - "addHostBtn": "Lägg till värd" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Sök värdar, kommandon eller inställningar...", - "quickActions": "Snabba åtgärder", - "hostManager": "Värdhanterare", - "hostManagerDesc": "Hantera, lägga till eller redigera värdar", - "addNewHost": "Lägg till ny värd", - "addNewHostDesc": "Registrera en ny värd", - "adminSettings": "Administratörsinställningar", - "adminSettingsDesc": "Konfigurera systeminställningar och användare", - "userProfile": "Användarprofil", - "userProfileDesc": "Hantera ditt konto och inställningar", - "addCredential": "Lägg till autentiseringsuppgifter", - "addCredentialDesc": "Spara SSH-nycklar eller lösenord", - "recentActivity": "Senaste aktivitet", - "serversAndHosts": "Servrar och värdar", - "noHostsFound": "Inga värdar hittade matchande \"{{search}}\"", - "links": "Länkar", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Välj", - "toggleWith": "Växla med" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Ingen flik tilldelad", - "focusedPane": "Aktiv ruta" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Inga anslutningar", - "noConnectionsDesc": "Öppna en terminal, filhanterare eller fjärrskrivbord för att se anslutningar här", - "connectedFor": "Ansluten för {{duration}}", - "connected": "Ansluten", - "disconnected": "Osammanhängande", - "closeTab": "Stäng fliken", - "closeConnection": "Stäng anslutning", - "forgetTab": "Glömma", - "removeBackground": "Ta bort", - "reconnect": "Återanslut", - "reopenTab": "Öppna igen", - "sectionOpen": "Öppna", - "sectionBackground": "Bakgrund", - "backgroundDesc": "Sessioner kvarstår i 30 minuter efter frånkoppling och kan återanslutas.", - "persisted": "Fortsatte i bakgrunden", - "expiresIn": "Utgår om {{duration}}", - "search": "Sök efter kopplingar...", - "noSearchResults": "Inga kontakter matchar din sökning" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Ansluter till {{type}} session...", - "connectionError": "Anslutningsfel", - "connectionFailed": "Anslutningen misslyckades", - "failedToConnect": "Det gick inte att hämta anslutningstoken", - "hostNotFound": "Värden hittades inte", - "noHostSelected": "Ingen värd vald", - "reconnect": "Återanslut", - "retry": "Försök igen", - "guacdUnavailable": "Fjärrskrivbordstjänst (guacd) är inte tillgänglig. Se till att guacd körs och är tillgänglig och konfigurerad korrekt i administratörsinställningar.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Lock skärm)", - "winKey": "Windows nyckel", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "Skift", - "win": "Vinn", - "stickyActive": "{{key}} (låst - klicka för att släppa)", - "stickyInactive": "{{key}} (klicka för att låsa)", - "esc": "Fly", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Hem", - "end": "Slut", - "pageUp": "Sida upp", - "pageDown": "Sida ner", - "arrowUp": "Pil upp", - "arrowDown": "Pil ner", - "arrowLeft": "Pil vänster", - "arrowRight": "Pil till höger", - "fnToggle": "Funktionsnycklar", - "reconnect": "Återanslut sessionen", - "collapse": "Komprimera verktygsfältet", - "expand": "Expandera verktygsfältet", - "dragHandle": "Dra för att flytta" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Anslut till värd", - "clear": "Rensa", - "paste": "Klistra in", - "reconnect": "Återanslut", - "connectionLost": "Anslutning förlorad", - "connected": "Ansluten", - "clipboardWriteFailed": "Det gick inte att kopiera till urklipp. Se till att sidan serveras över HTTPS eller localhost.", - "clipboardReadFailed": "Det gick inte att läsa från urklipp. Se till att urklippsbehörigheterna är beviljade.", - "clipboardHttpWarning": "Klistra in kräver HTTPS. Använd Ctrl+Shift+V eller servera Termix över HTTPS.", - "unknownError": "Okänt fel uppstod", - "websocketError": "WebSocket anslutningsfel", - "connecting": "Ansluter...", - "noHostSelected": "Ingen värd vald", - "reconnecting": "Återansluter... ({{attempt}}/{{max}})", - "reconnected": "Återansluten framgångsrikt", - "tmuxSessionCreated": "tmux-session skapad: {{name}}", - "tmuxSessionAttached": "tmux session bifogad: {{name}}", - "tmuxUnavailable": "tmux är inte installerat på fjärrvärden, faller tillbaka till standard skal", - "tmuxSessionPickerTitle": "tmux sessioner", - "tmuxSessionPickerDesc": "Befintliga tmux-sessioner hittades på den här värden. Välj en för att ansluta eller skapa en ny session.", - "tmuxWindows": "Fönster", - "tmuxWindowCount": "{{count}} fönster", - "tmuxAttached": "Bifogade klienter", - "tmuxAttachedCount": "{{count}} bifogad", - "tmuxLastActivity": "Senaste aktivitet", - "tmuxTimeJustNow": "just nu", - "tmuxTimeMinutes": "{{count}}m sedan", - "tmuxTimeHours": "{{count}}h sedan", - "tmuxTimeDays": "{{count}}d sedan", - "tmuxCreateNew": "Starta ny session", - "tmuxCopyHint": "Justera markering och tryck Enter för att kopiera till urklipp", - "tmuxDetach": "Ta bort från tmux session", - "tmuxDetached": "Fristående från tmux-session", - "maxReconnectAttemptsReached": "Maximalt antal återanslutningsförsök har uppnåtts", - "closeTab": "Stäng", - "connectionTimeout": "Tidsgräns för anslutning", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Kör {{command}} - {{host}}", - "totpRequired": "Tvåfaktorsautentisering krävs", - "totpCodeLabel": "Verifieringskod", - "totpVerify": "Verifiera", - "warpgateAuthRequired": "Warpgate-autentisering krävs", - "warpgateSecurityKey": "Säkerhetsnyckel", - "warpgateAuthUrl": "URL för autentisering", - "warpgateOpenBrowser": "Öppna i webbläsare", - "warpgateContinue": "Jag har slutfört autentisering", - "opksshAuthRequired": "OPKSSH-autentisering krävs", - "opksshAuthDescription": "Fullständig autentisering i din webbläsare för att fortsätta. Denna session kommer att förbli giltig i 24 timmar.", - "opksshOpenBrowser": "Öppna webbläsaren för att autentisera", - "opksshWaitingForAuth": "Väntar på autentisering i webbläsaren...", - "opksshAuthenticating": "Bearbetar autentisering...", - "opksshTimeout": "Autentisering gick ut. Försök igen.", - "opksshAuthFailed": "Autentisering misslyckades. Kontrollera dina uppgifter och försök igen.", - "opksshSignInWith": "Logga in med {{provider}}", - "sudoPasswordPopupTitle": "Infoga lösenord?", - "websocketAbnormalClose": "Anslutning stängd oväntat. Detta kan bero på en omvänd proxy eller SSL-konfigurationsproblem. Kontrollera serverloggar.", - "connectionLogTitle": "Anslutningslogg", - "connectionLogCopy": "Kopiera loggar till urklipp", - "connectionLogEmpty": "Inga anslutningsloggar ännu", - "connectionLogWaiting": "Väntar på anslutningsloggar...", - "connectionLogCopied": "Anslutningsloggar kopierade till urklipp", - "connectionLogCopyFailed": "Det gick inte att kopiera loggar till urklipp", - "connectionRejected": "Anslutning avvisad av servern. Kontrollera din autentisering och nätverkskonfiguration.", - "hostKeyRejected": "Verifiering av SSH-värdnyckel avvisad. Anslutningen avbröts.", - "sessionTakenOver": "Sessionen öppnades i en annan flik. Återansluter..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Ingen värd vald", - "initializingEditor": "Initierar redaktör...", - "file": "Fil", - "folder": "Mapp", - "uploadFile": "Ladda upp fil", - "downloadFile": "Hämta", - "extractArchive": "Extrahera arkiv", - "extractingArchive": "Extraherar {{name}}...", - "archiveExtractedSuccessfully": "{{name}} har extraherats", - "extractFailed": "Extrahering misslyckades", - "compressFile": "Komprimera fil", - "compressFiles": "Komprimera filer", - "compressFilesDesc": "Komprimera {{count}} objekt till ett arkiv", - "archiveName": "Arkivets namn", - "enterArchiveName": "Ange arkivnamn...", - "compressionFormat": "Komprimeringsformat", - "selectedFiles": "Valda filer", - "andMoreFiles": "och {{count}} mer...", - "compress": "Komprimera", - "compressingFiles": "Komprimerar {{count}} objekt till {{name}}...", - "filesCompressedSuccessfully": "{{name}} har skapats", - "compressFailed": "Komprimering misslyckades", - "edit": "Redigera", - "preview": "Förhandsgranska", - "previous": "Föregående", - "next": "Nästa", - "pageXOfY": "Sida {{current}} av {{total}}", - "zoomOut": "Zooma ut", - "zoomIn": "Zooma in", - "newFile": "Ny fil", - "newFolder": "Ny mapp", - "rename": "Döp om", - "uploading": "Uppladdar...", - "uploadingFile": "Laddar upp {{name}}...", - "fileName": "Filnamn", - "folderName": "Mappens namn", - "fileUploadedSuccessfully": "Filen \"{{name}}\" laddades upp", - "failedToUploadFile": "Det gick inte att ladda upp fil", - "fileDownloadedSuccessfully": "Filen \"{{name}}\" hämtades framgångsrikt", - "failedToDownloadFile": "Kunde inte ladda ner fil", - "fileCreatedSuccessfully": "Filen \"{{name}}\" har skapats", - "folderCreatedSuccessfully": "Mappen \"{{name}}\" har skapats", - "failedToCreateItem": "Det gick inte att skapa objekt", - "operationFailed": "{{operation}} misslyckades för {{name}}: {{error}}", - "failedToResolveSymlink": "Det gick inte att lösa symbolisk länk", - "itemsDeletedSuccessfully": "{{count}} objekt har tagits bort", - "failedToDeleteItems": "Misslyckades att ta bort objekt", - "sudoPasswordRequired": "Lösenord för administratör krävs", - "enterSudoPassword": "Ange sudo lösenord för att fortsätta åtgärden", - "sudoPassword": "Sudo lösenord", - "sudoOperationFailed": "Sudo operation misslyckades", - "sudoAuthFailed": "Sudo-autentisering misslyckades", - "dragFilesToUpload": "Släpp filer här för att ladda upp", - "emptyFolder": "Denna mapp är tom", - "searchFiles": "Sök filer...", - "upload": "Ladda upp", - "selectHostToStart": "Välj en värd för att starta filhantering", - "sshRequiredForFileManager": "Filhanteraren kräver SSH. Värden har inte SSH aktiverat.", - "failedToConnect": "Det gick inte att ansluta till SSH", - "failedToLoadDirectory": "Det gick inte att ladda katalog", - "noSSHConnection": "Ingen SSH-anslutning tillgänglig", - "copy": "Kopiera", - "cut": "Klipp", - "paste": "Klistra in", - "copyPath": "Kopiera sökväg", - "copyPaths": "Kopiera sökvägar", - "delete": "Radera", - "properties": "Egenskaper", - "refresh": "Uppdatera", - "downloadFiles": "Ladda ner {{count}} filer till Webbläsaren", - "copyFiles": "Kopiera {{count}} objekt", - "cutFiles": "Klipp ut {{count}} objekt", - "deleteFiles": "Ta bort {{count}} objekt", - "filesCopiedToClipboard": "{{count}} objekt kopierade till urklipp", - "filesCutToClipboard": "{{count}} objekt klippta till urklipp", - "pathCopiedToClipboard": "Sökväg kopierad till urklipp", - "pathsCopiedToClipboard": "{{count}} vägar kopierade till urklipp", - "failedToCopyPath": "Det gick inte att kopiera sökvägen till urklipp", - "movedItems": "Flyttade {{count}} objekt", - "failedToDeleteItem": "Misslyckades att ta bort objekt", - "itemRenamedSuccessfully": "{{type}} omdöpt framgångsrikt", - "failedToRenameItem": "Det gick inte att byta namn på objekt", - "download": "Hämta", - "permissions": "Behörigheter", - "size": "Storlek", - "modified": "Ändrad", - "path": "Sökväg", - "confirmDelete": "Är du säker på att du vill radera {{name}}?", - "permissionDenied": "Åtkomst nekad", - "serverError": "Serverfel", - "fileSavedSuccessfully": "Filen har sparats", - "failedToSaveFile": "Det gick inte att spara filen", - "confirmDeleteSingleItem": "Är du säker på att du vill ta bort permanent \"{{name}}\"?", - "confirmDeleteMultipleItems": "Är du säker på att du vill radera {{count}} objekt permanent?", - "confirmDeleteMultipleItemsWithFolders": "Är du säker på att du vill ta bort {{count}} objekt permanent? Detta inkluderar mappar och deras innehåll.", - "confirmDeleteFolder": "Är du säker på att du vill ta bort mappen \"{{name}}\" och allt dess innehåll?", - "permanentDeleteWarning": "Denna åtgärd kan inte ångras. Objektet kommer att raderas permanent från servern.", - "recent": "Senaste", - "pinned": "Klistrad", - "folderShortcuts": "Genvägar för mapp", - "failedToReconnectSSH": "Det gick inte att återansluta SSH-sessionen", - "openTerminalHere": "Öppna Terminal här", - "run": "Kör", - "openTerminalInFolder": "Öppna Terminal i den här mappen", - "openTerminalInFileLocation": "Öppna Terminal på filens plats", - "runningFile": "Körs - {{file}}", - "onlyRunExecutableFiles": "Kan bara köra körbara filer", - "directories": "Kataloger", - "removedFromRecentFiles": "Tog bort \"{{name}}\" från de senaste filerna", - "removeFailed": "Borttagning misslyckades", - "unpinnedSuccessfully": "Ej fäst \"{{name}}\" lyckades", - "unpinFailed": "Lossa misslyckades", - "removedShortcut": "Tog bort genväg \"{{name}}\"", - "removeShortcutFailed": "Ta bort genväg misslyckades", - "clearedAllRecentFiles": "Rensade alla senaste filer", - "clearFailed": "Rensa misslyckades", - "removeFromRecentFiles": "Ta bort från senaste filer", - "clearAllRecentFiles": "Rensa alla senaste filer", - "unpinFile": "Lossa fil", - "removeShortcut": "Ta bort genväg", - "pinFile": "Fäst fil", - "addToShortcuts": "Lägg till genvägar", - "pasteFailed": "Klistra in misslyckades", - "noUndoableActions": "Inga ogenomförbara åtgärder", - "undoCopySuccess": "Ogiltig kopieringsoperation: Raderade {{count}} kopierade filer", - "undoCopyFailedDelete": "Ångra misslyckades: Kunde inte ta bort några kopierade filer", - "undoCopyFailedNoInfo": "Ångra misslyckades: Kunde inte hitta kopierad filinformation", - "undoMoveSuccess": "Ångra flyttning: Flyttade {{count}} filer tillbaka till ursprunglig plats", - "undoMoveFailedMove": "Ångra misslyckades: Kunde inte flytta några filer tillbaka", - "undoMoveFailedNoInfo": "Ångra misslyckades: Kunde inte hitta flyttad filinformation", - "undoDeleteNotSupported": "Borttagningsåtgärden kan inte ångras: Filer har tagits bort permanent från servern", - "undoTypeNotSupported": "Ångra åtgärdstypen stöds inte", - "undoOperationFailed": "Ångra åtgärden misslyckades", - "unknownError": "Okänt fel", - "confirm": "Bekräfta", - "find": "Sök...", - "replace": "Ersätt", - "downloadInstead": "Ladda ner istället", - "keyboardShortcuts": "Genvägar för tangentbord", - "searchAndReplace": "Sök & Ersätt", - "editing": "Redigerar", - "search": "Sök", - "findNext": "Hitta nästa", - "findPrevious": "Hitta föregående", - "save": "Spara", - "selectAll": "Markera alla", - "undo": "Ångra", - "redo": "Gör om", - "moveLineUp": "Flytta linjen uppåt", - "moveLineDown": "Flytta linjen nedåt", - "toggleComment": "Växla kommentar", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Det gick inte att ladda bilden", - "startTyping": "Börja skriva...", - "unknownSize": "Okänd storlek", - "fileIsEmpty": "Filen är tom", - "largeFileWarning": "Stor filvarning", - "largeFileWarningDesc": "Denna fil är {{size}} i storlek, vilket kan orsaka prestandaproblem när den öppnas som text.", - "fileNotFoundAndRemoved": "Filen \"{{name}}\" hittades inte och har tagits bort från senast/fästa filer", - "failedToLoadFile": "Det gick inte att ladda filen: {{error}}", - "serverErrorOccurred": "Serverfel uppstod. Försök igen senare.", - "autoSaveFailed": "Auto-sparning misslyckades", - "fileAutoSaved": "Fil automatiskt sparad", - "moveFileFailed": "Det gick inte att flytta {{name}}", - "moveOperationFailed": "Flyttningen misslyckades", - "canOnlyCompareFiles": "Kan bara jämföra två filer", - "comparingFiles": "Jämföra filer: {{file1}} och {{file2}}", - "dragFailed": "Drag operation misslyckades", - "filePinnedSuccessfully": "Filen \"{{name}}\" fäst framgångsrikt", - "pinFileFailed": "Det gick inte att fästa filen", - "fileUnpinnedSuccessfully": "Filen \"{{name}}\" lossas framgångsrikt", - "unpinFileFailed": "Det gick inte att lossa filen", - "shortcutAddedSuccessfully": "Genväg till mappen \"{{name}}\" har lagts till", - "addShortcutFailed": "Kunde inte lägga till genväg", - "operationCompletedSuccessfully": "{{operation}} {{count}} objekt framgångsrikt", - "operationCompleted": "{{operation}} {{count}} objekt", - "downloadFileSuccess": "Filen {{name}} hämtades framgångsrikt", - "downloadFileFailed": "Nedladdning misslyckades", - "moveTo": "Flytta till {{name}}", - "diffCompareWith": "Diff jämför med {{name}}", - "dragOutsideToDownload": "Dra utanför fönstret för att ladda ner ({{count}} filer)", - "newFolderDefault": "NyMapp", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Framgångsrikt flyttat {{count}} objekt till {{target}}", - "move": "Flytta", - "searchInFile": "Sök i fil (Ctrl+F)", - "showKeyboardShortcuts": "Visa kortkommandon", - "startWritingMarkdown": "Börja skriva ditt markdown-innehåll...", - "loadingFileComparison": "Laddar filjämförelse...", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Jämför", - "sideBySide": "Sida vid sida", - "inline": "Infogad", - "fileComparison": "Filjämförelse: {{file1}} vs {{file2}}", - "fileTooLarge": "Filen är för stor: {{error}}", - "sshConnectionFailed": "SSH-anslutningen misslyckades. Kontrollera din anslutning till {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Det gick inte att ladda filen: {{error}}", - "connecting": "Ansluter...", - "connectedSuccessfully": "Ansluten lyckades", - "totpVerificationFailed": "TOTP-verifiering misslyckades", - "warpgateVerificationFailed": "Warpgate-autentisering misslyckades", - "authenticationFailed": "Autentisering misslyckades", - "verificationCodePrompt": "Verifieringskod:", - "changePermissions": "Ändra behörigheter", - "currentPermissions": "Aktuella behörigheter", - "owner": "Ägare", - "group": "Grupp", - "others": "Andra", - "read": "Läsa", - "write": "Skriv", - "execute": "Utför", - "permissionsChangedSuccessfully": "Behörigheterna har ändrats", - "failedToChangePermissions": "Det gick inte att ändra behörigheter", - "name": "Namn", - "sortByName": "Namn", - "sortByDate": "Ändrad datum", - "sortBySize": "Storlek", - "ascending": "Stigande", - "descending": "Fallande", - "root": "Rot", - "new": "Ny", - "sortBy": "Sortera efter", - "items": "Objekt", - "selected": "Vald", - "editor": "Redigerare", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", "octal": "Octal", - "storage": "Lagring", - "disk": "Diskett", - "used": "Använt", - "of": "av", - "toggleSidebar": "Växla sidofält", - "cannotLoadPdf": "Kan inte ladda PDF", - "pdfLoadError": "Det gick inte att ladda denna PDF-fil.", - "loadingPdf": "Laddar PDF...", - "loadingPage": "Laddar sida..." + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Kopiera till värd…", - "moveToHost": "Flytta till värd…", - "copyItemsToHost": "Kopiera {{count}} objekt till…", - "moveItemsToHost": "Flytta {{count}} objekt till…", - "noHostsConnected": "Inga andra filhanterarvärdar tillgängliga.", - "noHostsConnectedHint": "Lägg till en annan SSH-värd med filhanteraren aktiverad i värdhanteraren.", - "selectDestinationHost": "Välj destinationsvärd", - "destinationPath": "Destinationsväg", - "recentDestinations": "Senaste destinationerna", - "collapseRecentDestinations": "Komprimera senaste destinationer", - "expandRecentDestinations": "Expandera senaste destinationer", - "browseFolders": "Bläddra bland målmappar", - "browseDestination": "Bläddra eller ange sökväg", - "confirmCopy": "Kopiera", - "confirmMove": "Flytta", - "transferring": "Överför…", - "compressing": "Komprimering…", - "extracting": "Extraherar…", - "transferringItems": "Överför {{current}} av {{total}} objekt…", - "transferSuccess": "Överföringen är klar", - "transferError": "Överföringen misslyckades", - "transferPartial": "Överföringen slutförd med {{count}} fel", - "transferPartialHint": "Kunde inte överföra: {{paths}}", - "itemsSummary": "{{count}} objekt", - "destMustBeDirectory": "Destinationen måste vara en katalog för överföringar av flera objekt", - "selectThisFolder": "Välj den här mappen", - "browsePathWillBeCreated": "Den här mappen finns inte ännu. Den kommer att skapas när överföringen startar.", - "browsePathError": "Kunde inte öppna den här sökvägen på målvärden.", - "goUp": "Gå upp", - "copyFolderToHost": "Kopiera mapp till värden…", - "moveFolderToHost": "Flytta mappen till värden…", - "hostReady": "Redo", - "hostConnecting": "Ansluter…", - "hostDisconnected": "Inte ansluten", - "hostAuthRequired": "Autentisering krävs — öppna filhanteraren på den här värden först", - "hostConnectionFailed": "Anslutningen misslyckades", - "metricsTitle": "Överföringstider", - "metricsPrepare": "Förbered destination: {{duration}}", - "metricsCompress": "Komprimera på källan: {{duration}}", - "metricsHopSourceRead": "Källa → server: {{throughput}}", - "metricsHopDestSftpWrite": "Server → destination (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Server → destination (lokal): {{throughput}}", - "metricsTransfer": "Från slut till slut: {{throughput}} ({{duration}})", - "metricsExtract": "Extrahera på destination: {{duration}}", - "metricsSourceDelete": "Ta bort från källan: {{duration}}", - "metricsTotal": "Totalt: {{duration}}", - "progressCompressing": "Komprimering på källvärd…", - "progressExtracting": "Extraherar på destination…", - "progressTransferring": "Överför data…", - "progressReconnecting": "Återansluter…", - "parallelSegmentsLabel": "Parallella överföringsfält", - "parallelSegmentsOption": "{{count}} körfält", - "parallelSegmentsHint": "Stora filer delas upp i bitar på 256 MB. Flera filer använder separata anslutningar (som att starta flera överföringar) för högre total dataflöde.", - "progressTotalSpeed": "{{speed}} totalt ({{lanes}} körfält)", - "progressTransferringItems": "Överföra filer ({{current}} av {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} filer", - "sourceNotDeletedPartial": "Källfiler behållna (delvis överföring)", - "jumpHostLimitation": "Båda värdarna måste vara nåbara från Termix-servern. Direkt routing mellan värdar stöds inte.", - "cancel": "Avboka", - "methodLabel": "Överföringsmetod", - "methodAuto": "Bil", - "methodTar": "Tar-arkiv", - "methodItemSftp": "SFTP per fil", - "methodAutoHint": "Väljer tar eller SFTP per fil baserat på filantal, storlek och komprimerbarhet. Enstaka filer använder alltid strömmande SFTP.", - "methodTarHint": "Komprimera vid källan, överför ett arkiv, extrahera vid destinationen. Kräver tar på båda Unix-värdarna.", - "methodItemSftpHint": "Överför varje fil individuellt via SFTP. Fungerar på alla värdar inklusive Windows.", - "methodPreviewLoading": "Beräkning av överföringsmetod…", - "methodPreviewError": "Kunde inte förhandsgranska överföringsmetoden. Servern kommer fortfarande att välja en metod när du startar.", - "methodPreviewWillUseTar": "Kommer att använda: Tar-arkiv", - "methodPreviewWillUseItemSftp": "Kommer att använda: SFTP per fil", - "methodPreviewScanSummary": "{{fileCount}} filer, {{totalSize}} totalt (skannat på källvärd).", - "methodItemSftpLimitation": "Varje fil använder samma SFTP-ström som en kopia av en enda fil, en efter en. Förloppet kombineras över alla filer, så stapeln rör sig långsamt vid stora filer.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Du valde SFTP per fil.", - "user_tar": "Du valde tar-arkivet.", - "tar_unavailable": "Tar är inte tillgängligt på en eller båda värdarna — SFTP per fil kommer att användas istället.", - "windows_host": "En Windows-värd är inblandad — tar används inte.", - "auto_multi_large": "Auto: flera filer inklusive en stor fil ({{largestSize}}) med komprimerbar data — tar-paket samlas i en överföring.", - "auto_single_large_in_archive": "Auto: en stor fil ({{largestSize}}) i denna uppsättning — SFTP per fil.", - "auto_many_incompressible": "Auto: mestadels inkomprimerbar data — SFTP per fil.", - "auto_many_files": "Auto: många filer ({{fileCount}}) — tar minskar overhead per fil.", - "auto_default": "Auto: SFTP per fil för den här uppsättningen." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Avboka", - "progressCancelling": "Avbryter…", - "progressStalled": "Stannade", - "resumedHint": "Återansluten till en aktiv överföring som startade i ett annat fönster.", - "transferCancelled": "Överföringen avbruten", - "transferCancelledCopyHint": "Delvisa filer kan finnas kvar på destinationen.", - "transferCancelledMoveHint": "Delvisa filer kan finnas kvar på destinationen. Källfilerna togs inte bort.", - "cleanupDestFiles": "Städa upp destinationen", - "cleanupDestFilesSuccess": "Tog bort delvisa filer från destinationen", - "cleanupDestFilesPartial": "Vissa delfiler kunde inte tas bort", - "cleanupDestFilesNothing": "Inget att städa upp på destinationen", - "cleanupDestFilesError": "Rensningen misslyckades", - "retryTransfer": "Försöka igen", - "retryTransferError": "Återförsök misslyckades", - "transferFailedRetryHint": "Delvis data sparades på destinationen. Försöket återupptas när anslutningen är tillbaka." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Inga SSH-tunnlar", - "createFirstTunnelMessage": "Du har inte skapat några SSH-tunnlar än. Konfigurera tunnelanslutningar i värdhanteraren för att komma igång.", - "connected": "Ansluten", - "disconnected": "Frånkopplad", - "connecting": "Ansluter...", - "error": "Fel", - "canceling": "Avbrytar...", - "connect": "Anslut", - "disconnect": "Koppla från", - "cancel": "Avbryt", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", "port": "Port", - "localPort": "Lokal port", - "remotePort": "Fjärr port", - "currentHostPort": "Nuvarande värdport", - "endpointPort": "Slutpunkt port", - "bindIp": "Lokal IP", - "endpointSshConfig": "Slutpunkt SSH-konfiguration", - "endpointSshHost": "Slutpunkt SSH värd", - "endpointSshHostPlaceholder": "Välj en konfigurerad värd", - "endpointSshHostRequired": "Välj en slutpunkt SSH-värd för varje klienttunnel.", - "attempt": "Försök {{current}} av {{max}}", - "nextRetryIn": "Nästa försök igen om {{seconds}} sekunder", - "clientTunnels": "Klienttunnlar", - "clientTunnel": "Klienttunnel", - "addClientTunnel": "Lägg till klienttunnel", - "noClientTunnels": "Inga klienttunnlar konfigurerade på detta skrivbord.", - "tunnelName": "Tunnel namn", - "remoteHost": "Fjärrkontroll värd", - "autoStart": "Starta automatiskt", - "clientAutoStartDesc": "Startar när denna klient öppnas och förblir ansluten.", - "clientManualStartDesc": "Använd Start och Stop från denna rad. Termix kommer inte att öppna den automatiskt.", - "clientRemoteServerNote": "Fjärrvidarebefordran kan kräva AllowTcpForwarding och GatewayPorts på endpoint SSH-servern. Fjärrporten stängs när detta skrivbord kopplas från.", - "clientTunnelStarted": "Klienttunneln startad", - "clientTunnelStopped": "Klienttunneln stoppad", - "tunnelTestSucceeded": "Tunneltestet lyckades", - "tunnelTestFailed": "Tunneltestet misslyckades", - "localSaved": "Klienttunnlar sparade", - "localSaveError": "Det gick inte att spara lokala klienttunnlar", - "invalidBindIp": "Lokala IP måste vara en giltig IPv4-adress.", - "invalidLocalTargetIp": "Lokalt mål IP måste vara en giltig IPv4-adress.", - "invalidLocalPort": "Lokal hamn måste vara mellan 1 och 65535.", - "invalidRemotePort": "Fjärrporten måste vara mellan 1 och 65535.", - "invalidLocalTargetPort": "Lokal målport måste vara mellan 1 och 65535.", - "invalidEndpointPort": "Endpoint port måste vara mellan 1 och 65535.", - "duplicateAutoStartBind": "Endast en automatisk start klienttunnel kan använda {{bind}}.", - "manualControlError": "Det gick inte att uppdatera tunnelns tillstånd.", - "active": "Aktiv", - "start": "Starta", - "stop": "Stoppa", - "test": "Testa", - "type": "Typ av tunnel", - "typeLocal": "Lokal (-L)", - "typeRemote": "Fjärrkontroll (-R)", - "typeDynamic": "Dynamisk (-D)", - "typeServerLocalDesc": "Nuvarande värd till slutpunkt.", - "typeServerRemoteDesc": "Slutpunkt tillbaka till nuvarande värd.", - "typeClientLocalDesc": "Lokal dator till slutpunkt.", - "typeClientRemoteDesc": "Slutpunkt tillbaka till lokal dator.", - "typeClientDynamicDesc": "SOCKS på lokal dator.", - "typeDynamicDesc": "Vidarebefordra SOCKS5 CONNECT trafik genom SSH", - "forwardDescriptionServerLocal": "Nuvarande värd {{sourcePort}} → slutpunkt {{endpointPort}}.", - "forwardDescriptionServerRemote": "Slutpunkt {{endpointPort}} → nuvarande värd {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS på nuvarande värd {{sourcePort}}.", - "forwardDescriptionClientLocal": "Lokalt {{sourcePort}} → remote {{endpointPort}}.", - "forwardDescriptionClientRemote": "Fjärr {{sourcePort}} → lokal {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS på lokal port {{sourcePort}}.", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", - "autoNameClientLocal": "Lokalt {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → lokal {{localPort}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", "route": "Route:", - "lastStarted": "Senast startad", - "lastTested": "Senast testad", - "lastError": "Senaste fel", - "maxRetries": "Max antal försök", - "maxRetriesDescription": "Maximalt antal försök att försöka igen.", - "retryInterval": "Försök igen intervall (sekunder)", - "retryIntervalDescription": "Dags att vänta mellan försök igen.", - "local": "Lokal", - "remote": "Fjärrkontroll", - "destination": "Mål", - "host": "Värd", - "mode": "Läge", - "noHostSelected": "Ingen värd vald", - "working": "Arbetar..." + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Minne", - "disk": "Diskett", - "network": "Nätverk", - "uptime": "Drifttid", - "processes": "Processer", - "available": "Tillgänglig", - "free": "Gratis", - "connecting": "Ansluter...", - "connectionFailed": "Det gick inte att ansluta till servern", - "naCpus": "N/A CPU(er)", - "cpuCores_one": "{{count}} Kärna", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU användning", - "memoryUsage": "Minnesanvändning", - "diskUsage": "Diskanvändning", - "failedToFetchHostConfig": "Det gick inte att hämta värdkonfiguration", - "serverOffline": "Servern offline", - "cannotFetchMetrics": "Kan inte hämta mätvärden från offline-server", - "totpFailed": "TOTP-verifiering misslyckades", - "noneAuthNotSupported": "Serverstatistik stöder inte autentiseringstypen \"ingen\".", - "load": "Ladda", - "systemInfo": "Systeminformation", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Operativsystem", - "kernel": "Kärna", - "seconds": "sekunder", - "networkInterfaces": "Nätverksgränssnitt", - "noInterfacesFound": "Inga nätverksgränssnitt hittades", - "noProcessesFound": "Inga processer hittades", - "loginStats": "SSH inloggningsstatistik", - "noRecentLoginData": "Inga senaste inloggningsdata", - "executingQuickAction": "Kör {{name}}...", - "quickActionSuccess": "{{name}} slutförd", - "quickActionFailed": "{{name}} misslyckades", - "quickActionError": "Det gick inte att köra {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Lyssnar på portar", + "title": "Listening Ports", "protocol": "Protocol", "port": "Port", - "address": "Adress", - "process": "Bearbeta", - "noData": "Ingen lyssnande portdata" + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Brandvägg", - "inactive": "Inaktiv", + "title": "Firewall", + "inactive": "Inactive", "policy": "Policy", - "rules": "regler", - "noData": "Inga brandväggsdata tillgängliga", - "action": "Åtgärd", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", "port": "Port", - "source": "Källa", - "anywhere": "Någonstans" + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Ladda genomsnittet", - "swap": "Byt", - "architecture": "Arkitektur", - "refresh": "Uppdatera", - "retry": "Försök igen" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Självhostad SSH och fjärrskrivbordshantering", - "loginTitle": "Logga in till Termix", - "registerTitle": "Skapa konto", - "forgotPassword": "Glömt lösenordet?", - "rememberMe": "Kom ihåg enheten i 30 dagar (inkluderar TOTP)", - "noAccount": "Har du inget konto?", - "hasAccount": "Har du redan ett konto?", - "twoFactorAuth": "Tvåfaktorsautentisering", - "enterCode": "Ange verifieringskod", - "backupCode": "Eller använd säkerhetskod", - "verifyCode": "Verifiera kod", - "redirectingToApp": "Omdirigerar till app...", - "sshAuthenticationRequired": "SSH-autentisering krävs", - "sshNoKeyboardInteractive": "Tangentbord-Interaktiv Autentisering ej tillgänglig", - "sshAuthenticationFailed": "Autentisering misslyckades", - "sshAuthenticationTimeout": "Tidsgräns för autentisering", - "sshNoKeyboardInteractiveDescription": "Servern stöder inte tangentbords-interaktiv autentisering. Ange ditt lösenord eller SSH-nyckel.", - "sshAuthFailedDescription": "De angivna autentiseringsuppgifterna var felaktiga. Försök igen med giltiga uppgifter.", - "sshTimeoutDescription": "Autentiseringsförsöket gick ut. Försök igen.", - "sshProvideCredentialsDescription": "Ange dina SSH-uppgifter för att ansluta till den här servern.", - "sshPasswordDescription": "Ange lösenordet för denna SSH-anslutning.", - "sshKeyPasswordDescription": "Om din SSH-nyckel är krypterad, ange lösenfrasen här.", - "passphraseRequired": "Lösenfras krävs", - "passphraseRequiredDescription": "SSH-nyckeln är krypterad. Ange lösenfrasen för att låsa upp den.", - "back": "Tillbaka", - "firstUser": "Första användaren", - "firstUserMessage": "Du är den första användaren och kommer att göras en administratör. Du kan se administratörsinställningarna i sidolisten för användarens rullgardinsmeny. Om du tror att detta är ett misstag, kontrollera docker loggar, eller skapa en GitHub fråga.", - "external": "Extern", - "loginWithExternal": "Logga in med extern leverantör", - "loginWithExternalDesc": "Logga in med din konfigurerade externa identitetsleverantör", - "externalNotSupportedInElectron": "Extern autentisering stöds inte i appen Electron ännu. Använd webbversionen för OIDC-inloggning.", - "resetPasswordButton": "Återställ lösenord", - "sendResetCode": "Skicka återställningskod", - "resetCodeDesc": "Ange ditt användarnamn för att få en återställningskod för lösenord. Koden kommer att loggas in i behållarens loggar.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Verifiera kod", - "enterResetCode": "Ange den 6-siffriga koden från behållaren loggar för användare:", - "newPassword": "Nytt lösenord", - "confirmNewPassword": "Bekräfta lösenord", - "enterNewPassword": "Ange ditt nya lösenord för användare:", - "signUp": "Registrera dig", - "desktopApp": "Skrivbord app", - "loggingInToDesktopApp": "Loggar in på skrivbordsappen", - "loadingServer": "Laddar servern...", - "dataLossWarning": "Återställa ditt lösenord på detta sätt kommer att ta bort alla dina sparade SSH-värdar, inloggningsuppgifter och andra krypterade data. Denna åtgärd kan inte ångras. Använd endast detta om du har glömt ditt lösenord och inte är inloggad.", - "authenticationDisabled": "Autentisering inaktiverad", - "authenticationDisabledDesc": "Alla autentiseringsmetoder är för närvarande inaktiverade. Kontakta din administratör.", - "attemptsRemaining": "{{count}} försök återstår" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Verifiera SSH-värdnyckel", - "keyChangedWarning": "SSH Värdnyckel ändrad", - "firstConnectionTitle": "Första gången du ansluter till denna värd", - "firstConnectionDescription": "Autenticiteten hos denna värd kan inte fastställas. Kontrollera att fingeravtrycket matchar det du förväntar dig.", - "keyChangedDescription": "Värdnyckeln för denna server har ändrats sedan din senaste anslutning. Detta kan indikera ett säkerhetsproblem.", - "previousKey": "Föregående nyckel", - "newFingerprint": "Nytt fingeravtryck", - "fingerprint": "Fingeravtryck", - "verifyInstructions": "Om du litar på detta värd, klicka på Acceptera för att fortsätta och spara detta fingeravtryck för framtida anslutningar.", - "securityWarning": "Varning för säkerhet", - "acceptAndContinue": "Godkänn och fortsätt", - "acceptNewKey": "Acceptera ny nyckel och fortsätt" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Kunde inte ansluta till databasen", - "unknownError": "Okänt fel", - "loginFailed": "Inloggning misslyckades", - "failedPasswordReset": "Det gick inte att initiera återställning av lösenord", - "failedVerifyCode": "Det gick inte att verifiera återställningskoden", - "failedCompleteReset": "Det gick inte att slutföra lösenordsåterställningen", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Det gick inte att starta OIDC-inloggning", - "silentSigninOidcUnavailable": "Tyst inloggning begärdes, men OIDC-inloggning är inte tillgänglig.", - "failedUserInfo": "Det gick inte att hämta användarinformation efter inloggningen", - "oidcAuthFailed": "OIDC-autentisering misslyckades", - "invalidAuthUrl": "Ogiltig auktoriserings-URL mottagen från backend", - "requiredField": "Detta fält är tvingande", - "minLength": "Minsta längd är {{min}}", - "passwordMismatch": "Lösenorden matchar inte", - "passwordLoginDisabled": "Användarnamn/lösenord inloggning är för närvarande inaktiverad", - "sessionExpired": "Sessionen löpte ut - logga in igen", - "totpRateLimited": "Betygsbegränsning: För många TOTP-verifieringsförsök. Försök igen senare.", - "totpRateLimitedWithTime": "Betygsbegränsad: För många TOTP-verifieringsförsök. Vänligen vänta {{time}} sekunder innan du försöker igen.", - "resetCodeRateLimited": "Betygsbegränsning: För många verifieringsförsök. Försök igen senare.", - "resetCodeRateLimitedWithTime": "Betygsbegränsning: För många verifieringsförsök. Vänligen vänta {{time}} sekunder innan du försöker igen.", - "authTokenSaveFailed": "Det gick inte att spara autentiseringstoken", - "failedToLoadServer": "Det gick inte att ladda servern" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Registrering av nytt konto är för närvarande inaktiverat av en administratör. Logga in eller kontakta en administratör.", - "userNotAllowed": "Ditt konto har inte behörighet att registrera sig. Kontakta en administratör.", - "databaseConnectionFailed": "Det gick inte att ansluta till databasservern", - "resetCodeSent": "Återställ koden skickad till Docker-loggar", - "codeVerified": "Koden har verifierats", - "passwordResetSuccess": "Lösenord återställt framgångsrikt", - "loginSuccess": "Inloggning lyckades", - "registrationSuccess": "Registreringen lyckades" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Lokala stationära tunnlar med inriktning på konfigurerade SSH-värdar.", - "c2sTunnelPresets": "Förinställningar för klienttunnel", - "c2sTunnelPresetsDesc": "Spara klientens lokala tunnellista som en namngiven server förinställning, eller ladda en förinställning tillbaka till denna klient.", - "c2sTunnelPresetsUnavailable": "Förinställningar för klienttunnlar är endast tillgängliga i klientklienten.", - "c2sPresetName": "Förinställt namn", - "c2sPresetNamePlaceholder": "Klientens förinställda namn", - "c2sPresetToLoad": "Förval att ladda", - "c2sNoPresetSelected": "Ingen förinställning vald", - "c2sNoPresets": "Inga förval sparade", - "c2sLoadPreset": "Ladda", - "c2sCurrentLocalConfig": "{{count}} lokala klienttunnel(er) konfigurerade på detta skrivbord.", - "c2sPresetSyncNote": "Förval är explicita ögonblicksbilder; inläsning av en ersätter klientens lokala klienttunnellista.", - "c2sPresetSaved": "Förinställning för klienttunneln sparad", - "c2sPresetLoaded": "Klienttunnelförinställning laddad lokalt", - "c2sPresetRenamed": "Förinställd klienttunnel bytt namn", - "c2sPresetDeleted": "Förinställning för klienttunneln borttagen", - "c2sPresetLoadError": "Det gick inte att ladda förinställningar för klienttunneln" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Språk", - "keyPassword": "nyckel lösenord", - "pastePrivateKey": "Klistra in din privata nyckel här...", - "localListenerHost": "127.0.0.1 (lyssna lokalt)", - "localTargetHost": "127.0.0.1 (mål på denna dator)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Ange ditt lösenord", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Instrumentpanel", - "loading": "Laddar instrumentpanelen...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Stöd", + "support": "Support", "discord": "Discord", - "serverOverview": "Serverns översikt", + "docs": "Docs", + "serverOverview": "Server Overview", "version": "Version", - "upToDate": "Upp till datum", - "updateAvailable": "Uppdatering tillgänglig", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Drifttid", - "database": "Databas", - "healthy": "Friska", - "error": "Fel", - "totalHosts": "Totalt antal värdar", - "totalTunnels": "Totalt antal tunnlar", - "totalCredentials": "Totalt antal uppgifter", - "recentActivity": "Senaste aktivitet", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Laddar senaste aktivitet...", - "noRecentActivity": "Ingen ny aktivitet", - "quickActions": "Snabba åtgärder", - "addHost": "Lägg till värd", - "addCredential": "Lägg till autentiseringsuppgifter", - "adminSettings": "Administratörsinställningar", - "userProfile": "Användarprofil", - "serverStats": "Server statistik", - "loadingServerStats": "Laddar serverstatistik...", - "noServerData": "Ingen serverdata tillgänglig", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Anpassa instrumentpanel", - "dashboardSettings": "Inställningar för instrumentpanel", - "enableDisableCards": "Aktivera/inaktivera kort", - "resetLayout": "Återställ till standard", - "serverOverviewCard": "Serverns översikt", - "recentActivityCard": "Senaste aktivitet", - "networkGraphCard": "Nätverksgraf", - "networkGraph": "Nätverksgraf", - "quickActionsCard": "Snabba åtgärder", - "serverStatsCard": "Server statistik", - "panelMain": "Huvud", - "panelSide": "Sida", - "justNow": "just nu" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABELL", - "hostsOnline": "Värdar online", - "activeTunnels": "Aktiva tunnlar", - "registerNewServer": "Registrera en ny server", - "storeSshKeysOrPasswords": "Spara SSH-nycklar eller lösenord", - "manageUsersAndRoles": "Hantera användare och roller", - "manageYourAccount": "Hantera ditt konto", - "hostStatus": "Värd Status", - "noHostsConfigured": "Inga värdar konfigurerade", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", "online": "ONLINE", - "offline": "AVBRYT", + "offline": "OFFLINE", "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Lägg till:", - "commandPalette": "Kommando palett", - "done": "Klar", - "editModeInstructions": "Dra kort för att ändra ordning · Dra kolumnavdelaren för att ändra storlek kolumner · Dra nedre kanten av ett kort för att ändra storlek på höjden · Papperskorgen för att ta bort", - "empty": "Tom", - "clear": "Rensa" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Lägg till värd", - "addGroup": "Lägg till grupp", - "addLink": "Lägg till länk", - "zoomIn": "Zooma in", - "zoomOut": "Zooma ut", - "resetView": "Återställ vy", - "selectHost": "Välj värd", - "chooseHost": "Välj en värd...", - "parentGroup": "Överordnad grupp", - "noGroup": "Ingen grupp", - "groupName": "Gruppens namn", - "color": "Färg", - "source": "Källa", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Flytta till grupp", - "selectGroup": "Välj grupp...", - "addConnection": "Lägg till anslutning", - "hostDetails": "Värduppgifter", - "removeFromGroup": "Ta bort från grupp", - "addHostHere": "Lägg till värd här", - "editGroup": "Redigera grupp", - "delete": "Radera", - "add": "Lägg till", - "create": "Skapa", - "move": "Flytta", - "connect": "Anslut", - "createGroup": "Skapa grupp", - "selectSourcePlaceholder": "Välj källa...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Ogiltig fil", - "hostAlreadyExists": "Värd finns redan i topologin", - "connectionExists": "Anslutningen finns redan", - "unknown": "Okänd", - "name": "Namn", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", "status": "Status", - "failedToAddNode": "Det gick inte att lägga till noden", - "sourceDifferentFromTarget": "Källa och mål måste vara olika", - "exportJSON": "Exportera JSON", - "importJSON": "Importera JSON", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", "terminal": "Terminal", - "fileManager": "Filhanterare", + "fileManager": "File Manager", "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Server statistik", - "noNodes": "Inga noder ännu" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker är inte aktiverat för denna värd", - "validating": "Validerar docker...", - "connecting": "Ansluter...", - "error": "Fel", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Det gick inte att ansluta till Docker", - "containerStarted": "Behållare {{name}} startad", - "failedToStartContainer": "Det gick inte att starta behållare {{name}}", - "containerStopped": "Behållare {{name}} stoppad", - "failedToStopContainer": "Det gick inte att stoppa behållare {{name}}", - "containerRestarted": "Behållare {{name}} omstartad", - "failedToRestartContainer": "Det gick inte att starta om behållare {{name}}", - "containerPaused": "Behållare {{name}} pausad", - "containerUnpaused": "Behållare {{name}} opausad", - "failedToTogglePauseContainer": "Det gick inte att växla pausstatus för behållare {{name}}", - "containerRemoved": "Behållare {{name}} borttagen", - "failedToRemoveContainer": "Det gick inte att ta bort behållare {{name}}", - "image": "Bild", - "ports": "Hamnar", - "noPorts": "Inga portar", - "start": "Starta", - "confirmRemoveContainer": "Är du säker på att du vill ta bort behållaren '{{name}}'? Åtgärden kan inte ångras.", - "runningContainerWarning": "Varning: Denna behållare körs just nu. Borttagning av den kommer att stoppa behållaren först.", - "loadingContainers": "Laddar behållare...", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", "manager": "Docker Manager", - "autoRefresh": "Uppdatera automatiskt", - "timestamps": "Tidsstämplar", - "lines": "Linjer", - "filterLogs": "Filtrera loggar...", - "refresh": "Uppdatera", - "download": "Hämta", - "clear": "Rensa", - "logsDownloaded": "Hämtningen av loggar lyckades", - "last50": "Senaste 50", - "last100": "Sista 100", - "last500": "Senaste 500", - "last1000": "Sista 1000", - "allLogs": "Alla loggar", - "noLogsMatching": "Inga loggar som matchar \"{{query}}\"", - "noLogsAvailable": "Inga loggar tillgängliga", - "noContainersFound": "Inga behållare hittades", - "noContainersFoundHint": "Inga Docker-behållare finns tillgängliga på denna värd", - "searchPlaceholder": "Sök behållare...", - "allStatuses": "Alla statusar", - "stateRunning": "Körs", - "statePaused": "Pausad", - "stateExited": "Avslutad", - "stateRestarting": "Startar om", - "noContainersMatchFilters": "Inga behållare matchar dina filter", - "noContainersMatchFiltersHint": "Försök att justera sök- eller filterkriterierna", - "failedToFetchStats": "Det gick inte att hämta containerstatistik", - "containerNotRunning": "Behållare körs inte", - "startContainerToViewStats": "Starta behållaren för att visa statistik", - "loadingStats": "Laddar statistik...", - "errorLoadingStats": "Fel vid inläsning av statistik", - "noStatsAvailable": "Ingen statistik tillgänglig", - "cpuUsage": "CPU användning", - "current": "Nuvarande", - "memoryUsage": "Minnesanvändning", - "networkIo": "Nätverk I/O", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Utdata", - "blockIo": "Blockera I/O", - "read": "Läsa", - "write": "Skriv", - "pids": "PID", - "containerInformation": "Information om behållare", - "name": "Namn", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Län", - "containerMustBeRunning": "Behållare måste köras för att komma åt konsolen", - "verificationCodePrompt": "Ange verifieringskod", - "totpVerificationFailed": "TOTP-verifiering misslyckades. Försök igen.", - "warpgateVerificationFailed": "Warpgate-autentisering misslyckades. Försök igen.", - "connectedTo": "Ansluten till {{containerName}}", - "disconnected": "Frånkopplad", - "consoleError": "Fel vid konsol", - "errorMessage": "Fel: {{message}}", - "failedToConnect": "Det gick inte att ansluta till behållare", - "console": "Konsol", - "selectShell": "Välj skal", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "aska", - "connect": "Anslut", - "disconnect": "Koppla från", - "notConnected": "Inte ansluten", - "clickToConnect": "Klicka på anslut för att starta en skal-session", - "connectingTo": "Ansluter till {{containerName}}...", - "containerNotFound": "Behållare hittades inte", - "backToList": "Tillbaka till listan", - "logs": "Loggar", - "stats": "Statistik", - "consoleTab": "Konsol", - "startContainerToAccess": "Starta behållaren för att komma åt konsolen" + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Allmänt", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Användare", - "sectionSessions": "Sessioner", - "sectionRoles": "Roller", - "sectionDatabase": "Databas", - "sectionApiKeys": "API Nycklar", - "allowRegistration": "Tillåt registrering av användare", - "allowRegistrationDesc": "Låt nya användare självregistrera sig", - "allowPasswordLogin": "Tillåt lösenordsinloggning", - "allowPasswordLoginDesc": "Användarnamn/lösenord inloggning", - "oidcAutoProvision": "Auto-tilldelning av OIDC", - "oidcAutoProvisionDesc": "Skapa automatiskt konton för OIDC-användare även när registreringen är inaktiverad", - "allowPasswordReset": "Tillåt återställning av lösenord", - "allowPasswordResetDesc": "Återställ kod via Docker loggar", - "sessionTimeout": "Timeout för sessionen", - "hours": "timmar", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", "sessionTimeoutRange": "Min 1h · Max 720h", - "monitoringDefaults": "Övervakning av standard", - "statusCheck": "Statuskontroll", - "metrics": "Meter", - "sec": "sek", - "logLevel": "Loggnivå", - "enableGuacamole": "Aktivera Guacamole", - "enableGuacamoleDesc": "RDP/VNC fjärrskrivbord", - "guacdUrl": "URL för guacd", - "oidcDescription": "Konfigurera OpenID Connect för SSO. Fält markerade * är obligatoriska.", - "oidcClientId": "Klient ID", - "oidcClientSecret": "Klienthemlighet", - "oidcAuthUrl": "URL för auktorisering", - "oidcIssuerUrl": "Utfärdarens URL", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "Token URL", - "oidcUserIdentifier": "Sökväg för användaridentifierare", - "oidcDisplayName": "Visa sökväg för namn", - "oidcScopes": "Omfattningar", - "oidcUserinfoUrl": "Åsidosätt Användarinfo URL", - "oidcAllowedUsers": "Tillåtna användare", - "oidcAllowedUsersDesc": "Ett e-postmeddelande per rad. Lämna tomt för att tillåta alla.", - "removeOidc": "Radera", - "usersCount": "{{count}} användare", - "createUser": "Skapa", - "newRole": "Ny roll", - "roleName": "Namn", - "roleDisplayName": "Visningsnamn", - "roleDescription": "Beskrivning", - "rolesCount": "{{count}} roller", - "createRole": "Skapa", - "creating": "Skapar...", - "exportDatabase": "Exportera databas", - "exportDatabaseDesc": "Ladda ner en säkerhetskopia av alla värdar, inloggningsuppgifter och inställningar", - "export": "Exportera", - "exporting": "exporterar...", - "importDatabase": "Importera databas", - "importDatabaseDesc": "Återställ från en .sqlite backupfil", - "importDatabaseSelected": "Vald: {{name}}", - "selectFile": "Välj fil", - "changeFile": "Ändra", - "import": "Importera", - "importing": "Importerar...", - "apiKeysCount": "{{count}} nycklar", - "newApiKey": "Ny API-nyckel", - "apiKeyCreatedWarning": "Nyckel skapad - kopiera den nu, den kommer inte att visas igen.", - "apiKeyName": "Namn", - "apiKeyUser": "Användare", - "apiKeySelectUser": "Välj en användar...", - "apiKeyExpiresAt": "Förfaller vid", - "createKey": "Skapa nyckel", - "apiKeyNoExpiry": "Inget utgångsdatum", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Lokal", - "adminStatusAdministrator": "Administratör", - "adminStatusRegularUser": "Vanlig användare", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", "adminBadge": "ADMIN", - "systemBadge": "SYRA", - "customBadge": "ANVÄND", - "youBadge": "DU", - "sessionsActive": "{{count}} aktiv", - "sessionActive": "Aktiv: {{time}}", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Alla", - "revokeAllSessionsSuccess": "Alla sessioner för användare återkallade", - "revokeAllSessionsFailed": "Det gick inte att återkalla sessioner", - "revokeSessionFailed": "Det gick inte att återkalla sessionen", - "addRole": "Lägg till roll", - "noCustomRoles": "Inga anpassade roller har definierats", - "removeRoleFailed": "Kunde inte ta bort roll", - "assignRoleFailed": "Det gick inte att tilldela roll", - "deleteRoleFailed": "Kunde inte ta bort roll", - "userAdminAccess": "Administratör", - "userAdminAccessDesc": "Full tillgång till alla administratörsinställningar", - "userRoles": "Roller", - "revokeAllUserSessions": "Återkalla alla sessioner", - "revokeAllUserSessionsDesc": "Tvinga återinloggning på alla enheter", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Borttagning av denna användare är permanent.", - "deleteUser": "Ta bort {{username}}", - "deleting": "Raderar...", - "deleteUserFailed": "Det gick inte att ta bort användaren", - "deleteUserSuccess": "Användare \"{{username}}\" raderad", - "deleteRoleSuccess": "Roll \"{{name}}\" raderad", - "revokeKeySuccess": "Nyckel \"{{name}}\" återkallad", - "revokeKeyFailed": "Det gick inte att återkalla nyckel", - "copiedToClipboard": "Kopierad till urklipp", - "done": "Klar", - "createUserTitle": "Skapa användare", - "createUserDesc": "Skapa ett nytt lokalt konto.", - "createUserUsername": "Användarnamn", - "createUserPassword": "Lösenord", - "createUserPasswordHint": "Minsta 6 tecken.", - "createUserEnterUsername": "Ange användarnamn", - "createUserEnterPassword": "Ange lösenord", - "createUserSubmit": "Skapa användare", - "editUserTitle": "Hantera användare: {{username}}", - "editUserDesc": "Redigera roller, administratörsstatus, sessioner och kontoinställningar.", - "editUserUsername": "Användarnamn", - "editUserAuthType": "Auth typ", - "editUserAdminStatus": "Admins status", - "editUserUserId": "Användar-ID", - "linkAccountTitle": "Länka OIDC till lösenordskonto", - "linkAccountDesc": "Slå ihop OIDC-kontot {{username}} med ett befintligt lokalt konto.", - "linkAccountWarningTitle": "Detta kommer att:", - "linkAccountEffect1": "Ta bort endast OIDC-konto", - "linkAccountEffect2": "Lägg till OIDC-inloggning till målkontot", - "linkAccountEffect3": "Tillåt både OIDC och lösenord inloggning", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Ange det lokala kontots användarnamn för att länka till", - "linkAccounts": "Länka konton", - "linkAccountSuccess": "OIDC-konto länkat till \"{{username}}\"", - "linkAccountFailed": "Misslyckades med att länka OIDC-kontot", - "linkAccountInProgress": "Länkar...", - "saving": "Sparar...", - "updateRegistrationFailed": "Det gick inte att uppdatera registreringsinställningen", - "updatePasswordLoginFailed": "Det gick inte att uppdatera lösenordsinställningen", - "updateOidcAutoProvisionFailed": "Det gick inte att uppdatera auto-provisionsinställningen för OIDC", - "updatePasswordResetFailed": "Det gick inte att uppdatera inställningen för lösenordsåterställning", - "sessionTimeoutRange2": "Sessionstiderna måste vara mellan 1 och 720 timmar", - "sessionTimeoutSaved": "Sessionens timeout sparad", - "sessionTimeoutSaveFailed": "Det gick inte att spara sessionens timeout", - "monitoringIntervalInvalid": "Ogiltiga intervallvärden", - "monitoringSaved": "Övervakningsinställningar sparade", - "monitoringSaveFailed": "Det gick inte att spara övervakningsinställningar", - "guacamoleSaved": "Guacamole-inställningar sparade", - "guacamoleSaveFailed": "Det gick inte att spara Guacamole-inställningarna", - "guacamoleUpdateFailed": "Det gick inte att uppdatera Guacamole-inställningen", - "logLevelUpdateFailed": "Det gick inte att uppdatera loggnivån", - "oidcSaved": "OIDC-konfiguration sparad", - "oidcSaveFailed": "Det gick inte att spara OIDC-konfiguration", - "oidcRemoved": "OIDC-konfiguration borttagen", - "oidcRemoveFailed": "Det gick inte att ta bort OIDC-konfiguration", - "createUserRequired": "Användarnamn och lösenord krävs", - "createUserPasswordTooShort": "Lösenordet måste vara minst 6 tecken", - "createUserSuccess": "Användare \"{{username}}\" skapad", - "createUserFailed": "Det gick inte att skapa användare", - "updateAdminStatusFailed": "Det gick inte att uppdatera administratörsstatus", - "allSessionsRevoked": "Alla sessioner återkallade", - "revokeSessionsFailed": "Det gick inte att återkalla sessioner", - "createRoleRequired": "Namn och visningsnamn krävs", - "createRoleSuccess": "Roll \"{{name}}\" skapad", - "createRoleFailed": "Det gick inte att skapa roll", - "apiKeyNameRequired": "Nyckelnamn krävs", - "apiKeyUserRequired": "Användar-ID krävs", - "apiKeyCreatedSuccess": "API-nyckel \"{{name}}\" skapad", - "apiKeyCreateFailed": "Det gick inte att skapa API-nyckel", - "exportSuccess": "Databas exporterades framgångsrikt", - "exportFailed": "Databasexport misslyckades", - "importSelectFile": "Välj en fil först", - "importCompleted": "Importen slutförd: {{total}} objekt importerade, {{skipped}} överhoppad", - "importFailed": "Importen misslyckades: {{error}}", - "importError": "Databasimport misslyckades" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Värd", - "hostPlaceholder": "192.168.1.1 eller exempel.se", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Användarnamn", - "usernamePlaceholder": "användarnamn", + "usernameLabel": "Username", + "usernamePlaceholder": "username", "authLabel": "Auth", - "passwordLabel": "Lösenord", - "passwordPlaceholder": "lösenord", - "privateKeyLabel": "Privat nyckel", - "privateKeyPlaceholder": "Klistra in privat nyckel...", - "credentialLabel": "Uppgifter", - "credentialPlaceholder": "Välj en sparad referens", - "connectToTerminal": "Anslut till terminal", - "connectToFiles": "Anslut till filer" + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Ingen terminal vald", - "noTerminalSelectedHint": "Öppna en SSH-terminalflik för att visa dess kommandohistorik", - "searchPlaceholder": "Sök historik...", - "clearAll": "Rensa alla", - "noHistoryEntries": "Inga historikposter", - "trackingDisabled": "Historikspårning är inaktiverad", - "trackingDisabledHint": "Aktivera det i dina profilinställningar för att spela in kommandon." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Nyckel inspelning", - "recordToTerminals": "Spela in till terminaler", - "selectAll": "Alla", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Inga terminalflikar öppna", - "selectTerminalsAbove": "Välj terminaler ovan", - "broadcastInputPlaceholder": "Skriv här för att sända tangenttryckningar...", - "stopRecording": "Stoppa inspelning", - "startRecording": "Starta inspelning", - "settingsTitle": "Inställningar", - "enableRightClickCopyPaste": "Aktivera högerklicka på kopiera/klistra in" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { "layoutTitle": "Layout", - "selectLayoutAbove": "Välj en layout ovan", - "selectLayoutHint": "Välj hur många rutor som ska visas", - "panesTitle": "Paneler", - "openTabsTitle": "Öppna flikar", - "dragTabsHint": "Dra flikar till rutor ovan eller använd Snabbtilldelning", - "dropHere": "Släpp här", - "emptyPane": "Tom", - "dashboard": "Instrumentpanel", - "clearSplitScreen": "Rensa delad skärm", - "quickAssign": "Snabbtilldelning", - "alreadyAssigned": "Ruta {{index}}", - "splitTab": "Dela fliken", - "addToSplit": "Lägg till i delning", - "removeFromSplit": "Ta bort från Split", - "assignToPane": "Tilldela till ruta" + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Skapa textmodulen", - "createSnippetDescription": "Skapa en ny kommandotsnippet för snabb körning", - "nameLabel": "Namn", - "namePlaceholder": "t.ex., starta om Nginx", - "descriptionLabel": "Beskrivning", - "descriptionPlaceholder": "Valfri beskrivning", - "optional": "Valfri", - "folderLabel": "Mapp", - "noFolder": "Ingen mapp (okategoriserad)", - "commandLabel": "Kommando", - "commandPlaceholder": "t.ex., sudo systemctl omstart nginx", - "cancel": "Avbryt", - "createSnippetButton": "Skapa textmodulen", - "createFolderTitle": "Skapa mapp", - "createFolderDescription": "Organisera dina textmoduler i mappar", - "folderNameLabel": "Mappens namn", - "folderNamePlaceholder": "t.ex., Systemkommandon, Docker skript", - "folderColorLabel": "Mappens färg", - "folderIconLabel": "Mappens ikon", - "previewLabel": "Förhandsgranska", - "folderNameFallback": "Mappens namn", - "createFolderButton": "Skapa mapp", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Alla", - "selectNone": "Ingen", - "noTerminalTabsOpen": "Inga terminalflikar öppna", - "searchPlaceholder": "Sök snippets...", - "newSnippet": "Ny textmodul", - "newFolder": "Ny mapp", - "run": "Kör", - "noSnippetsInFolder": "Inga textmoduler i den här mappen", - "uncategorized": "Okategoriserad", - "editSnippetTitle": "Redigera textmodulen", - "editSnippetDescription": "Uppdatera detta kommando snippet", - "saveSnippetButton": "Spara ändringar", - "createSuccess": "Textmodulen har skapats", - "createFailed": "Det gick inte att skapa snippet", - "updateSuccess": "Textmodulen har uppdaterats", - "updateFailed": "Det gick inte att uppdatera snippet", - "deleteFailed": "Det gick inte att ta bort snippet", - "folderCreateSuccess": "Mappen har skapats", - "folderCreateFailed": "Det gick inte att skapa mapp", - "editFolderTitle": "Redigera mapp", - "editFolderDescription": "Döp om eller ändra utseendet på denna mapp", - "saveFolderButton": "Spara ändringar", - "editFolder": "Redigera mapp", - "deleteFolder": "Ta bort mapp", - "folderDeleteSuccess": "Mappen \"{{name}}\" raderad", - "folderDeleteFailed": "Kunde inte ta bort mapp", - "folderEditSuccess": "Mappen har uppdaterats", - "folderEditFailed": "Det gick inte att uppdatera mappen", - "confirmRunMessage": "Kör \"{{name}}\"?", - "confirmRunButton": "Sikt", - "runSuccess": "Ran \"{{name}}\" i {{count}} terminal(er)", - "copySuccess": "Kopierade \"{{name}}\" till urklipp", - "shareTitle": "Dela textmodulen", - "shareUser": "Användare", - "shareRole": "Roll", - "selectUser": "Välj en användar...", - "selectRole": "Välj en roll...", - "shareSuccess": "Textmodulen delades framgångsrikt", - "shareFailed": "Misslyckades att dela snippet", - "revokeSuccess": "Åtkomst återkallad", - "revokeFailed": "Det gick inte att återkalla åtkomst", - "currentAccess": "Aktuell åtkomst", - "shareLoadError": "Misslyckades att ladda delningsdata", - "loading": "Laddar...", - "close": "Stäng", - "reorderFailed": "Det gick inte att spara kodavsnittets ordning" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Konto", - "sectionAppearance": "Utseende", - "sectionSecurity": "Säkerhet", - "sectionApiKeys": "API Nycklar", - "sectionC2sTunnels": "C2S tunnlar", - "usernameLabel": "Användarnamn", - "roleLabel": "Roll", - "roleAdministrator": "Administratör", - "authMethodLabel": "Auth metod", - "authMethodLocal": "Lokal", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "På", - "twoFaOff": "Av", + "twoFaOn": "On", + "twoFaOff": "Off", "versionLabel": "Version", - "deleteAccount": "Ta bort konto", - "deleteAccountDescription": "Radera ditt konto permanent", - "deleteButton": "Radera", - "deleteAccountPermanent": "Denna åtgärd är permanent och kan inte ångras.", - "deleteAccountWarning": "Alla sessioner, värdar, inloggningsuppgifter och inställningar kommer att tas bort permanent.", - "confirmPasswordDeletePlaceholder": "Ange ditt lösenord för att bekräfta", - "languageLabel": "Språk", - "themeLabel": "Tema", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Accentfärg", + "accentColorLabel": "Accent Color", "settingsTerminal": "Terminal", - "commandAutocomplete": "Kommandot Autokomplettering", - "commandAutocompleteDesc": "Visa autocomplete medan du skriver", - "historyTracking": "Spårning av historik", - "historyTrackingDesc": "Spåra terminalkommandon", - "syntaxHighlighting": "Syntax markerar", - "syntaxHighlightingDesc": "Markera terminalutgång", - "commandPalette": "Kommando palett", - "commandPaletteDesc": "Aktivera kortkommando", - "reopenTabsOnLogin": "Öppna flikar igen vid inloggning", - "reopenTabsOnLoginDesc": "Återställ dina öppna flikar när du loggar in eller uppdaterar sidan, även från en annan enhet", - "confirmTabClose": "Bekräfta flikstängning", - "confirmTabCloseDesc": "Fråga innan terminalflikar stängs", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Visa värdtaggar", - "showHostTagsDesc": "Visa taggar i värdlistan", - "hostTrayOnClick": "Klicka för att expandera värdåtgärder", - "hostTrayOnClickDesc": "Visa alltid anslutningsknappar; klicka för att expandera hanteringsalternativ istället för att hålla muspekaren över", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "Håll applist i vänster sidofält alltid expanderad istället för att expandera när du håller muspekaren över den", - "settingsSnippets": "Textmoduler", - "foldersCollapsed": "Mappar kollapsade", - "foldersCollapsedDesc": "Kollapsa mappar som standard", - "confirmExecution": "Bekräfta utförande", - "confirmExecutionDesc": "Bekräfta innan du kör textmoduler", - "settingsUpdates": "Uppdateringar", - "disableUpdateChecks": "Inaktivera uppdateringskontroller", - "disableUpdateChecksDesc": "Sluta leta efter uppdateringar", - "totpAuthenticator": "TOTP-autentiserare", - "totpEnabled": "2FA är aktiverat", - "totpDisabled": "Lägg till extra inloggningssäkerhet", - "disable": "Inaktivera", - "enable": "Aktivera", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Skanna QR-koden eller ange hemlighet i din autentiseringsapp och ange sedan den 6-siffriga koden", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Verifiera", - "changePassword": "Ändra lösenord", - "currentPasswordLabel": "Nuvarande lösenord", - "currentPasswordPlaceholder": "Nuvarande lösenord", - "newPasswordLabel": "Nytt lösenord", - "newPasswordPlaceholder": "Nytt lösenord", - "confirmPasswordLabel": "Bekräfta nytt lösenord", - "confirmPasswordPlaceholder": "Bekräfta nytt lösenord", - "updatePassword": "Uppdatera lösenord", - "createApiKeyTitle": "Skapa API-nyckel", - "createApiKeyDescription": "Skapa en ny API-nyckel för programmatisk åtkomst.", - "apiKeyNameLabel": "Namn", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "valfri", - "cancel": "Avbryt", - "createKey": "Skapa nyckel", - "apiKeyCount": "{{count}} nycklar", - "newKey": "Ny nyckel", - "noApiKeys": "Inga API-nycklar än.", - "apiKeyActive": "Aktiv", - "apiKeyUsageHint": "Inkludera din nyckel i", - "apiKeyUsageHintHeader": "rubrik.", - "apiKeyPermissionsHint": "Nycklar ärver behörigheter för att skapa användaren.", - "roleUser": "Användare", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Det gick inte att starta TOTP-installationen", - "totpEnter6Digits": "Ange en 6-siffrig kod", - "totpEnabledSuccess": "Tvåfaktorsautentisering aktiverad", - "totpInvalidCode": "Ogiltig kod, försök igen", - "totpDisableInputRequired": "Ange din TOTP-kod eller lösenord", - "totpDisabledSuccess": "Tvåfaktorsautentisering inaktiverad", - "totpDisableFailed": "Det gick inte att inaktivera 2FA", - "totpDisableTitle": "Inaktivera 2FA", - "totpDisablePlaceholder": "Ange TOTP-kod eller lösenord", - "totpDisableConfirm": "Inaktivera 2FA", - "totpContinueVerify": "Fortsätt att verifiera", - "totpVerifyTitle": "Verifiera kod", - "totpBackupTitle": "Säkerhetskopiera koder", - "totpDownloadBackup": "Ladda ned säkerhetskopieringskoder", - "done": "Klar", - "secretCopied": "Hemligheten har kopierats till urklipp", - "apiKeyNameRequired": "Nyckelnamn krävs", - "apiKeyCreated": "API-nyckel \"{{name}}\" skapad", - "apiKeyCreateFailed": "Det gick inte att skapa API-nyckel", - "apiKeyUser": "Användare", - "apiKeyExpires": "Förfaller", - "apiKeyRevoked": "API-nyckel \"{{name}}\" återkallad", - "apiKeyRevokeFailed": "Det gick inte att återkalla API-nyckel", - "passwordFieldsRequired": "Nuvarande och nya lösenord krävs", - "passwordMismatch": "Lösenorden matchar inte", - "passwordTooShort": "Lösenordet måste vara minst 6 tecken", - "passwordUpdated": "Lösenordet har uppdaterats", - "passwordUpdateFailed": "Det gick inte att uppdatera lösenordet", - "deletePasswordRequired": "Lösenord krävs för att ta bort ditt konto", - "deleteFailed": "Kunde inte ta bort konto", - "deleting": "Raderar...", - "colorPickerTooltip": "Öppna färgväljaren", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", "themeSystem": "System", - "themeLight": "Ljus", - "themeDark": "Mörk", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Solariserad", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "En mörk", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/th_TH.json b/src/ui/locales/translated/th_TH.json index 33551e1e..9bc5af32 100644 --- a/src/ui/locales/translated/th_TH.json +++ b/src/ui/locales/translated/th_TH.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "โฟลเดอร์", - "folder": "โฟลเดอร์", - "password": "รหัสผ่าน", - "key": "สำคัญ", - "sshPrivateKey": "คีย์ส่วนตัว SSH", - "upload": "อัปโหลด", - "keyPassword": "รหัสผ่าน", - "sshKey": "คีย์ SSH", - "uploadPrivateKeyFile": "อัปโหลดไฟล์คีย์ส่วนตัว", - "searchCredentials": "ค้นหาข้อมูลประจำตัว...", - "addCredential": "เพิ่มข้อมูลรับรอง", - "caCertificate": "ใบรับรอง CA (-cert.pub)", - "caCertificateDescription": "ขั้นตอนเสริม: อัปโหลดหรือวางไฟล์ใบรับรองที่ลงนามโดย CA (เช่น id_ed25519-cert.pub) จำเป็นต้องใช้เมื่อเซิร์ฟเวอร์ SSH ของคุณใช้การตรวจสอบสิทธิ์แบบใช้ใบรับรอง", - "uploadCertFile": "อัปโหลดไฟล์ -cert.pub", - "clearCert": "ชัดเจน", - "certLoaded": "โหลดใบรับรองแล้ว", - "certPublicKeyLabel": "ใบรับรอง CA", - "certTypeLabel": "ประเภทใบรับรอง", - "pasteOrUploadCert": "วางหรืออัปโหลดไฟล์ใบรับรอง -cert.pub...", - "hasCaCert": "มีใบรับรอง CA", - "noCaCert": "ไม่มีใบรับรอง CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "ไม่สามารถโหลดการแจ้งเตือนได้", - "failedToDismissAlert": "ไม่สามารถปิดการแจ้งเตือนได้" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "การกำหนดค่าเซิร์ฟเวอร์", - "description": "กำหนดค่า URL ของเซิร์ฟเวอร์ Termix เพื่อเชื่อมต่อกับบริการแบ็กเอนด์ของคุณ", - "serverUrl": "URL ของเซิร์ฟเวอร์", - "enterServerUrl": "โปรดป้อน URL ของเซิร์ฟเวอร์", - "saveFailed": "ไม่สามารถบันทึกการตั้งค่าได้", - "saveError": "เกิดข้อผิดพลาดในการบันทึกการตั้งค่า", - "saving": "ประหยัด...", - "saveConfig": "บันทึกการตั้งค่า", - "helpText": "ป้อน URL ที่เซิร์ฟเวอร์ Termix ของคุณกำลังทำงานอยู่ (เช่น http://localhost:30001 หรือ https://your-server.com)", - "changeServer": "เปลี่ยนเซิร์ฟเวอร์", - "mustIncludeProtocol": "URL ของเซิร์ฟเวอร์ต้องขึ้นต้นด้วย http:// หรือ https://", - "allowInvalidCertificate": "อนุญาตให้ใช้ใบรับรองที่ไม่ถูกต้อง", - "allowInvalidCertificateDesc": "ใช้ได้เฉพาะกับเซิร์ฟเวอร์ที่ดูแลเองที่เชื่อถือได้ ซึ่งมีใบรับรองแบบลงนามเองหรือใบรับรองตามที่อยู่ IP เท่านั้น", - "useEmbedded": "ใช้เซิร์ฟเวอร์ภายในเครื่อง", - "embeddedDesc": "เรียกใช้งาน Termix ด้วยเซิร์ฟเวอร์ภายในเครื่อง (ไม่จำเป็นต้องใช้เซิร์ฟเวอร์ระยะไกล)", - "embeddedConnecting": "กำลังเชื่อมต่อกับเซิร์ฟเวอร์ภายใน...", - "embeddedNotReady": "เซิร์ฟเวอร์ในพื้นที่ยังไม่พร้อมใช้งาน โปรดรอสักครู่แล้วลองใหม่อีกครั้ง", - "localServer": "เซิร์ฟเวอร์ภายใน" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "ข้อผิดพลาดในการตรวจสอบเวอร์ชัน", - "checkFailed": "ไม่สามารถตรวจสอบการอัปเดตได้", - "upToDate": "แอปได้รับการอัปเดตแล้ว", - "currentVersion": "คุณกำลังใช้งานเวอร์ชัน {{version}}", - "updateAvailable": "มีการอัปเดตแล้ว", - "newVersionAvailable": "มีเวอร์ชันใหม่ให้ใช้งานแล้ว! คุณกำลังใช้งาน {{current}}อยู่ แต่มี {{latest}} ให้ใช้งานแล้ว", - "betaVersion": "เวอร์ชันเบต้า", - "betaVersionDesc": "คุณกำลังใช้งาน {{current}}ซึ่งใหม่กว่าเวอร์ชันเสถียรล่าสุด {{latest}}", - "releasedOn": "เผยแพร่เมื่อ {{date}}", - "downloadUpdate": "ดาวน์โหลดการอัปเดต", - "checking": "กำลังตรวจสอบการอัปเดต...", - "checkUpdates": "ตรวจสอบการอัปเดต", - "checkingUpdates": "กำลังตรวจสอบการอัปเดต...", - "updateRequired": "ต้องอัปเดตข้อมูล" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "ปิด", - "minimize": "ลดขนาด", - "online": "ออนไลน์", - "offline": "ออฟไลน์", - "continue": "ดำเนินการต่อ", - "maintenance": "การซ่อมบำรุง", - "degraded": "เสื่อมสภาพ", - "error": "ข้อผิดพลาด", - "warning": "คำเตือน", - "unsavedChanges": "การเปลี่ยนแปลงที่ยังไม่ได้บันทึก", - "dismiss": "อนุญาตให้ออกไป", - "loading": "กำลังโหลด...", - "optional": "ไม่จำเป็น", - "connect": "เชื่อมต่อ", - "copied": "คัดลอกแล้ว", - "connecting": "กำลังเชื่อมต่อ...", - "updateAvailable": "มีการอัปเดตแล้ว", - "appName": "เทอร์มิกซ์", - "openInNewTab": "เปิดในแท็บใหม่", - "noReleases": "ไม่มีการเผยแพร่", - "updatesAndReleases": "การอัปเดตและการเผยแพร่", - "newVersionAvailable": "มีเวอร์ชันใหม่ ({{version}}) ให้ใช้งานแล้ว", - "failedToFetchUpdateInfo": "ไม่สามารถดึงข้อมูลการอัปเดตได้", - "preRelease": "ก่อนวางจำหน่าย", - "noReleasesFound": "ไม่พบข้อมูลการเผยแพร่ใดๆ", - "cancel": "ยกเลิก", - "username": "ชื่อผู้ใช้", - "login": "เข้าสู่ระบบ", - "register": "ลงทะเบียน", - "password": "รหัสผ่าน", - "confirmPassword": "ยืนยันรหัสผ่าน", - "back": "กลับ", - "save": "บันทึก", - "saving": "ประหยัด...", - "delete": "ลบ", - "rename": "เปลี่ยนชื่อ", - "edit": "แก้ไข", - "add": "เพิ่ม", - "confirm": "ยืนยัน", - "no": "เลขที่", - "or": "หรือ", - "next": "ต่อไป", - "previous": "ก่อนหน้า", - "refresh": "รีเฟรช", - "language": "ภาษา", - "checking": "กำลังตรวจสอบ...", - "checkingDatabase": "กำลังตรวจสอบการเชื่อมต่อฐานข้อมูล...", - "checkingAuthentication": "กำลังตรวจสอบการยืนยันตัวตน...", - "backendReconnected": "การเชื่อมต่อเซิร์ฟเวอร์กลับมาใช้งานได้แล้ว", - "connectionDegraded": "การเชื่อมต่อเซิร์ฟเวอร์ขาดหาย กำลังกู้คืน…", - "reload": "โหลดซ้ำ", - "remove": "ลบ", - "create": "สร้าง", - "update": "อัปเดต", - "copy": "สำเนา", - "copyFailed": "ไม่สามารถคัดลอกไปยังคลิปบอร์ดได้", - "maximize": "เพิ่มให้สูงสุด", - "restore": "คืนค่า", - "of": "ของ" + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "บ้าน", - "terminal": "เทอร์มินัล", - "docker": "ด็อกเกอร์", - "tunnels": "อุโมงค์", - "fileManager": "ตัวจัดการไฟล์", - "serverStats": "สถิติเซิร์ฟเวอร์", - "admin": "ผู้ดูแลระบบ", - "userProfile": "โปรไฟล์ผู้ใช้", - "splitScreen": "แบ่งหน้าจอ", - "confirmClose": "ปิดเซสชันที่ใช้งานอยู่นี้หรือไม่?", - "close": "ปิด", - "cancel": "ยกเลิก", - "sshManager": "ตัวจัดการ SSH", - "cannotSplitTab": "ไม่สามารถแบ่งแท็บนี้ได้", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "คัดลอกรหัสผ่าน", - "copySudoPassword": "คัดลอกรหัสผ่าน Sudo", - "passwordCopied": "คัดลอกรหัสผ่านไปยังคลิปบอร์ดแล้ว", - "noPasswordAvailable": "ไม่มีรหัสผ่านให้ใช้งาน", - "failedToCopyPassword": "ไม่สามารถคัดลอกรหัสผ่านได้", - "refreshTab": "รีเฟรชการเชื่อมต่อ", - "openFileManager": "เปิดตัวจัดการไฟล์", - "dashboard": "แดชบอร์ด", - "networkGraph": "กราฟเครือข่าย", - "quickConnect": "การเชื่อมต่อด่วน", - "sshTools": "เครื่องมือ SSH", - "history": "ประวัติศาสตร์", - "hosts": "โฮสต์", - "snippets": "เศษเสี้ยว", - "hostManager": "ผู้จัดการโฮสต์", - "credentials": "คุณสมบัติ", - "connections": "การเชื่อมต่อ", - "roleAdministrator": "ผู้ดูแลระบบ", - "roleUser": "ผู้ใช้" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "โฮสต์", - "noHosts": "ไม่มีโฮสต์ SSH", - "retry": "ลองใหม่อีกครั้ง", - "refresh": "รีเฟรช", - "optional": "ไม่จำเป็น", - "downloadSample": "ดาวน์โหลดตัวอย่าง", - "failedToDeleteHost": "ไม่สามารถลบ {{name}} ได้", - "importSkipExisting": "นำเข้า (ข้ามรายการที่มีอยู่)", - "connectionDetails": "รายละเอียดการเชื่อมต่อ", - "ssh": "เอสเอช", - "telnet": "เทลเน็ต", - "remoteDesktop": "รีโมทเดสก์ท็อป", - "port": "ท่าเรือ", - "username": "ชื่อผู้ใช้", - "folder": "โฟลเดอร์", - "tags": "แท็ก", - "pin": "เข็มหมุด", - "addHost": "เพิ่มโฮสต์", - "editHost": "แก้ไขโฮสต์", - "cloneHost": "โคลนโฮสต์", - "enableTerminal": "เปิดใช้งานเทอร์มินัล", - "enableTunnel": "เปิดใช้งานอุโมงค์", - "enableFileManager": "เปิดใช้งานตัวจัดการไฟล์", - "enableDocker": "เปิดใช้งาน Docker", - "defaultPath": "เส้นทางเริ่มต้น", - "connection": "การเชื่อมต่อ", - "upload": "อัปโหลด", - "authentication": "การตรวจสอบสิทธิ์", - "password": "รหัสผ่าน", - "key": "สำคัญ", - "credential": "ใบรับรอง", - "none": "ไม่มี", - "sshPrivateKey": "คีย์ส่วนตัว SSH", - "keyType": "ประเภทกุญแจ", - "uploadFile": "อัปโหลดไฟล์", - "tabGeneral": "ทั่วไป", - "tabSsh": "เอสเอช", - "tabRdp": "อาร์ดีพี", - "tabVnc": "วีเอ็นซี", - "tabTunnels": "อุโมงค์", - "tabDocker": "ด็อกเกอร์", - "tabFiles": "ไฟล์", - "tabStats": "สถิติ", - "tabTelnet": "เทลเน็ต", - "tabSharing": "การแบ่งปัน", - "tabAuthentication": "การตรวจสอบสิทธิ์", - "terminal": "เทอร์มินัล", - "tunnel": "อุโมงค์", - "fileManager": "ตัวจัดการไฟล์", - "serverStats": "สถิติเซิร์ฟเวอร์", - "status": "สถานะ", - "folderRenamed": "เปลี่ยนชื่อโฟลเดอร์ \"{{oldName}}\" เป็น \"{{newName}}\" สำเร็จแล้ว", - "failedToRenameFolder": "ไม่สามารถเปลี่ยนชื่อโฟลเดอร์ได้", - "movedToFolder": "ย้ายไปที่ \"{{folder}}\"", - "editHostTooltip": "แก้ไขโฮสต์", - "statusChecks": "การตรวจสอบสถานะ", - "metricsCollection": "การรวบรวมเมตริก", - "metricsInterval": "ช่วงเวลาการเก็บรวบรวมเมตริก", - "metricsIntervalDesc": "ควรเก็บรวบรวมสถิติเซิร์ฟเวอร์บ่อยแค่ไหน (5 วินาที - 1 ชั่วโมง)", - "behavior": "พฤติกรรม", - "themePreview": "ตัวอย่างธีม", - "theme": "ธีม", - "fontFamily": "ตระกูลฟอนต์", - "fontSize": "ขนาดตัวอักษร", - "letterSpacing": "ระยะห่างระหว่างตัวอักษร", - "lineHeight": "ความสูงของเส้น", - "cursorStyle": "รูปแบบเคอร์เซอร์", - "cursorBlink": "เคอร์เซอร์กระพริบ", - "scrollbackBuffer": "บัฟเฟอร์การเลื่อนกลับ", - "bellStyle": "สไตล์เบลล์", - "rightClickSelectsWord": "คลิกขวาแล้วเลือก Word", - "fastScrollModifier": "ตัวแก้ไขการเลื่อนเร็ว", - "fastScrollSensitivity": "ความไวในการเลื่อนเร็ว", - "sshAgentForwarding": "การส่งต่อเอเจนต์ SSH", - "backspaceMode": "โหมดลบ", - "startupSnippet": "ตัวอย่างโค้ดสำหรับเริ่มต้น", - "selectSnippet": "เลือกส่วนย่อย", - "forceKeyboardInteractive": "แป้นพิมพ์แบบอินเทอร์แอคทีฟของ Force", - "overrideCredentialUsername": "แทนที่ชื่อผู้ใช้ข้อมูลประจำตัว", - "overrideCredentialUsernameDesc": "ใช้ชื่อผู้ใช้ที่ระบุไว้ข้างต้นแทนชื่อผู้ใช้ของข้อมูลประจำตัว", - "jumpHostChain": "ห่วงโซ่ Jump Host", - "portKnocking": "การเคาะพอร์ต", - "addKnock": "เพิ่มพอร์ต", - "addProxyNode": "เพิ่มโหนด", - "proxyNode": "โหนดพร็อกซี", - "proxyType": "ประเภทพร็อกซี", - "quickActions": "การดำเนินการด่วน", - "sudoPasswordAutoFill": "การกรอกรหัสผ่านอัตโนมัติของ Sudo", - "sudoPassword": "รหัสผ่าน Sudo", - "keepaliveInterval": "ช่วงเวลาการรักษาการเชื่อมต่อ (มิลลิวินาที)", - "moshCommand": "กองบัญชาการ MOSH", - "environmentVariables": "ตัวแปรสภาพแวดล้อม", - "addVariable": "เพิ่มตัวแปร", - "docker": "ด็อกเกอร์", - "copyTerminalUrl": "คัดลอก URL ของเทอร์มินัล", - "copyFileManagerUrl": "คัดลอก URL ของตัวจัดการไฟล์", - "copyRemoteDesktopUrl": "คัดลอก URL ของเดสก์ท็อประยะไกล", - "failedToConnect": "ไม่สามารถเชื่อมต่อกับคอนโซลได้", - "connect": "เชื่อมต่อ", - "disconnect": "ตัดการเชื่อมต่อ", - "start": "เริ่ม", - "enableStatusCheck": "เปิดใช้งานการตรวจสอบสถานะ", - "enableMetrics": "เปิดใช้งานเมตริก", - "bulkUpdateFailed": "การอัปเดตจำนวนมากไม่สำเร็จ", - "selectAll": "เลือกทั้งหมด", - "deselectAll": "ยกเลิกการเลือกทั้งหมด", - "protocols": "โปรโตคอล", - "secureShell": "เปลือกหุ้มที่ปลอดภัย", - "virtualNetwork": "เครือข่ายเสมือน", - "unencryptedShell": "เชลล์ที่ไม่ได้เข้ารหัส", - "addressIp": "ที่อยู่ / IP", - "friendlyName": "ชื่อที่เป็นมิตร", - "folderAndAdvanced": "โฟลเดอร์และขั้นสูง", - "privateNotes": "บันทึกส่วนตัว", - "privateNotesPlaceholder": "รายละเอียดเกี่ยวกับเซิร์ฟเวอร์นี้...", - "pinToTop": "ปักหมุดไว้ด้านบน", - "pinToTopDesc": "ควรแสดงโฮสต์นี้ไว้ด้านบนสุดของรายการเสมอ", - "portKnockingSequence": "ลำดับการเคาะพอร์ต", - "addKnockBtn": "เพิ่มการเคาะ", - "noPortKnocking": "ไม่ได้ตั้งค่าการตรวจจับพอร์ต (port knocking) ไว้", - "knockPort": "น็อคพอร์ต", - "protocol": "โปรโตคอล", - "delayAfterMs": "เวลาหน่วงหลังจาก (มิลลิวินาที)", - "useSocks5Proxy": "ใช้พร็อกซี SOCKS5", - "useSocks5ProxyDesc": "การเชื่อมต่อเส้นทางผ่านพร็อกซีเซิร์ฟเวอร์", - "proxyHost": "พร็อกซีโฮสต์", - "proxyPort": "พอร์ตพร็อกซี", - "proxyUsername": "ชื่อผู้ใช้พร็อกซี", - "proxyPassword": "รหัสผ่านพร็อกซี", - "proxySingleMode": "พร็อกซีเดี่ยว", - "proxyChainMode": "พร็อกซีเชน", - "you": "คุณ", - "jumpHostChainLabel": "ห่วงโซ่ Jump Host", - "addJumpBtn": "เพิ่มการกระโดด", - "noJumpHosts": "ไม่ได้กำหนดค่า jump host ไว้", - "selectAServer": "เลือกเซิร์ฟเวอร์...", - "sshPort": "พอร์ต SSH", - "authMethod": "วิธีการตรวจสอบสิทธิ์", - "storedCredential": "ข้อมูลประจำตัวที่บันทึกไว้", - "selectACredential": "เลือกข้อมูลประจำตัว...", - "keyTypeLabel": "ประเภทกุญแจ", - "keyTypeAuto": "ตรวจจับอัตโนมัติ", - "keyPasteTab": "แปะ", - "keyUploadTab": "อัปโหลด", - "keyFileLoaded": "โหลดไฟล์คีย์แล้ว", - "keyUploadClick": "คลิกเพื่ออัปโหลดไฟล์ .pem / .key / .ppk", - "clearKey": "กุญแจล้าง", - "keySaved": "บันทึกคีย์ SSH แล้ว", - "keyReplaceNotice": "วางคีย์ใหม่ด้านล่างเพื่อแทนที่คีย์เดิม", - "keyPassphraseSaved": "บันทึกรหัสผ่านแล้ว พิมพ์เพื่อเปลี่ยน", - "replaceKey": "เปลี่ยนกุญแจ", - "forceKeyboardInteractiveLabel": "แป้นพิมพ์แบบอินเทอร์แอคทีฟของ Force", - "forceKeyboardInteractiveShortDesc": "บังคับให้ป้อนรหัสผ่านด้วยตนเองแม้ว่าจะมีกุญแจอยู่ก็ตาม", - "terminalAppearance": "ลักษณะปลายทาง", - "colorTheme": "ธีมสี", - "fontFamilyLabel": "ตระกูลฟอนต์", - "fontSizeLabel": "ขนาดตัวอักษร", - "cursorStyleLabel": "รูปแบบเคอร์เซอร์", - "letterSpacingPx": "ระยะห่างระหว่างตัวอักษร (พิกเซล)", - "lineHeightLabel": "ความสูงของเส้น", - "bellStyleLabel": "สไตล์เบลล์", - "backspaceModeLabel": "โหมดลบ", - "cursorBlinking": "เคอร์เซอร์กะพริบ", - "cursorBlinkingDesc": "เปิดใช้งานแอนิเมชั่นกระพริบสำหรับเคอร์เซอร์เทอร์มินัล", - "rightClickSelectsWordLabel": "คลิกขวา เลือก Word", - "rightClickSelectsWordShortDesc": "เลือกคำที่อยู่ใต้เคอร์เซอร์เมื่อคลิกขวา", - "behaviorAndAdvanced": "พฤติกรรมและขั้นสูง", - "scrollbackBufferLabel": "บัฟเฟอร์การเลื่อนกลับ", - "scrollbackMaxLines": "จำนวนบรรทัดสูงสุดที่เก็บไว้ในประวัติ", - "sshAgentForwardingLabel": "การส่งต่อเอเจนต์ SSH", - "sshAgentForwardingShortDesc": "ส่งคีย์ SSH ในเครื่องของคุณไปยังโฮสต์นี้", - "enableAutoMosh": "เปิดใช้งาน Auto-Mosh", - "enableAutoMoshDesc": "หากมีตัวเลือก ให้เลือกใช้ Mosh แทน SSH", - "enableAutoTmux": "เปิดใช้งาน Auto-Tmux", - "enableAutoTmuxDesc": "เปิดใช้งานหรือเชื่อมต่อกับเซสชัน tmux โดยอัตโนมัติ", - "sudoPasswordAutoFillLabel": "การกรอกรหัสผ่านอัตโนมัติของ Sudo", - "sudoPasswordAutoFillShortDesc": "ป้อนรหัสผ่าน sudo โดยอัตโนมัติเมื่อได้รับแจ้ง", - "sudoPasswordLabel": "รหัสผ่าน Sudo", - "environmentVariablesLabel": "ตัวแปรสภาพแวดล้อม", - "addVariableBtn": "เพิ่มตัวแปร", - "noEnvVars": "ไม่มีการกำหนดค่าตัวแปรสภาพแวดล้อมใดๆ", - "fastScrollModifierLabel": "ตัวแก้ไขการเลื่อนเร็ว", - "fastScrollSensitivityLabel": "ความไวในการเลื่อนเร็ว", - "moshCommandLabel": "ม็อช คอมมานโด", - "startupSnippetLabel": "ตัวอย่างโค้ดสำหรับเริ่มต้น", - "keepaliveIntervalLabel": "ช่วงเวลาการเชื่อมต่อ (วินาที)", - "maxKeepaliveMisses": "แม็กซ์ คีทอะไลฟ์ พลาด", - "tunnelSettings": "การตั้งค่าอุโมงค์", - "enableTunneling": "เปิดใช้งานการสร้างอุโมงค์", - "enableTunnelingDesc": "เปิดใช้งานฟังก์ชันอุโมงค์ SSH สำหรับโฮสต์นี้", - "serverTunnelsSection": "อุโมงค์เซิร์ฟเวอร์", - "addTunnelBtn": "เพิ่มอุโมงค์", - "noTunnelsConfigured": "ไม่มีการกำหนดค่าอุโมงค์ใดๆ", - "tunnelLabel": "อุโมงค์ {{number}}", - "tunnelType": "ประเภทอุโมงค์", - "tunnelModeLocalDesc": "ส่งต่อพอร์ตภายในเครื่องไปยังพอร์ตบนเซิร์ฟเวอร์ระยะไกล (หรือโฮสต์ที่สามารถเข้าถึงได้จากเซิร์ฟเวอร์นั้น)", - "tunnelModeRemoteDesc": "ส่งต่อพอร์ตบนเซิร์ฟเวอร์ระยะไกลกลับมายังพอร์ตภายในเครื่องของคุณ", - "tunnelModeDynamicDesc": "สร้างพร็อกซี SOCKS5 บนพอร์ตภายในเครื่องเพื่อใช้ในการส่งต่อพอร์ตแบบไดนามิก", - "sameHost": "โฮสต์นี้ (อุโมงค์ตรง)", - "endpointHost": "โฮสต์ปลายทาง", - "endpointPort": "พอร์ตปลายทาง", - "bindHost": "โฮสต์ผูก", - "sourcePort": "พอร์ตต้นทาง", - "maxRetries": "จำนวนครั้งการลองใหม่สูงสุด", - "retryIntervalS": "ช่วงเวลาการลองใหม่ (วินาที)", - "autoStartLabel": "เริ่มอัตโนมัติ", - "autoStartDesc": "เชื่อมต่ออุโมงค์นี้โดยอัตโนมัติเมื่อโหลดโฮสต์เสร็จ", - "tunnelConnecting": "อุโมงค์เชื่อมต่อ...", - "tunnelDisconnected": "อุโมงค์ถูกตัดการเชื่อมต่อ", - "failedToConnectTunnel": "ไม่สามารถเชื่อมต่อได้", - "failedToDisconnectTunnel": "ไม่สามารถตัดการเชื่อมต่อได้", - "dockerIntegration": "การผสานรวม Docker", - "enableDockerMonitor": "เปิดใช้งาน Docker", - "enableDockerMonitorDesc": "ตรวจสอบและจัดการคอนเทนเนอร์บนโฮสต์นี้ผ่าน Docker", - "enableFileManagerMonitor": "เปิดใช้งานตัวจัดการไฟล์", - "enableFileManagerMonitorDesc": "เรียกดูและจัดการไฟล์บนโฮสต์นี้ผ่านทาง SFTP", - "defaultPathLabel": "เส้นทางเริ่มต้น", - "fileManagerPathHint": "โฟลเดอร์ที่จะเปิดเมื่อโปรแกรมจัดการไฟล์เริ่มทำงานสำหรับโฮสต์นี้", - "statusChecksLabel": "การตรวจสอบสถานะ", - "enableStatusChecks": "เปิดใช้งานการตรวจสอบสถานะ", - "enableStatusChecksDesc": "ตรวจสอบความพร้อมใช้งานของโฮสต์นี้เป็นระยะโดยการ ping ไปยังโฮสต์นี้", - "useGlobalInterval": "ใช้ช่วงเวลาทั่วโลก", - "useGlobalIntervalDesc": "แทนที่ด้วยช่วงเวลาตรวจสอบสถานะทั่วทั้งเซิร์ฟเวอร์", - "checkIntervalS": "ช่วงเวลาตรวจสอบ", - "checkIntervalDesc": "เวลาเป็นวินาทีระหว่างการส่งสัญญาณเชื่อมต่อแต่ละครั้ง", - "metricsCollectionLabel": "การรวบรวมเมตริก", - "enableMetricsLabel": "เปิดใช้งานเมตริก", - "enableMetricsDesc": "รวบรวมข้อมูลการใช้งาน CPU, RAM, ดิสก์ และเครือข่ายจากโฮสต์นี้", - "useGlobalMetrics": "ใช้ช่วงเวลาทั่วโลก", - "useGlobalMetricsDesc": "แทนที่ด้วยช่วงเวลาการวัดเมตริกทั่วทั้งเซิร์ฟเวอร์", - "metricsIntervalS": "ช่วงเวลาการวัด (วินาที)", - "metricsIntervalDesc2": "จำนวนวินาทีระหว่างการบันทึกภาพเมตริก", - "visibleWidgets": "วิดเจ็ตที่มองเห็นได้", - "cpuUsageLabel": "การใช้งาน CPU", - "cpuUsageDesc": "เปอร์เซ็นต์การใช้งาน CPU, ค่าเฉลี่ยโหลด, กราฟสปาร์คไลน์", - "memoryLabel": "การใช้งานหน่วยความจำ", - "memoryDesc": "การใช้งาน RAM, สวอป, แคช", - "storageLabel": "การใช้งานดิสก์", - "storageDesc": "ปริมาณการใช้ดิสก์ต่อจุดเชื่อมต่อ", - "networkLabel": "อินเทอร์เฟซเครือข่าย", - "networkDesc": "รายการอินเทอร์เฟซและแบนด์วิดท์", - "uptimeLabel": "เวลาใช้งาน", - "uptimeDesc": "เวลาการทำงานของระบบและเวลาบูตเครื่อง", - "systemInfoLabel": "ข้อมูลระบบ", - "systemInfoDesc": "ระบบปฏิบัติการ, เคอร์เนล, ชื่อโฮสต์, สถาปัตยกรรม", - "recentLoginsLabel": "การเข้าสู่ระบบล่าสุด", - "recentLoginsDesc": "เหตุการณ์การเข้าสู่ระบบสำเร็จและล้มเหลว", - "topProcessesLabel": "กระบวนการสำคัญ", - "topProcessesDesc": "PID, เปอร์เซ็นต์ CPU, เปอร์เซ็นต์หน่วยความจำ, คำสั่ง", - "listeningPortsLabel": "พอร์ตรับฟัง", - "listeningPortsDesc": "เปิดพอร์ตด้วยกระบวนการและสถานะ", - "firewallLabel": "ไฟร์วอลล์", - "firewallDesc": "สถานะไฟร์วอลล์, AppArmor, SELinux", - "quickActionsLabel": "การดำเนินการด่วน", - "quickActionsToolbar": "การดำเนินการด่วนจะปรากฏเป็นปุ่มในแถบเครื่องมือสถิติเซิร์ฟเวอร์ เพื่อให้สามารถเรียกใช้คำสั่งได้ด้วยการคลิกเพียงครั้งเดียว", - "noQuickActions": "ยังไม่มีการดำเนินการใดๆ อย่างเร่งด่วนในขณะนี้", - "buttonLabel": "ป้ายกำกับปุ่ม", - "selectSnippetPlaceholder": "เลือกส่วนย่อย...", - "addActionBtn": "เพิ่มการดำเนินการ", - "hostSharedSuccessfully": "โฮสต์แชร์สำเร็จแล้ว", - "failedToShareHost": "ไม่สามารถแชร์โฮสต์ได้", - "accessRevoked": "สิทธิ์การเข้าถึงถูกเพิกถอน", - "failedToRevokeAccess": "ไม่สามารถเพิกถอนสิทธิ์การเข้าถึงได้", - "cancelBtn": "ยกเลิก", - "savingBtn": "ประหยัด...", - "addHostBtn": "เพิ่มโฮสต์", - "hostUpdated": "โฮสต์ได้รับการอัปเดตแล้ว", - "hostCreated": "โฮสต์สร้าง", - "failedToSave": "ไม่สามารถบันทึกโฮสต์ได้", - "credentialUpdated": "ข้อมูลประจำตัวได้รับการอัปเดตแล้ว", - "credentialCreated": "สร้างข้อมูลประจำตัวแล้ว", - "failedToSaveCredential": "ไม่สามารถบันทึกข้อมูลประจำตัวได้", - "backToHosts": "กลับไปที่หน้าโฮสต์", - "backToCredentials": "กลับไปที่ข้อมูลประจำตัว", - "pinned": "ปักหมุด", - "noHostsFound": "ไม่พบโฮสต์", - "tryDifferentTerm": "ลองใช้คำอื่นดู", - "addFirstHost": "เพิ่มโฮสต์แรกของคุณเพื่อเริ่มต้นใช้งาน", - "noCredentialsFound": "ไม่พบข้อมูลประจำตัว", - "addCredentialBtn": "เพิ่มข้อมูลรับรอง", - "updateCredentialBtn": "อัปเดตข้อมูลรับรอง", - "features": "คุณสมบัติ", - "noFolder": "(ไม่มีโฟลเดอร์)", - "deleteSelected": "ลบ", - "exitSelection": "การเลือกทางออก", - "importSkip": "นำเข้า (ข้ามรายการที่มีอยู่)", - "importOverwrite": "นำเข้า (เขียนทับ)", - "collapseBtn": "ทรุด", - "importExportBtn": "นำเข้า/ส่งออก", - "hostStatusesRefreshed": "สถานะโฮสต์ได้รับการอัปเดตแล้ว", - "failedToRefreshHosts": "ไม่สามารถรีเฟรชโฮสต์ได้", - "movedHostTo": "ย้าย {{host}} ไปที่ \"{{folder}}\"", - "failedToMoveHost": "ไม่สามารถย้ายโฮสต์ได้", - "folderRenamedTo": "เปลี่ยนชื่อโฟลเดอร์เป็น \"{{name}}\"", - "deletedFolder": "โฟลเดอร์ที่ถูกลบ \"{{name}}\"", - "failedToDeleteFolder": "ไม่สามารถลบโฟลเดอร์ได้", - "deleteAllInFolder": "ลบโฮสต์ทั้งหมดใน \"{{name}}\"? การดำเนินการนี้ไม่สามารถย้อนกลับได้", - "deletedHost": "ลบแล้ว {{name}}", - "copiedToClipboard": "คัดลอกไปยังคลิปบอร์ดแล้ว", - "terminalUrlCopied": "คัดลอก URL ของเทอร์มินัลแล้ว", - "fileManagerUrlCopied": "คัดลอก URL ของตัวจัดการไฟล์แล้ว", - "tunnelUrlCopied": "คัดลอก URL ของอุโมงค์แล้ว", - "dockerUrlCopied": "คัดลอก URL ของ Docker แล้ว", - "serverStatsUrlCopied": "คัดลอก URL สถิติเซิร์ฟเวอร์แล้ว", - "rdpUrlCopied": "คัดลอก URL ของ RDP แล้ว", - "vncUrlCopied": "คัดลอก URL ของ VNC แล้ว", - "telnetUrlCopied": "คัดลอก URL ของ Telnet แล้ว", - "remoteDesktopUrlCopied": "คัดลอก URL ของเดสก์ท็อประยะไกลแล้ว", - "expandActions": "ขยายการดำเนินการ", - "collapseActions": "การดำเนินการยุบ", - "wakeOnLanAction": "เปิดใช้งานผ่าน LAN", - "wakeOnLanSuccess": "แพ็กเก็ตเวทมนตร์ถูกส่งไปยัง {{name}}", - "wakeOnLanError": "ส่งแพ็กเก็ตเวทมนตร์ไม่สำเร็จ", - "cloneHostAction": "โคลนโฮสต์", - "copyAddress": "คัดลอกที่อยู่", - "copyLink": "คัดลอกลิงก์", - "copyTerminalUrlAction": "คัดลอก URL ของเทอร์มินัล", - "copyFileManagerUrlAction": "คัดลอก URL ของตัวจัดการไฟล์", - "copyTunnelUrlAction": "คัดลอก URL อุโมงค์", - "copyDockerUrlAction": "คัดลอก URL ของ Docker", - "copyServerStatsUrlAction": "คัดลอก URL สถิติเซิร์ฟเวอร์", - "copyRdpUrlAction": "คัดลอก URL ของ RDP", - "copyVncUrlAction": "คัดลอก URL ของ VNC", - "copyTelnetUrlAction": "คัดลอก URL ของ Telnet", - "copyRemoteDesktopUrlAction": "คัดลอก URL ของเดสก์ท็อประยะไกล", - "deleteCredentialConfirm": "ลบข้อมูลประจำตัว \"{{name}}\"?", - "deletedCredential": "ลบแล้ว {{name}}", - "deploySSHKeyTitle": "ปรับใช้คีย์ SSH", - "deployingBtn": "กำลังติดตั้ง...", - "deployBtn": "ปรับใช้", - "failedToDeployKey": "ไม่สามารถใช้งานคีย์ได้", - "deleteHostsConfirm": "ลบ {{count}} โฮสต์{{plural}}? ไม่สามารถยกเลิกได้", - "movedToRoot": "ย้ายไปยังรูทแล้ว", - "failedToMoveHosts": "ไม่สามารถย้ายโฮสต์ได้", - "enableTerminalFeature": "เปิดใช้งานเทอร์มินัล", - "disableTerminalFeature": "ปิดใช้งานเทอร์มินัล", - "enableFilesFeature": "เปิดใช้งานไฟล์", - "disableFilesFeature": "ปิดใช้งานไฟล์", - "enableTunnelsFeature": "เปิดใช้งานอุโมงค์", - "disableTunnelsFeature": "ปิดใช้งานอุโมงค์", - "enableDockerFeature": "เปิดใช้งาน Docker", - "disableDockerFeature": "ปิดใช้งาน Docker", - "addTagsPlaceholder": "เพิ่มแท็ก...", - "authDetails": "รายละเอียดการตรวจสอบสิทธิ์", - "credType": "พิมพ์", - "generateKeyPairDesc": "สร้างคู่คีย์ใหม่ โดยทั้งคีย์ส่วนตัวและคีย์สาธารณะจะถูกกรอกโดยอัตโนมัติ", - "generatingKey": "กำลังสร้าง...", - "generateLabel": "สร้าง {{label}}", - "uploadFileBtn": "อัปโหลดไฟล์", - "keyPassphraseOptional": "รหัสผ่านหลัก (ไม่บังคับ)", - "sshPublicKeyOptional": "คีย์สาธารณะ SSH (ไม่บังคับ)", - "publicKeyGenerated": "สร้างคีย์สาธารณะแล้ว", - "failedToGeneratePublicKey": "ไม่สามารถสร้างคีย์สาธารณะได้", - "publicKeyCopied": "คัดลอกคีย์สาธารณะแล้ว", - "keyPairGenerated": "สร้างคู่คีย์ {{label}} แล้ว", - "failedToGenerateKeyPair": "ไม่สามารถสร้างคู่คีย์ได้", - "searchHostsPlaceholder": "ค้นหาโฮสต์ ที่อยู่ แท็ก…", - "searchCredentialsPlaceholder": "ข้อมูลประจำตัวการค้นหา…", - "refreshBtn": "รีเฟรช", - "addTag": "เพิ่มแท็ก...", - "deleteConfirmBtn": "ลบ", - "tunnelRequirementsText": "เซิร์ฟเวอร์ SSH ต้องตั้งค่า GatewayPorts yes, AllowTcpForwarding yes และ PermitRootLogin yes ในไฟล์ /etc/ssh/sshd_config", - "deleteHostConfirm": "ลบ \"{{name}}\"?", - "enableAtLeastOneProtocol": "เปิดใช้งานโปรโตคอลอย่างน้อยหนึ่งรายการด้านบนเพื่อกำหนดค่าการตรวจสอบสิทธิ์และการตั้งค่าการเชื่อมต่อ", - "keyPassphrase": "รหัสผ่านหลัก", - "connectBtn": "เชื่อมต่อ", - "disconnectBtn": "ตัดการเชื่อมต่อ", - "basicInformation": "ข้อมูลพื้นฐาน", - "authDetailsSection": "รายละเอียดการตรวจสอบสิทธิ์", - "credTypeLabel": "พิมพ์", - "hostsTab": "โฮสต์", - "credentialsTab": "คุณสมบัติ", - "selectMultiple": "เลือกหลายรายการ", - "selectHosts": "เลือกโฮสต์", - "connectionLabel": "การเชื่อมต่อ", - "authenticationLabel": "การตรวจสอบสิทธิ์", - "generateKeyPairTitle": "สร้างคู่คีย์", - "generateKeyPairDescription": "สร้างคู่คีย์ใหม่ โดยทั้งคีย์ส่วนตัวและคีย์สาธารณะจะถูกกรอกโดยอัตโนมัติ", - "generateFromPrivateKey": "สร้างจากรหัสส่วนตัว", - "refreshBtn2": "รีเฟรช", - "exitSelectionTitle": "การเลือกทางออก", - "exportAll": "ส่งออกทั้งหมด", - "addHostBtn2": "เพิ่มโฮสต์", - "addCredentialBtn2": "เพิ่มข้อมูลรับรอง", - "checkingHostStatuses": "กำลังตรวจสอบสถานะของโฮสต์...", - "pinnedSection": "ปักหมุด", - "hostsExported": "การส่งออกโฮสต์สำเร็จแล้ว", - "exportFailed": "ไม่สามารถส่งออกโฮสต์ได้", - "sampleDownloaded": "ดาวน์โหลดไฟล์ตัวอย่างแล้ว", - "failedToDeleteCredential2": "ไม่สามารถลบข้อมูลประจำตัวได้", - "noFolderOption": "(ไม่มีโฟลเดอร์)", - "nSelected": "{{count}} เลือกแล้ว", - "featuresMenu": "คุณสมบัติ", - "moveMenu": "เคลื่อนไหว", - "cancelSelection": "ยกเลิก", - "deployDialogDesc": "ปรับใช้ {{name}} กับ authorized_keys ของโฮสต์", - "targetHostLabel": "โฮสต์เป้าหมาย", - "selectHostOption": "เลือกโฮสต์...", - "keyDeployedSuccess": "คีย์ถูกใช้งานสำเร็จแล้ว", - "failedToDeployKey2": "ไม่สามารถใช้งานคีย์ได้", - "deletedCount": "โฮสต์ที่ถูกลบ {{count}}", - "failedToDeleteCount": "ไม่สามารถลบโฮสต์ {{count}} ได้", - "duplicatedHost": "ซ้ำกัน \"{{name}}\"", - "failedToDuplicateHost": "ไม่สามารถสร้างโฮสต์ซ้ำได้", - "updatedCount": "อัปเดตโฮสต์ {{count}} แล้ว", - "friendlyNameLabel": "ชื่อที่เป็นมิตร", - "descriptionLabel": "คำอธิบาย", - "loadingHost": "กำลังโหลดโฮสต์...", - "loadingHosts": "กำลังโหลดโฮสต์...", - "loadingCredentials": "กำลังโหลดข้อมูลประจำตัว...", - "noHostsYet": "ยังไม่มีโฮสต์", - "noHostsMatchSearch": "ไม่พบโฮสต์ใดตรงกับผลการค้นหาของคุณ", - "hostNotFound": "ไม่พบโฮสต์", - "searchHosts": "ค้นหาโฮสต์...", - "sortHosts": "เรียงลำดับโฮสต์", - "sortDefault": "ลำดับเริ่มต้น", - "sortNameAsc": "ชื่อ (A → Z)", - "sortNameDesc": "ชื่อ (Z → A)", - "sortIpAsc": "ที่อยู่ IP (จากน้อยไปมาก)", - "sortIpDesc": "ที่อยู่ IP (คำอธิบาย)", - "sortOnlineFirst": "ออนไลน์ก่อน", - "sortOfflineFirst": "ออฟไลน์ก่อน", - "sortPinnedFirst": "ปักหมุดไว้ก่อน", - "filterHosts": "ตัวกรองโฮสต์", - "filterClearAll": "ล้างตัวกรอง", - "filterStatusGroup": "สถานะ", - "filterOnline": "ออนไลน์", - "filterOffline": "ออฟไลน์", - "filterPinned": "ปักหมุด", - "filterAuthGroup": "ประเภทการตรวจสอบสิทธิ์", - "filterAuthPassword": "รหัสผ่าน", - "filterAuthKey": "คีย์ SSH", - "filterAuthCredential": "ใบรับรอง", - "filterAuthNone": "ไม่มี", - "filterAuthOpkssh": "โอพีเคเอสเอช", - "filterProtocolGroup": "โปรโตคอล", - "filterProtocolSsh": "เอสเอช", - "filterProtocolRdp": "อาร์ดีพี", - "filterProtocolVnc": "วีเอ็นซี", - "filterProtocolTelnet": "เทลเน็ต", - "filterFeaturesGroup": "คุณสมบัติ", - "filterFeatureTerminal": "เทอร์มินัล", - "filterFeatureFileManager": "ตัวจัดการไฟล์", - "filterFeatureTunnel": "อุโมงค์", - "filterFeatureDocker": "ด็อกเกอร์", - "filterTagsGroup": "แท็ก", - "shareHost": "แชร์โฮสต์", - "shareHostTitle": "แชร์: {{name}}", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", + "ssh": "SSH", + "telnet": "Telnet", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", + "tabSsh": "SSH", + "tabTerminal": "Terminal", + "tabRdp": "RDP", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", + "tabTelnet": "Telnet", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", + "filterProtocolSsh": "SSH", + "filterProtocolRdp": "RDP", + "filterProtocolVnc": "VNC", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "โฮสต์นี้ต้องใช้ข้อมูลรับรองเพื่อเปิดใช้งานการแชร์ แก้ไขข้อมูลโฮสต์และกำหนดข้อมูลรับรองก่อน" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "การเชื่อมต่อ", - "authentication": "การตรวจสอบสิทธิ์", - "connectionSettings": "การตั้งค่าการเชื่อมต่อ", - "displaySettings": "การตั้งค่าการแสดงผล", - "audioSettings": "การตั้งค่าเสียง", - "rdpPerformance": "ประสิทธิภาพ RDP", - "deviceRedirection": "การเปลี่ยนเส้นทางอุปกรณ์", - "session": "การประชุม", - "gateway": "เกตเวย์", - "remoteApp": "รีโมทแอป", - "clipboard": "คลิปบอร์ด", - "sessionRecording": "การบันทึกการประชุม", - "wakeOnLan": "ปลุกเครื่องผ่าน LAN", - "vncSettings": "การตั้งค่า VNC", - "terminalSettings": "การตั้งค่าเทอร์มินัล", - "rdpPort": "พอร์ต RDP", - "username": "ชื่อผู้ใช้", - "password": "รหัสผ่าน", - "domain": "โดเมน", - "securityMode": "โหมดความปลอดภัย", - "colorDepth": "ความลึกของสี", - "width": "ความกว้าง", - "height": "ความสูง", - "dpi": "ดีพีไอ", - "resizeMethod": "วิธีการปรับขนาด", - "clientName": "ชื่อลูกค้า", - "initialProgram": "โปรแกรมเริ่มต้น", - "serverLayout": "เค้าโครงเซิร์ฟเวอร์", - "timezone": "เขตเวลา", - "gatewayHostname": "ชื่อโฮสต์เกตเวย์", - "gatewayPort": "พอร์ตเกตเวย์", - "gatewayUsername": "ชื่อผู้ใช้เกตเวย์", - "gatewayPassword": "รหัสผ่านเกตเวย์", - "gatewayDomain": "โดเมนเกตเวย์", - "remoteAppProgram": "โปรแกรม RemoteApp", - "workingDirectory": "ไดเร็กทอรีการทำงาน", - "arguments": "ข้อโต้แย้ง", - "normalizeLineEndings": "ปรับความยาวบรรทัดให้เป็นมาตรฐาน", - "recordingPath": "เส้นทางการบันทึก", - "recordingName": "ชื่อการบันทึก", - "macAddress": "ที่อยู่ MAC", - "broadcastAddress": "ที่อยู่กระจายเสียง", - "udpPort": "พอร์ต UDP", - "waitTimeS": "ระยะเวลารอคอย (วินาที)", - "driveName": "ชื่อไดรฟ์", - "drivePath": "เส้นทางขับรถ", - "ignoreCertificate": "ละเว้นใบรับรอง", - "ignoreCertificateDesc": "อนุญาตการเชื่อมต่อกับโฮสต์ที่มีใบรับรองที่ลงนามด้วยตนเอง", - "forceLossless": "บังคับการสูญเสีย", - "forceLosslessDesc": "บังคับใช้การเข้ารหัสภาพแบบไม่สูญเสียคุณภาพ (คุณภาพสูงกว่า ใช้แบนด์วิดท์มากกว่า)", - "disableAudio": "ปิดใช้งานเสียง", - "disableAudioDesc": "ปิดเสียงทั้งหมดจากการเชื่อมต่อระยะไกล", - "enableAudioInput": "เปิดใช้งานการรับเสียง (ไมโครโฟน)", - "enableAudioInputDesc": "ส่งต่อไมโครโฟนในเครื่องไปยังเซสชันระยะไกล", - "wallpaper": "วอลเปเปอร์", - "wallpaperDesc": "แสดงภาพพื้นหลังเดสก์ท็อป (การปิดใช้งานจะช่วยเพิ่มประสิทธิภาพ)", - "theming": "ธีม", - "themingDesc": "เปิดใช้งานธีมและสไตล์ภาพ", - "fontSmoothing": "การปรับความเรียบของแบบอักษร", - "fontSmoothingDesc": "เปิดใช้งานการแสดงผลฟอนต์ ClearType", - "fullWindowDrag": "ลากแบบเต็มหน้าต่าง", - "fullWindowDragDesc": "แสดงเนื้อหาในหน้าต่างขณะลาก", - "desktopComposition": "การจัดองค์ประกอบเดสก์ท็อป", - "desktopCompositionDesc": "เปิดใช้งานเอฟเฟกต์กระจก Aero", - "menuAnimations": "แอนิเมชันเมนู", - "menuAnimationsDesc": "เปิดใช้งานแอนิเมชั่นเฟดและสไลด์ของเมนู", - "disableBitmapCaching": "ปิดใช้งานการแคชภาพบิตแมป", - "disableBitmapCachingDesc": "ปิดใช้งานแคชภาพบิตแมป (อาจช่วยลดปัญหาภาพกระตุกได้)", - "disableOffscreenCaching": "ปิดใช้งานการแคชแบบออฟสกรีน", - "disableOffscreenCachingDesc": "ปิดใช้งานแคชแบบออฟไลน์", - "disableGlyphCaching": "ปิดใช้งานการแคชสัญลักษณ์", - "disableGlyphCachingDesc": "ปิดใช้งานแคชสัญลักษณ์", - "enableGfx": "เปิดใช้งาน GFX", - "enableGfxDesc": "ใช้ไปป์ไลน์กราฟิก RemoteFX", - "enablePrinting": "เปิดใช้งานการพิมพ์", - "enablePrintingDesc": "โอนย้ายเครื่องพิมพ์ในเครื่องไปยังเซสชันระยะไกล", - "enableDriveRedirection": "เปิดใช้งานการเปลี่ยนเส้นทางไดรฟ์", - "enableDriveRedirectionDesc": "แมปโฟลเดอร์ในเครื่องเป็นไดรฟ์ในเซสชันระยะไกล", - "createDrivePath": "สร้างเส้นทางไดรฟ์", - "createDrivePathDesc": "สร้างโฟลเดอร์โดยอัตโนมัติหากยังไม่มีอยู่", - "disableDownload": "ปิดใช้งานการดาวน์โหลด", - "disableDownloadDesc": "ป้องกันการดาวน์โหลดไฟล์จากการเชื่อมต่อระยะไกล", - "disableUpload": "ปิดใช้งานการอัปโหลด", - "disableUploadDesc": "ป้องกันการอัปโหลดไฟล์ไปยังเซสชันระยะไกล", - "enableTouch": "เปิดใช้งานระบบสัมผัส", - "enableTouchDesc": "เปิดใช้งานการส่งต่อข้อมูลการสัมผัส", - "consoleSession": "เซสชันคอนโซล", - "consoleSessionDesc": "เชื่อมต่อกับคอนโซล (เซสชัน 0) แทนที่จะสร้างเซสชันใหม่", - "sendWolPacket": "ส่งแพ็กเก็ต WOL", - "sendWolPacketDesc": "ส่งแพ็กเก็ตเวทมนตร์เพื่อปลุกโฮสต์นี้ก่อนเชื่อมต่อ", - "disableCopy": "ปิดใช้งานการคัดลอก", - "disableCopyDesc": "ป้องกันการคัดลอกข้อความจากเซสชันระยะไกล", - "disablePaste": "ปิดใช้งานการวาง", - "disablePasteDesc": "ป้องกันการวางข้อความลงในเซสชันระยะไกล", - "createPathIfMissing": "สร้างเส้นทางหากยังไม่มีอยู่", - "createPathIfMissingDesc": "สร้างไดเร็กทอรีสำหรับการบันทึกโดยอัตโนมัติ", - "excludeOutput": "ไม่รวมผลลัพธ์", - "excludeOutputDesc": "ห้ามบันทึกข้อมูลที่แสดงบนหน้าจอ (บันทึกเฉพาะข้อมูลเมตาเท่านั้น)", - "excludeMouse": "ไม่รวมเมาส์", - "excludeMouseDesc": "ห้ามบันทึกการเคลื่อนไหวของเมาส์", - "includeKeystrokes": "รวมการกดแป้นพิมพ์", - "includeKeystrokesDesc": "บันทึกการกดแป้นพิมพ์ดิบนอกเหนือจากข้อมูลที่แสดงผลบนหน้าจอ", - "vncPort": "พอร์ต VNC", - "vncPassword": "รหัสผ่าน VNC", - "vncUsernameOptional": "ชื่อผู้ใช้ (ไม่บังคับ)", - "vncLeaveBlank": "เว้นว่างไว้หากไม่ต้องการใช้", - "cursorMode": "โหมดเคอร์เซอร์", - "swapRedBlue": "สลับสีแดง/น้ำเงิน", - "swapRedBlueDesc": "สลับช่องสีแดงและสีน้ำเงิน (แก้ไขปัญหาเรื่องสีบางอย่าง)", - "readOnly": "อ่านอย่างเดียว", - "readOnlyDesc": "ดูหน้าจอระยะไกลโดยไม่ต้องส่งข้อมูลใดๆ", - "telnetPort": "พอร์ตเทลเน็ต", - "terminalType": "ประเภทเทอร์มินัล", - "fontName": "ชื่อแบบอักษร", - "fontSize": "ขนาดตัวอักษร", - "colorScheme": "โทนสี", - "backspaceKey": "ปุ่มลบ", - "saveHostFirst": "บันทึกข้อมูลโฮสต์ก่อน", - "sharingOptionsAfterSave": "ตัวเลือกการแชร์จะพร้อมใช้งานหลังจากบันทึกโฮสต์แล้ว", - "sharingLoadError": "ไม่สามารถโหลดข้อมูลการแชร์ได้ โปรดตรวจสอบการเชื่อมต่อของคุณแล้วลองใหม่อีกครั้ง", - "shareHostSection": "แชร์โฮสต์", - "shareWithUser": "แชร์กับผู้ใช้", - "shareWithRole": "แชร์กับบทบาท", - "selectUser": "เลือกผู้ใช้", - "selectRole": "เลือกบทบาท", - "selectUserOption": "เลือกผู้ใช้...", - "selectRoleOption": "เลือกบทบาท...", - "permissionLevel": "ระดับการอนุญาต", - "expiresInHours": "หมดอายุใน (ชั่วโมง)", - "noExpiryPlaceholder": "เว้นว่างไว้เพื่อป้องกันการหมดอายุ", - "shareBtn": "แบ่งปัน", - "currentAccess": "การเข้าถึงปัจจุบัน", - "typeHeader": "พิมพ์", - "targetHeader": "เป้า", - "permissionHeader": "การอนุญาต", - "grantedByHeader": "ได้รับอนุญาตโดย", - "expiresHeader": "หมดอายุ", - "noAccessEntries": "ยังไม่มีข้อมูลการเข้าถึงใดๆ", - "expiredLabel": "หมดอายุ", - "neverLabel": "ไม่เคย", - "revokeBtn": "ถอน", - "cancelBtn": "ยกเลิก", - "savingBtn": "ประหยัด...", - "updateHostBtn": "อัปเดตโฮสต์", - "addHostBtn": "เพิ่มโฮสต์" + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "ค้นหาโฮสต์ คำสั่ง หรือการตั้งค่า...", - "quickActions": "การดำเนินการด่วน", - "hostManager": "ผู้จัดการโฮสต์", - "hostManagerDesc": "จัดการ เพิ่ม หรือแก้ไขโฮสต์", - "addNewHost": "เพิ่มโฮสต์ใหม่", - "addNewHostDesc": "ลงทะเบียนโฮสต์ใหม่", - "adminSettings": "การตั้งค่าผู้ดูแลระบบ", - "adminSettingsDesc": "ตั้งค่าระบบและผู้ใช้", - "userProfile": "โปรไฟล์ผู้ใช้", - "userProfileDesc": "จัดการบัญชีและตั้งค่าของคุณ", - "addCredential": "เพิ่มข้อมูลรับรอง", - "addCredentialDesc": "จัดเก็บคีย์ SSH หรือรหัสผ่าน", - "recentActivity": "กิจกรรมล่าสุด", - "serversAndHosts": "เซิร์ฟเวอร์และโฮสต์", - "noHostsFound": "ไม่พบโฮสต์ที่ตรงกับ \"{{search}}\"", - "links": "ลิงก์", - "navigate": "นำทาง", - "select": "เลือก", - "toggleWith": "สลับด้วย" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "บานหน้าต่าง {{index}} - ว่างเปล่า", - "noTabAssigned": "ไม่มีการกำหนดแท็บ", - "focusedPane": "หน้าต่างที่ใช้งานอยู่" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "ไม่มีการเชื่อมต่อ", - "noConnectionsDesc": "เปิดเทอร์มินัล โปรแกรมจัดการไฟล์ หรือเดสก์ท็อประยะไกล เพื่อดูการเชื่อมต่อได้ที่นี่", - "connectedFor": "เชื่อมต่อเป็นเวลา {{duration}}", - "connected": "เชื่อมต่อแล้ว", - "disconnected": "ตัดการเชื่อมต่อ", - "closeTab": "ปิดแท็บ", - "closeConnection": "ความสัมพันธ์ใกล้ชิด", - "forgetTab": "ลืม", - "removeBackground": "ลบ", - "reconnect": "เชื่อมต่อใหม่", - "reopenTab": "เปิดใหม่", - "sectionOpen": "เปิด", - "sectionBackground": "พื้นหลัง", - "backgroundDesc": "การเชื่อมต่อจะยังคงอยู่เป็นเวลา 30 นาทีหลังจากตัดการเชื่อมต่อ และสามารถเชื่อมต่อใหม่ได้", - "persisted": "ยังคงอยู่ในพื้นหลัง", - "expiresIn": "หมดอายุใน {{duration}}", - "search": "ค้นหาการเชื่อมต่อ...", - "noSearchResults": "ไม่พบผลลัพธ์ที่ตรงกับการค้นหาของคุณ" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "กำลังเชื่อมต่อกับเซสชัน {{type}}...", - "connectionError": "ข้อผิดพลาดในการเชื่อมต่อ", - "connectionFailed": "การเชื่อมต่อล้มเหลว", - "failedToConnect": "ไม่สามารถรับโทเค็นการเชื่อมต่อได้", - "hostNotFound": "ไม่พบโฮสต์", - "noHostSelected": "ไม่ได้เลือกโฮสต์", - "reconnect": "เชื่อมต่อใหม่", - "retry": "ลองใหม่อีกครั้ง", - "guacdUnavailable": "บริการเดสก์ท็อประยะไกล (guacd) ไม่พร้อมใช้งาน โปรดตรวจสอบให้แน่ใจว่า guacd กำลังทำงาน สามารถเข้าถึงได้ และกำหนดค่าอย่างถูกต้องในการตั้งค่าผู้ดูแลระบบ", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (หน้าจอล็อก)", - "winKey": "ปุ่ม Windows", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "อัลท์", - "shift": "กะ", - "win": "ชนะ", - "stickyActive": "{{key}} (ล็อคอยู่ - คลิกเพื่อปลดล็อค)", - "stickyInactive": "{{key}} (คลิกเพื่อล็อค)", - "esc": "หนี", - "tab": "แท็บ", - "home": "บ้าน", - "end": "จบ", - "pageUp": "เลื่อนขึ้น", - "pageDown": "เลื่อนลง", - "arrowUp": "ลูกศรขึ้น", - "arrowDown": "ลูกศรลง", - "arrowLeft": "ลูกศรซ้าย", - "arrowRight": "ลูกศรขวา", - "fnToggle": "ปุ่มฟังก์ชัน", - "reconnect": "เซสชั่นเชื่อมต่อใหม่", - "collapse": "ยุบแถบเครื่องมือ", - "expand": "ขยายแถบเครื่องมือ", - "dragHandle": "ลากเพื่อจัดตำแหน่งใหม่" + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "เชื่อมต่อกับโฮสต์", - "clear": "ชัดเจน", - "paste": "แปะ", - "reconnect": "เชื่อมต่อใหม่", - "connectionLost": "การเชื่อมต่อขาดหาย", - "connected": "เชื่อมต่อแล้ว", - "clipboardWriteFailed": "ไม่สามารถคัดลอกไปยังคลิปบอร์ดได้ โปรดตรวจสอบให้แน่ใจว่าหน้าเว็บนั้นให้บริการผ่าน HTTPS หรือ localhost", - "clipboardReadFailed": "ไม่สามารถอ่านข้อมูลจากคลิปบอร์ดได้ โปรดตรวจสอบให้แน่ใจว่าได้ให้สิทธิ์การเข้าถึงคลิปบอร์ดแล้ว", - "clipboardHttpWarning": "การวางต้องใช้ HTTPS กด Ctrl+Shift+V หรือใช้งาน Termix ผ่าน HTTPS", - "unknownError": "เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ", - "websocketError": "ข้อผิดพลาดในการเชื่อมต่อ WebSocket", - "connecting": "กำลังเชื่อมต่อ...", - "noHostSelected": "ไม่ได้เลือกโฮสต์", - "reconnecting": "กำลังเชื่อมต่อใหม่... ({{attempt}}/{{max}})", - "reconnected": "เชื่อมต่อสำเร็จแล้ว", - "tmuxSessionCreated": "สร้างเซสชัน tmux แล้ว: {{name}}", - "tmuxSessionAttached": "การเชื่อมต่อเซสชัน tmux: {{name}}", - "tmuxUnavailable": "tmux ไม่ได้ติดตั้งอยู่บนโฮสต์ระยะไกล จึงใช้เชลล์มาตรฐานแทน", - "tmuxSessionPickerTitle": "เซสชัน tmux", - "tmuxSessionPickerDesc": "พบเซสชัน tmux ที่มีอยู่แล้วในโฮสต์นี้ เลือกหนึ่งเซสชันเพื่อเชื่อมต่อใหม่หรือสร้างเซสชันใหม่", - "tmuxWindows": "วินโดวส์", - "tmuxWindowCount": "{{count}} หน้าต่าง", - "tmuxAttached": "ลูกค้าที่แนบมาด้วย", - "tmuxAttachedCount": "{{count}} แนบมาด้วย", - "tmuxLastActivity": "กิจกรรมล่าสุด", - "tmuxTimeJustNow": "เมื่อสักครู่นี้เอง", - "tmuxTimeMinutes": "{{count}}นาทีที่แล้ว", - "tmuxTimeHours": "{{count}}ชั่วโมงที่แล้ว", - "tmuxTimeDays": "{{count}}วันก่อน", - "tmuxCreateNew": "เริ่มเซสชั่นใหม่", - "tmuxCopyHint": "ปรับตำแหน่งที่เลือกแล้วกด Enter เพื่อคัดลอกไปยังคลิปบอร์ด", - "tmuxDetach": "ยกเลิกการเชื่อมต่อจากเซสชัน tmux", - "tmuxDetached": "ตัดการเชื่อมต่อจากเซสชัน tmux แล้ว", - "maxReconnectAttemptsReached": "จำนวนครั้งการเชื่อมต่อใหม่สูงสุดครบแล้ว", - "closeTab": "ปิด", - "connectionTimeout": "หมดเวลาการเชื่อมต่อ", - "terminalTitle": "เทอร์มินัล - {{host}}", - "terminalWithPath": "เทอร์มินัล - {{host}}:{{path}}", - "runTitle": "กำลังวิ่ง {{command}} - {{host}}", - "totpRequired": "ต้องใช้การยืนยันตัวตนสองขั้นตอน", - "totpCodeLabel": "รหัสยืนยัน", - "totpVerify": "ตรวจสอบ", - "warpgateAuthRequired": "ต้องยืนยันตัวตนผ่านวาร์ปเกต", - "warpgateSecurityKey": "รหัสความปลอดภัย", - "warpgateAuthUrl": "URL สำหรับการตรวจสอบสิทธิ์", - "warpgateOpenBrowser": "เปิดในเบราว์เซอร์", - "warpgateContinue": "ฉันทำการยืนยันตัวตนเสร็จเรียบร้อยแล้ว", - "opksshAuthRequired": "ต้องใช้การตรวจสอบสิทธิ์ OPKSSH", - "opksshAuthDescription": "โปรดยืนยันตัวตนในเบราว์เซอร์ของคุณเพื่อดำเนินการต่อ เซสชันนี้จะมีอายุใช้งาน 24 ชั่วโมง", - "opksshOpenBrowser": "เปิดเบราว์เซอร์เพื่อยืนยันตัวตน", - "opksshWaitingForAuth": "กำลังรอการยืนยันตัวตนในเบราว์เซอร์...", - "opksshAuthenticating": "กำลังตรวจสอบสิทธิ์...", - "opksshTimeout": "การตรวจสอบสิทธิ์หมดเวลา โปรดลองอีกครั้ง", - "opksshAuthFailed": "การตรวจสอบสิทธิ์ล้มเหลว โปรดตรวจสอบข้อมูลประจำตัวของคุณและลองอีกครั้ง", - "opksshSignInWith": "ลงชื่อเข้าใช้ด้วย {{provider}}", - "sudoPasswordPopupTitle": "ใส่รหัสผ่าน?", - "websocketAbnormalClose": "การเชื่อมต่อถูกตัดโดยไม่คาดคิด อาจเกิดจากปัญหาเกี่ยวกับรีเวิร์สพร็อกซีหรือการกำหนดค่า SSL โปรดตรวจสอบบันทึกของเซิร์ฟเวอร์", - "connectionLogTitle": "บันทึกการเชื่อมต่อ", - "connectionLogCopy": "คัดลอกบันทึกไปยังคลิปบอร์ด", - "connectionLogEmpty": "ยังไม่มีบันทึกการเชื่อมต่อ", - "connectionLogWaiting": "กำลังรอข้อมูลบันทึกการเชื่อมต่อ...", - "connectionLogCopied": "บันทึกการเชื่อมต่อถูกคัดลอกไปยังคลิปบอร์ด", - "connectionLogCopyFailed": "ไม่สามารถคัดลอกบันทึกไปยังคลิปบอร์ดได้", - "connectionRejected": "เซิร์ฟเวอร์ปฏิเสธการเชื่อมต่อ โปรดตรวจสอบการยืนยันตัวตนและการกำหนดค่าเครือข่ายของคุณ", - "hostKeyRejected": "การตรวจสอบคีย์โฮสต์ SSH ถูกปฏิเสธ การเชื่อมต่อถูกยกเลิก", - "sessionTakenOver": "เซสชั่นนี้เปิดอยู่ในแท็บอื่น กำลังเชื่อมต่อใหม่..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "ไม่ได้เลือกโฮสต์", - "initializingEditor": "กำลังเริ่มต้นโปรแกรมแก้ไข...", - "file": "ไฟล์", - "folder": "โฟลเดอร์", - "uploadFile": "อัปโหลดไฟล์", - "downloadFile": "ดาวน์โหลด", - "extractArchive": "แตกไฟล์เก็บถาวร", - "extractingArchive": "กำลังดึงข้อมูล {{name}}...", - "archiveExtractedSuccessfully": "{{name}} ดึงข้อมูลสำเร็จแล้ว", - "extractFailed": "การดึงข้อมูลล้มเหลว", - "compressFile": "บีบอัดไฟล์", - "compressFiles": "บีบอัดไฟล์", - "compressFilesDesc": "บีบอัดรายการ {{count}} รายการลงในไฟล์เก็บถาวร", - "archiveName": "ชื่อไฟล์เก็บถาวร", - "enterArchiveName": "ป้อนชื่อไฟล์เก็บถาวร...", - "compressionFormat": "รูปแบบการบีบอัด", - "selectedFiles": "ไฟล์ที่เลือก", - "andMoreFiles": "และ {{count}} เพิ่มเติม...", - "compress": "บีบอัด", - "compressingFiles": "บีบอัดรายการ {{count}} รายการลงใน {{name}} รายการ ...", - "filesCompressedSuccessfully": "{{name}} สร้างสำเร็จแล้ว", - "compressFailed": "การบีบอัดล้มเหลว", - "edit": "แก้ไข", - "preview": "ตัวอย่าง", - "previous": "ก่อนหน้า", - "next": "ต่อไป", - "pageXOfY": "หน้า {{current}} จาก {{total}}", - "zoomOut": "ซูมออก", - "zoomIn": "ซูมเข้า", - "newFile": "ไฟล์ใหม่", - "newFolder": "โฟลเดอร์ใหม่", - "rename": "เปลี่ยนชื่อ", - "uploading": "กำลังอัปโหลด...", - "uploadingFile": "กำลังอัปโหลด {{name}}...", - "fileName": "ชื่อไฟล์", - "folderName": "ชื่อโฟลเดอร์", - "fileUploadedSuccessfully": "ไฟล์ \"{{name}}\" อัปโหลดสำเร็จแล้ว", - "failedToUploadFile": "ไม่สามารถอัปโหลดไฟล์ได้", - "fileDownloadedSuccessfully": "ดาวน์โหลดไฟล์ \"{{name}}\" สำเร็จแล้ว", - "failedToDownloadFile": "ไม่สามารถดาวน์โหลดไฟล์ได้", - "fileCreatedSuccessfully": "สร้างไฟล์ \"{{name}}\" สำเร็จแล้ว", - "folderCreatedSuccessfully": "สร้างโฟลเดอร์ \"{{name}}\" สำเร็จแล้ว", - "failedToCreateItem": "ไม่สามารถสร้างรายการได้", - "operationFailed": "การดำเนินการ {{operation}} ล้มเหลวสำหรับ {{name}}: {{error}}", - "failedToResolveSymlink": "ไม่สามารถแก้ไขลิงก์สัญลักษณ์ได้", - "itemsDeletedSuccessfully": "รายการ {{count}} ถูกลบสำเร็จแล้ว", - "failedToDeleteItems": "ไม่สามารถลบรายการได้", - "sudoPasswordRequired": "ต้องใช้รหัสผ่านผู้ดูแลระบบ", - "enterSudoPassword": "ป้อนรหัสผ่าน sudo เพื่อดำเนินการต่อ", - "sudoPassword": "รหัสผ่าน Sudo", - "sudoOperationFailed": "การดำเนินการ Sudo ล้มเหลว", - "sudoAuthFailed": "การตรวจสอบสิทธิ์ Sudo ล้มเหลว", - "dragFilesToUpload": "ลากไฟล์มาวางที่นี่เพื่ออัปโหลด", - "emptyFolder": "โฟลเดอร์นี้ว่างเปล่า", - "searchFiles": "ค้นหาไฟล์...", - "upload": "อัปโหลด", - "selectHostToStart": "เลือกโฮสต์เพื่อเริ่มการจัดการไฟล์", - "sshRequiredForFileManager": "โปรแกรมจัดการไฟล์ต้องการการเชื่อมต่อ SSH แต่เครื่องนี้ไม่ได้เปิดใช้งาน SSH", - "failedToConnect": "ไม่สามารถเชื่อมต่อ SSH ได้", - "failedToLoadDirectory": "ไม่สามารถโหลดไดเร็กทอรีได้", - "noSSHConnection": "ไม่มีการเชื่อมต่อ SSH ให้ใช้งาน", - "copy": "สำเนา", - "cut": "ตัด", - "paste": "แปะ", - "copyPath": "คัดลอกเส้นทาง", - "copyPaths": "คัดลอกเส้นทาง", - "delete": "ลบ", - "properties": "คุณสมบัติ", - "refresh": "รีเฟรช", - "downloadFiles": "ดาวน์โหลดไฟล์ {{count}} ไปยังเบราว์เซอร์", - "copyFiles": "คัดลอกรายการ {{count}} รายการ", - "cutFiles": "ตัด {{count}} รายการ", - "deleteFiles": "ลบรายการ {{count}} รายการ", - "filesCopiedToClipboard": "{{count}} รายการที่คัดลอกไปยังคลิปบอร์ด", - "filesCutToClipboard": "{{count}} รายการที่ถูกตัดไปยังคลิปบอร์ด", - "pathCopiedToClipboard": "คัดลอกเส้นทางไปยังคลิปบอร์ดแล้ว", - "pathsCopiedToClipboard": "{{count}} คัดลอกเส้นทางไปยังคลิปบอร์ดแล้ว", - "failedToCopyPath": "ไม่สามารถคัดลอกเส้นทางไปยังคลิปบอร์ดได้", - "movedItems": "ย้ายรายการ {{count}} รายการ", - "failedToDeleteItem": "ไม่สามารถลบรายการได้", - "itemRenamedSuccessfully": "{{type}} เปลี่ยนชื่อสำเร็จแล้ว", - "failedToRenameItem": "ไม่สามารถเปลี่ยนชื่อรายการได้", - "download": "ดาวน์โหลด", - "permissions": "สิทธิ์การเข้าถึง", - "size": "ขนาด", - "modified": "แก้ไขแล้ว", - "path": "เส้นทาง", - "confirmDelete": "คุณแน่ใจหรือไม่ว่าต้องการลบ {{name}}?", - "permissionDenied": "ไม่ได้รับอนุญาต", - "serverError": "ข้อผิดพลาดของเซิร์ฟเวอร์", - "fileSavedSuccessfully": "บันทึกไฟล์สำเร็จแล้ว", - "failedToSaveFile": "ไม่สามารถบันทึกไฟล์ได้", - "confirmDeleteSingleItem": "คุณแน่ใจหรือไม่ว่าต้องการลบ \"{{name}} \" อย่างถาวร?", - "confirmDeleteMultipleItems": "คุณแน่ใจหรือไม่ว่าต้องการลบรายการ {{count}} อย่างถาวร?", - "confirmDeleteMultipleItemsWithFolders": "คุณแน่ใจหรือไม่ว่าต้องการลบรายการ {{count}} รายการอย่างถาวร ซึ่งรวมถึงโฟลเดอร์และเนื้อหาภายในด้วย", - "confirmDeleteFolder": "คุณแน่ใจหรือไม่ว่าต้องการลบโฟลเดอร์ \"{{name}}\" และเนื้อหาทั้งหมดในนั้นอย่างถาวร?", - "permanentDeleteWarning": "การดำเนินการนี้ไม่สามารถยกเลิกได้ รายการดังกล่าวจะถูกลบออกจากเซิร์ฟเวอร์อย่างถาวร", - "recent": "ล่าสุด", - "pinned": "ปักหมุด", - "folderShortcuts": "ทางลัดโฟลเดอร์", - "failedToReconnectSSH": "ไม่สามารถเชื่อมต่อเซสชัน SSH ใหม่ได้", - "openTerminalHere": "เปิดเทอร์มินัลที่นี่", - "run": "วิ่ง", - "openTerminalInFolder": "เปิดเทอร์มินัลในโฟลเดอร์นี้", - "openTerminalInFileLocation": "เปิดเทอร์มินัลที่ตำแหน่งไฟล์", - "runningFile": "การวิ่ง - {{file}}", - "onlyRunExecutableFiles": "สามารถเรียกใช้งานได้เฉพาะไฟล์ปฏิบัติการเท่านั้น", - "directories": "รายชื่อ", - "removedFromRecentFiles": "ลบ \"{{name}}\" ออกจากไฟล์ล่าสุดแล้ว", - "removeFailed": "ลบสิ่งที่ล้มเหลว", - "unpinnedSuccessfully": "ยกเลิกการตรึง \"{{name}}\" สำเร็จแล้ว", - "unpinFailed": "ยกเลิกการตรึงไม่สำเร็จ", - "removedShortcut": "ลบทางลัด \"{{name}} \" ออกแล้ว", - "removeShortcutFailed": "การลบทางลัดล้มเหลว", - "clearedAllRecentFiles": "ลบไฟล์ล่าสุดทั้งหมดแล้ว", - "clearFailed": "เคลียร์ล้มเหลว", - "removeFromRecentFiles": "ลบออกจากไฟล์ล่าสุด", - "clearAllRecentFiles": "ลบไฟล์ล่าสุดทั้งหมด", - "unpinFile": "ยกเลิกการตรึงไฟล์", - "removeShortcut": "ลบทางลัด", - "pinFile": "ไฟล์พิน", - "addToShortcuts": "เพิ่มไปยังทางลัด", - "pasteFailed": "การวางล้มเหลว", - "noUndoableActions": "ไม่มีการกระทำใดที่ย้อนกลับไม่ได้", - "undoCopySuccess": "ยกเลิกการคัดลอก: ลบไฟล์ที่คัดลอก {{count}} ไฟล์", - "undoCopyFailedDelete": "การยกเลิกการกระทำล้มเหลว: ไม่สามารถลบไฟล์ที่คัดลอกไว้ได้", - "undoCopyFailedNoInfo": "การยกเลิกการกระทำล้มเหลว: ไม่พบข้อมูลไฟล์ที่คัดลอกไว้", - "undoMoveSuccess": "ยกเลิกการย้าย: ย้ายไฟล์ {{count}} ไฟล์กลับไปยังตำแหน่งเดิม", - "undoMoveFailedMove": "การยกเลิกไม่สำเร็จ: ไม่สามารถย้ายไฟล์ใดๆ กลับได้", - "undoMoveFailedNoInfo": "การยกเลิกไม่สำเร็จ: ไม่พบข้อมูลไฟล์ที่ถูกย้าย", - "undoDeleteNotSupported": "ไม่สามารถยกเลิกการลบได้: ไฟล์ถูกลบออกจากเซิร์ฟเวอร์อย่างถาวรแล้ว", - "undoTypeNotSupported": "ประเภทการดำเนินการยกเลิกที่ไม่รองรับ", - "undoOperationFailed": "การยกเลิกการดำเนินการล้มเหลว", - "unknownError": "ข้อผิดพลาดที่ไม่ทราบสาเหตุ", - "confirm": "ยืนยัน", - "find": "หา...", - "replace": "แทนที่", - "downloadInstead": "ดาวน์โหลดแทน", - "keyboardShortcuts": "แป้นพิมพ์ลัด", - "searchAndReplace": "ค้นหาและแทนที่", - "editing": "การแก้ไข", - "search": "ค้นหา", - "findNext": "ค้นหาถัดไป", - "findPrevious": "ค้นหาก่อนหน้า", - "save": "บันทึก", - "selectAll": "เลือกทั้งหมด", - "undo": "เลิกทำ", - "redo": "ทำซ้ำ", - "moveLineUp": "เคลื่อนแถว", - "moveLineDown": "เลื่อนเส้นลง", - "toggleComment": "สลับการแสดงความคิดเห็น", - "autoComplete": "การเติมข้อความอัตโนมัติ", - "imageLoadError": "ไม่สามารถโหลดรูปภาพได้", - "startTyping": "เริ่มพิมพ์...", - "unknownSize": "ขนาดไม่ทราบแน่ชัด", - "fileIsEmpty": "ไฟล์ว่างเปล่า", - "largeFileWarning": "คำเตือนเกี่ยวกับไฟล์ขนาดใหญ่", - "largeFileWarningDesc": "ไฟล์นี้มีขนาด {{size}} ซึ่งอาจทำให้เกิดปัญหาด้านประสิทธิภาพเมื่อเปิดเป็นข้อความ", - "fileNotFoundAndRemoved": "ไม่พบไฟล์ \"{{name}}\" และไฟล์ดังกล่าวถูกลบออกจากไฟล์ล่าสุด/ไฟล์ปักหมุดแล้ว", - "failedToLoadFile": "ไม่สามารถโหลดไฟล์ได้: {{error}}", - "serverErrorOccurred": "เกิดข้อผิดพลาดของเซิร์ฟเวอร์ โปรดลองใหม่อีกครั้งในภายหลัง", - "autoSaveFailed": "การบันทึกอัตโนมัติล้มเหลว", - "fileAutoSaved": "ไฟล์ถูกบันทึกอัตโนมัติ", - "moveFileFailed": "การย้ายล้มเหลว {{name}}", - "moveOperationFailed": "การดำเนินการย้ายล้มเหลว", - "canOnlyCompareFiles": "สามารถเปรียบเทียบไฟล์ได้เพียงสองไฟล์เท่านั้น", - "comparingFiles": "เปรียบเทียบไฟล์: {{file1}} และ {{file2}}", - "dragFailed": "การดำเนินการลากล้มเหลว", - "filePinnedSuccessfully": "ไฟล์ \"{{name}}\" ถูกปักหมุดสำเร็จแล้ว", - "pinFileFailed": "ไม่สามารถตรึงไฟล์ได้", - "fileUnpinnedSuccessfully": "ไฟล์ \"{{name}}\" ถูกยกเลิกการตรึงสำเร็จแล้ว", - "unpinFileFailed": "ไม่สามารถยกเลิกการตรึงไฟล์ได้", - "shortcutAddedSuccessfully": "เพิ่มทางลัดโฟลเดอร์ \"{{name}}\" สำเร็จแล้ว", - "addShortcutFailed": "ไม่สามารถเพิ่มทางลัดได้", - "operationCompletedSuccessfully": "{{operation}} {{count}} รายการสำเร็จ", - "operationCompleted": "{{operation}} {{count}} รายการ", - "downloadFileSuccess": "ดาวน์โหลดไฟล์ {{name}} สำเร็จแล้ว", - "downloadFileFailed": "การดาวน์โหลดล้มเหลว", - "moveTo": "ย้ายไปที่ {{name}}", - "diffCompareWith": "เปรียบเทียบความแตกต่างกับ {{name}}", - "dragOutsideToDownload": "ลากออกนอกหน้าต่างเพื่อดาวน์โหลดไฟล์ ({{count}} ไฟล์)", - "newFolderDefault": "โฟลเดอร์ใหม่", - "newFileDefault": "ไฟล์ใหม่.txt", - "successfullyMovedItems": "ย้ายรายการ {{count}} รายการไปยัง {{target}} สำเร็จแล้ว", - "move": "เคลื่อนไหว", - "searchInFile": "ค้นหาในไฟล์ (Ctrl+F)", - "showKeyboardShortcuts": "แสดงทางลัดแป้นพิมพ์", - "startWritingMarkdown": "เริ่มเขียนเนื้อหา Markdown ของคุณได้เลย...", - "loadingFileComparison": "กำลังโหลดการเปรียบเทียบไฟล์...", - "reload": "โหลดซ้ำ", - "compare": "เปรียบเทียบ", - "sideBySide": "เคียงข้างกัน", - "inline": "อินไลน์", - "fileComparison": "การเปรียบเทียบไฟล์: {{file1}} กับ {{file2}}", - "fileTooLarge": "ไฟล์มีขนาดใหญ่เกินไป: {{error}}", - "sshConnectionFailed": "การเชื่อมต่อ SSH ล้มเหลว โปรดตรวจสอบการเชื่อมต่อของคุณกับ {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "ไม่สามารถโหลดไฟล์ได้: {{error}}", - "connecting": "กำลังเชื่อมต่อ...", - "connectedSuccessfully": "เชื่อมต่อสำเร็จแล้ว", - "totpVerificationFailed": "การตรวจสอบ TOTP ล้มเหลว", - "warpgateVerificationFailed": "การตรวจสอบสิทธิ์วาร์ปไม่สำเร็จ", - "authenticationFailed": "การตรวจสอบสิทธิ์ล้มเหลว", - "verificationCodePrompt": "รหัสยืนยัน:", - "changePermissions": "เปลี่ยนสิทธิ์การเข้าถึง", - "currentPermissions": "สิทธิ์การเข้าถึงปัจจุบัน", - "owner": "เจ้าของ", - "group": "กลุ่ม", - "others": "คนอื่น", - "read": "อ่าน", - "write": "เขียน", - "execute": "ดำเนินการ", - "permissionsChangedSuccessfully": "สิทธิ์การเข้าถึงเปลี่ยนแปลงสำเร็จแล้ว", - "failedToChangePermissions": "ไม่สามารถเปลี่ยนสิทธิ์ได้", - "name": "ชื่อ", - "sortByName": "ชื่อ", - "sortByDate": "วันที่แก้ไขล่าสุด", - "sortBySize": "ขนาด", - "ascending": "ขึ้นไป", - "descending": "ลง", - "root": "ราก", - "new": "ใหม่", - "sortBy": "เรียงลำดับตาม", - "items": "รายการ", - "selected": "เลือกแล้ว", - "editor": "บรรณาธิการ", - "octal": "แปด", - "storage": "พื้นที่จัดเก็บ", - "disk": "ดิสก์", - "used": "ใช้แล้ว", - "of": "ของ", - "toggleSidebar": "สลับแถบด้านข้าง", - "cannotLoadPdf": "ไม่สามารถโหลดไฟล์ PDF ได้", - "pdfLoadError": "เกิดข้อผิดพลาดในการโหลดไฟล์ PDF นี้", - "loadingPdf": "กำลังโหลดไฟล์ PDF...", - "loadingPage": "กำลังโหลดหน้าเว็บ..." + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "คัดลอกไปยังโฮสต์…", - "moveToHost": "ย้ายไปยังโฮสต์…", - "copyItemsToHost": "คัดลอกรายการ {{count}} รายการไปยังโฮสต์…", - "moveItemsToHost": "ย้ายรายการ {{count}} รายการไปยังโฮสต์…", - "noHostsConnected": "ไม่มีโฮสต์ตัวจัดการไฟล์อื่นให้บริการ", - "noHostsConnectedHint": "เพิ่มโฮสต์ SSH อีกตัวโดยเปิดใช้งานตัวจัดการไฟล์ในตัวจัดการโฮสต์", - "selectDestinationHost": "เลือกโฮสต์ปลายทาง", - "destinationPath": "เส้นทางปลายทาง", - "recentDestinations": "จุดหมายปลายทางล่าสุด", - "collapseRecentDestinations": "ยุบจุดหมายปลายทางล่าสุด", - "expandRecentDestinations": "ขยายข้อมูลจุดหมายปลายทางล่าสุด", - "browseFolders": "เรียกดูโฟลเดอร์ปลายทาง", - "browseDestination": "เรียกดูหรือป้อนเส้นทาง", - "confirmCopy": "สำเนา", - "confirmMove": "เคลื่อนไหว", - "transferring": "กำลังโอน…", - "compressing": "กำลังบีบอัด…", - "extracting": "กำลังแยก…", - "transferringItems": "กำลังโอนย้ายรายการ {{current}} รายการ {{total}} รายการ…", - "transferSuccess": "การโอนเสร็จสมบูรณ์", - "transferError": "การโอนล้มเหลว", - "transferPartial": "การโอนเสร็จสมบูรณ์โดยมีข้อผิดพลาด {{count}}", - "transferPartialHint": "ไม่สามารถโอนเงินได้: {{paths}}", - "itemsSummary": "{{count}} รายการ", - "destMustBeDirectory": "สำหรับการโอนย้ายหลายรายการ ปลายทางต้องเป็นไดเร็กทอรี", - "selectThisFolder": "เลือกโฟลเดอร์นี้", - "browsePathWillBeCreated": "โฟลเดอร์นี้ยังไม่มีอยู่ จะถูกสร้างขึ้นเมื่อการถ่ายโอนเริ่มต้นขึ้น", - "browsePathError": "ไม่สามารถเปิดเส้นทางนี้บนโฮสต์ปลายทางได้", - "goUp": "ขึ้นไป", - "copyFolderToHost": "คัดลอกโฟลเดอร์ไปยังโฮสต์…", - "moveFolderToHost": "ย้ายโฟลเดอร์ไปยังโฮสต์…", - "hostReady": "พร้อม", - "hostConnecting": "กำลังเชื่อมต่อ…", - "hostDisconnected": "ไม่ได้เชื่อมต่อ", - "hostAuthRequired": "ต้องมีการยืนยันตัวตน — โปรดเปิดตัวจัดการไฟล์บนโฮสต์นี้ก่อน", - "hostConnectionFailed": "การเชื่อมต่อล้มเหลว", - "metricsTitle": "เวลาในการโอนย้าย", - "metricsPrepare": "เตรียมปลายทาง: {{duration}}", - "metricsCompress": "บีบอัดจากแหล่งที่มา: {{duration}}", - "metricsHopSourceRead": "แหล่งที่มา → เซิร์ฟเวอร์: {{throughput}}", - "metricsHopDestSftpWrite": "เซิร์ฟเวอร์ → ปลายทาง (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "เซิร์ฟเวอร์ → ปลายทาง (ภายในเครื่อง): {{throughput}}", - "metricsTransfer": "จากต้นทางถึงปลายทาง: {{throughput}} ({{duration}})", - "metricsExtract": "แตกไฟล์ที่ปลายทาง: {{duration}}", - "metricsSourceDelete": "ลบออกจากแหล่งที่มา: {{duration}}", - "metricsTotal": "รวม: {{duration}}", - "progressCompressing": "กำลังบีบอัดบนโฮสต์ต้นทาง…", - "progressExtracting": "กำลังแตกไฟล์ที่ปลายทาง…", - "progressTransferring": "กำลังถ่ายโอนข้อมูล…", - "progressReconnecting": "กำลังเชื่อมต่อใหม่…", - "parallelSegmentsLabel": "ช่องทางเปลี่ยนถ่ายแบบขนาน", - "parallelSegmentsOption": "{{count}} เลน", - "parallelSegmentsHint": "ไฟล์ขนาดใหญ่จะถูกแบ่งออกเป็นส่วนย่อยขนาด 256 MB การใช้หลายเลนจะใช้การเชื่อมต่อแยกกัน (เช่น การเริ่มการถ่ายโอนหลายรายการพร้อมกัน) เพื่อให้ได้ปริมาณงานโดยรวมที่สูงขึ้น", - "progressTotalSpeed": "{{speed}} รวม ({{lanes}} เลน)", - "progressTransferringItems": "กำลังถ่ายโอนไฟล์ ({{current}} จาก {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "ไฟล์ {{current}} / {{total}}", - "sourceNotDeletedPartial": "ไฟล์ต้นฉบับถูกเก็บไว้ (ถ่ายโอนบางส่วน)", - "jumpHostLimitation": "โฮสต์ทั้งสองต้องสามารถเข้าถึงได้จากเซิร์ฟเวอร์ Termix ไม่รองรับการกำหนดเส้นทางโดยตรงระหว่างโฮสต์", - "cancel": "ยกเลิก", - "methodLabel": "วิธีการถ่ายโอน", - "methodAuto": "อัตโนมัติ", - "methodTar": "คลังข้อมูลทาร์", - "methodItemSftp": "SFTP ต่อไฟล์", - "methodAutoHint": "เลือกใช้ไฟล์บีบอัดแบบ tar หรือ SFTP แบบต่อไฟล์ โดยพิจารณาจากจำนวนไฟล์ ขนาดไฟล์ และความสามารถในการบีบอัด ไฟล์เดี่ยวจะใช้ SFTP แบบสตรีมมิ่งเสมอ", - "methodTarHint": "บีบอัดไฟล์ที่ต้นทาง ถ่ายโอนไฟล์เก็บถาวรหนึ่งไฟล์ และแตกไฟล์ที่ปลายทาง ต้องใช้ tar บนเครื่อง Unix ทั้งสองเครื่อง", - "methodItemSftpHint": "โอนไฟล์แต่ละไฟล์ทีละไฟล์ผ่าน SFTP ใช้งานได้กับทุกระบบปฏิบัติการ รวมถึง Windows", - "methodPreviewLoading": "กำลังคำนวณวิธีการโอน…", - "methodPreviewError": "ไม่สามารถดูตัวอย่างวิธีการถ่ายโอนได้ เซิร์ฟเวอร์จะเลือกวิธีการเมื่อคุณเริ่มต้นใช้งาน", - "methodPreviewWillUseTar": "จะใช้: ไฟล์เก็บถาวร Tar", - "methodPreviewWillUseItemSftp": "จะใช้: SFTP แบบแยกไฟล์", - "methodPreviewScanSummary": "ไฟล์ {{fileCount}} ไฟล์, รวม {{totalSize}} ไฟล์ (สแกนจากโฮสต์ต้นทาง)", - "methodItemSftpLimitation": "แต่ละไฟล์จะใช้สตรีม SFTP เดียวกันในการคัดลอกไฟล์ทีละไฟล์ ความคืบหน้าจะถูกรวมเข้าด้วยกัน ทำให้แถบแสดงความคืบหน้าเคลื่อนที่ช้าลงเมื่อคัดลอกไฟล์ขนาดใหญ่", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "คุณเลือกใช้ SFTP แบบต่อไฟล์", - "user_tar": "คุณเลือกไฟล์เก็บถาวรแบบ tar", - "tar_unavailable": "Tar ไม่สามารถใช้งานได้บนโฮสต์หนึ่งหรือทั้งสองโฮสต์ จึงจะใช้ SFTP แบบแยกไฟล์แทน", - "windows_host": "ระบบปฏิบัติการที่ใช้คือ Windows — ไม่ได้ใช้โปรแกรม tar", - "auto_multi_large": "อัตโนมัติ: ไฟล์หลายไฟล์ รวมถึงไฟล์ขนาดใหญ่ ({{largestSize}}) ที่มีข้อมูลที่สามารถบีบอัดได้ — tar จะรวมไฟล์เหล่านั้นไว้ในการถ่ายโอนครั้งเดียว", - "auto_single_large_in_archive": "อัตโนมัติ: ไฟล์ขนาดใหญ่หนึ่งไฟล์ ({{largestSize}}) ในชุดนี้ — SFTP ต่อไฟล์", - "auto_many_incompressible": "อัตโนมัติ: ข้อมูลส่วนใหญ่ไม่สามารถบีบอัดได้ — SFTP แบบแยกไฟล์", - "auto_many_files": "อัตโนมัติ: หลายไฟล์ ({{fileCount}}) — tar ช่วยลดภาระต่อไฟล์", - "auto_default": "อัตโนมัติ: ตั้งค่า SFTP สำหรับแต่ละไฟล์ในชุดนี้" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "ยกเลิก", - "progressCancelling": "การยกเลิก…", - "progressStalled": "หยุดชะงัก", - "resumedHint": "เชื่อมต่อใหม่กับการถ่ายโอนที่เริ่มทำงานในหน้าต่างอื่นแล้ว", - "transferCancelled": "การโอนเงินถูกยกเลิก", - "transferCancelledCopyHint": "ไฟล์บางส่วนอาจยังคงอยู่ในปลายทาง", - "transferCancelledMoveHint": "ไฟล์บางส่วนอาจยังคงอยู่ในปลายทาง ไฟล์ต้นฉบับไม่ได้ถูกลบออก", - "cleanupDestFiles": "สถานที่ทำความสะอาด", - "cleanupDestFilesSuccess": "ลบไฟล์บางส่วนออกจากปลายทางแล้ว", - "cleanupDestFilesPartial": "ไม่สามารถลบไฟล์บางส่วนได้", - "cleanupDestFilesNothing": "ไม่มีอะไรต้องทำความสะอาดที่ปลายทาง", - "cleanupDestFilesError": "การทำความสะอาดล้มเหลว", - "retryTransfer": "ลองใหม่อีกครั้ง", - "retryTransferError": "การลองใหม่ล้มเหลว", - "transferFailedRetryHint": "ข้อมูลบางส่วนถูกเก็บไว้ในปลายทาง การลองใหม่จะดำเนินการต่อเมื่อการเชื่อมต่อกลับมาใช้งานได้" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "ไม่มีอุโมงค์ SSH", - "createFirstTunnelMessage": "คุณยังไม่ได้สร้างอุโมงค์ SSH ใดๆ เลย กำหนดค่าการเชื่อมต่ออุโมงค์ใน Host Manager เพื่อเริ่มต้นใช้งาน", - "connected": "เชื่อมต่อแล้ว", - "disconnected": "ตัดการเชื่อมต่อ", - "connecting": "กำลังเชื่อมต่อ...", - "error": "ข้อผิดพลาด", - "canceling": "ยกเลิก...", - "connect": "เชื่อมต่อ", - "disconnect": "ตัดการเชื่อมต่อ", - "cancel": "ยกเลิก", - "port": "ท่าเรือ", - "localPort": "ท่าเรือท้องถิ่น", - "remotePort": "พอร์ตระยะไกล", - "currentHostPort": "พอร์ตโฮสต์ปัจจุบัน", - "endpointPort": "พอร์ตปลายทาง", - "bindIp": "ที่อยู่ IP ภายใน", - "endpointSshConfig": "การกำหนดค่า SSH ปลายทาง", - "endpointSshHost": "โฮสต์ SSH ปลายทาง", - "endpointSshHostPlaceholder": "เลือกโฮสต์ที่กำหนดค่าไว้", - "endpointSshHostRequired": "เลือกโฮสต์ SSH ปลายทางสำหรับแต่ละอุโมงค์ไคลเอ็นต์", - "attempt": "ความพยายาม {{current}} ของ {{max}}", - "nextRetryIn": "ลองใหม่อีกครั้งใน {{seconds}} วินาที", - "clientTunnels": "อุโมงค์ไคลเอ็นต์", - "clientTunnel": "อุโมงค์ลูกค้า", - "addClientTunnel": "เพิ่มอุโมงค์ไคลเอ็นต์", - "noClientTunnels": "ไม่มีการกำหนดค่าอุโมงค์ไคลเอ็นต์บนเดสก์ท็อปนี้", - "tunnelName": "ชื่ออุโมงค์", - "remoteHost": "โฮสต์ระยะไกล", - "autoStart": "เริ่มอัตโนมัติ", - "clientAutoStartDesc": "เริ่มทำงานเมื่อโปรแกรมไคลเอ็นต์บนเดสก์ท็อปนี้เปิดขึ้นและเชื่อมต่ออยู่ตลอดเวลา", - "clientManualStartDesc": "ใช้ปุ่ม Start และ Stop จากแถวนี้ Termix จะไม่เปิดไฟล์นี้โดยอัตโนมัติ", - "clientRemoteServerNote": "การส่งต่อพอร์ตระยะไกลอาจต้องใช้การตั้งค่า AllowTcpForwarding และ GatewayPorts บนเซิร์ฟเวอร์ SSH ปลายทาง พอร์ตระยะไกลจะปิดลงเมื่อเดสก์ท็อปนี้ตัดการเชื่อมต่อ", - "clientTunnelStarted": "อุโมงค์ไคลเอ็นต์เริ่มต้นแล้ว", - "clientTunnelStopped": "อุโมงค์ไคลเอ็นต์หยุดทำงานแล้ว", - "tunnelTestSucceeded": "การทดสอบอุโมงค์ประสบความสำเร็จ", - "tunnelTestFailed": "การทดสอบอุโมงค์ล้มเหลว", - "localSaved": "บันทึกอุโมงค์ไคลเอ็นต์แล้ว", - "localSaveError": "ไม่สามารถบันทึกอุโมงค์ไคลเอ็นต์ในพื้นที่ได้", - "invalidBindIp": "ที่อยู่ IP ภายในเครื่องต้องเป็นที่อยู่ IPv4 ที่ถูกต้อง", - "invalidLocalTargetIp": "ที่อยู่ IP เป้าหมายในเครือข่ายท้องถิ่นต้องเป็นที่อยู่ IPv4 ที่ถูกต้อง", - "invalidLocalPort": "หมายเลขพอร์ตท้องถิ่นต้องอยู่ระหว่าง 1 ถึง 65535", - "invalidRemotePort": "พอร์ตระยะไกลต้องอยู่ระหว่าง 1 ถึง 65535", - "invalidLocalTargetPort": "พอร์ตเป้าหมายในเครื่องต้องอยู่ระหว่าง 1 ถึง 65535", - "invalidEndpointPort": "พอร์ตปลายทางต้องอยู่ระหว่าง 1 ถึง 65535", - "duplicateAutoStartBind": "อุโมงค์ไคลเอ็นต์ที่เริ่มต้นอัตโนมัติเพียงหนึ่งเดียวเท่านั้นที่สามารถใช้ {{bind}} ได้", - "manualControlError": "ไม่สามารถอัปเดตสถานะอุโมงค์ได้", - "active": "คล่องแคล่ว", - "start": "เริ่ม", - "stop": "หยุด", - "test": "ทดสอบ", - "type": "ประเภทอุโมงค์", - "typeLocal": "ท้องถิ่น (-L)", - "typeRemote": "รีโมท (-R)", - "typeDynamic": "ไดนามิก (-D)", - "typeServerLocalDesc": "ปัจจุบันโฮสต์ไปยังเอนด์พอยต์", - "typeServerRemoteDesc": "ปลายทางกลับไปยังโฮสต์ปัจจุบัน", - "typeClientLocalDesc": "คอมพิวเตอร์ในพื้นที่เชื่อมต่อกับอุปกรณ์ปลายทาง", - "typeClientRemoteDesc": "ปลายทางกลับไปยังคอมพิวเตอร์เครื่องโลคอล", - "typeClientDynamicDesc": "ถุงเท้าบนคอมพิวเตอร์เครื่องโลคัล", - "typeDynamicDesc": "ส่งต่อทราฟฟิก SOCKS5 CONNECT ผ่าน SSH", - "forwardDescriptionServerLocal": "โฮสต์ปัจจุบัน {{sourcePort}} → ปลายทาง {{endpointPort}}", - "forwardDescriptionServerRemote": "ปลายทาง {{endpointPort}} → โฮสต์ปัจจุบัน {{sourcePort}}", - "forwardDescriptionServerDynamic": "SOCKS บนโฮสต์ปัจจุบัน {{sourcePort}}.", - "forwardDescriptionClientLocal": "เครื่องโลคอล {{sourcePort}} → รีโมท {{endpointPort}}", - "forwardDescriptionClientRemote": "รีโมท {{sourcePort}} → โลคอล {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS บนพอร์ตท้องถิ่น {{sourcePort}}", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → ถุงเท้า ผ่านทาง {{endpoint}}", - "autoNameClientLocal": "โลคอล {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → โลคอล {{localPort}}", - "autoNameClientDynamic": "ถุงเท้า {{localPort}} ผ่าน {{endpoint}}", - "route": "เส้นทาง:", - "lastStarted": "เริ่มครั้งล่าสุด", - "lastTested": "ทดสอบครั้งล่าสุด", - "lastError": "ข้อผิดพลาดล่าสุด", - "maxRetries": "จำนวนครั้งการลองใหม่สูงสุด", - "maxRetriesDescription": "จำนวนครั้งสูงสุดที่สามารถลองใหม่ได้", - "retryInterval": "ช่วงเวลาการลองใหม่ (วินาที)", - "retryIntervalDescription": "ต้องรอเวลาระหว่างการลองใหม่แต่ละครั้ง", - "local": "ท้องถิ่น", - "remote": "ระยะไกล", - "destination": "ปลายทาง", - "host": "เจ้าภาพ", - "mode": "โหมด", - "noHostSelected": "ไม่ได้เลือกโฮสต์", - "working": "การทำงาน..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "ซีพียู", - "memory": "หน่วยความจำ", - "disk": "ดิสก์", - "network": "เครือข่าย", - "uptime": "เวลาใช้งาน", - "processes": "กระบวนการ", - "available": "มีอยู่", - "free": "ฟรี", - "connecting": "กำลังเชื่อมต่อ...", - "connectionFailed": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้", - "naCpus": "ไม่มีข้อมูล CPU", - "cpuCores_one": "{{count}} แกนหลัก", - "cpuCores_other": "{{count}} คอร์", - "cpuUsage": "การใช้งาน CPU", - "memoryUsage": "การใช้งานหน่วยความจำ", - "diskUsage": "การใช้งานดิสก์", - "failedToFetchHostConfig": "ไม่สามารถดึงข้อมูลการกำหนดค่าโฮสต์ได้", - "serverOffline": "เซิร์ฟเวอร์ออฟไลน์", - "cannotFetchMetrics": "ไม่สามารถดึงข้อมูลเมตริกจากเซิร์ฟเวอร์ออฟไลน์ได้", - "totpFailed": "การตรวจสอบ TOTP ล้มเหลว", - "noneAuthNotSupported": "สถิติเซิร์ฟเวอร์ไม่รองรับประเภทการตรวจสอบสิทธิ์แบบ 'none'", - "load": "โหลด", - "systemInfo": "ข้อมูลระบบ", - "hostname": "ชื่อโฮสต์", - "operatingSystem": "ระบบปฏิบัติการ", - "kernel": "เคอร์เนล", - "seconds": "วินาที", - "networkInterfaces": "อินเทอร์เฟซเครือข่าย", - "noInterfacesFound": "ไม่พบอินเทอร์เฟซเครือข่าย", - "noProcessesFound": "ไม่พบกระบวนการใดๆ", - "loginStats": "สถิติการเข้าสู่ระบบ SSH", - "noRecentLoginData": "ไม่มีข้อมูลการเข้าสู่ระบบล่าสุด", - "executingQuickAction": "กำลังดำเนินการ {{name}}...", - "quickActionSuccess": "{{name}} เสร็จสมบูรณ์เรียบร้อยแล้ว", - "quickActionFailed": "{{name}} ล้มเหลว", - "quickActionError": "ไม่สามารถดำเนินการ {{name}} ได้", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "พอร์ตรับฟัง", - "protocol": "โปรโตคอล", - "port": "ท่าเรือ", - "address": "ที่อยู่", - "process": "กระบวนการ", - "noData": "ไม่มีข้อมูลพอร์ตการฟัง" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "ไฟร์วอลล์", - "inactive": "ไม่ใช้งาน", - "policy": "นโยบาย", - "rules": "กฎ", - "noData": "ไม่มีข้อมูลไฟร์วอลล์ให้ใช้งาน", - "action": "การกระทำ", - "protocol": "โปรโต", - "port": "ท่าเรือ", - "source": "แหล่งที่มา", - "anywhere": "ทุกที่" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "โหลดเฉลี่ย", - "swap": "แลกเปลี่ยน", - "architecture": "สถาปัตยกรรม", - "refresh": "รีเฟรช", - "retry": "ลองใหม่อีกครั้ง" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "การจัดการ SSH และเดสก์ท็อประยะไกลแบบโฮสต์เอง", - "loginTitle": "เข้าสู่ระบบ Termix", - "registerTitle": "สร้างบัญชี", - "forgotPassword": "ลืมรหัสผ่านใช่ไหม?", - "rememberMe": "จดจำอุปกรณ์เป็นเวลา 30 วัน (รวมถึง TOTP)", - "noAccount": "ยังไม่มีบัญชีใช่ไหม?", - "hasAccount": "มีบัญชีอยู่แล้วใช่ไหม?", - "twoFactorAuth": "การตรวจสอบสิทธิ์แบบสองปัจจัย", - "enterCode": "ป้อนรหัสยืนยัน", - "backupCode": "หรือใช้รหัสสำรอง", - "verifyCode": "ยืนยันรหัส", - "redirectingToApp": "กำลังเปลี่ยนเส้นทางไปยังแอป...", - "sshAuthenticationRequired": "ต้องมีการตรวจสอบสิทธิ์ SSH", - "sshNoKeyboardInteractive": "การตรวจสอบสิทธิ์แบบโต้ตอบผ่านแป้นพิมพ์ไม่พร้อมใช้งาน", - "sshAuthenticationFailed": "การตรวจสอบสิทธิ์ล้มเหลว", - "sshAuthenticationTimeout": "หมดเวลาการตรวจสอบสิทธิ์", - "sshNoKeyboardInteractiveDescription": "เซิร์ฟเวอร์ไม่รองรับการตรวจสอบสิทธิ์แบบโต้ตอบด้วยแป้นพิมพ์ โปรดป้อนรหัสผ่านหรือคีย์ SSH ของคุณ", - "sshAuthFailedDescription": "ข้อมูลประจำตัวที่ให้มาไม่ถูกต้อง โปรดลองอีกครั้งด้วยข้อมูลประจำตัวที่ถูกต้อง", - "sshTimeoutDescription": "การพยายามยืนยันตัวตนหมดเวลา โปรดลองอีกครั้ง", - "sshProvideCredentialsDescription": "โปรดระบุข้อมูลประจำตัว SSH ของคุณเพื่อเชื่อมต่อกับเซิร์ฟเวอร์นี้", - "sshPasswordDescription": "ป้อนรหัสผ่านสำหรับการเชื่อมต่อ SSH นี้", - "sshKeyPasswordDescription": "หากคีย์ SSH ของคุณถูกเข้ารหัส ให้ป้อนรหัสผ่านที่นี่", - "passphraseRequired": "ต้องใส่รหัสผ่าน", - "passphraseRequiredDescription": "คีย์ SSH ถูกเข้ารหัสไว้ โปรดป้อนรหัสผ่านเพื่อปลดล็อก", - "back": "กลับ", - "firstUser": "ผู้ใช้คนแรก", - "firstUserMessage": "คุณเป็นผู้ใช้คนแรกและจะได้รับสิทธิ์เป็นผู้ดูแลระบบ คุณสามารถดูการตั้งค่าผู้ดูแลระบบได้ในเมนูแบบเลื่อนลงผู้ใช้ด้านข้าง หากคุณคิดว่านี่เป็นข้อผิดพลาด โปรดตรวจสอบบันทึกของ Docker หรือสร้างปัญหาใน GitHub", - "external": "ภายนอก", - "loginWithExternal": "เข้าสู่ระบบด้วยผู้ให้บริการภายนอก", - "loginWithExternalDesc": "เข้าสู่ระบบโดยใช้ผู้ให้บริการยืนยันตัวตนภายนอกที่คุณกำหนดค่าไว้", - "externalNotSupportedInElectron": "แอป Electron ยังไม่รองรับการตรวจสอบสิทธิ์จากภายนอก โปรดใช้เวอร์ชันเว็บสำหรับการเข้าสู่ระบบ OIDC", - "resetPasswordButton": "รีเซ็ตรหัสผ่าน", - "sendResetCode": "ส่งรหัสรีเซ็ต", - "resetCodeDesc": "ป้อนชื่อผู้ใช้ของคุณเพื่อรับรหัสรีเซ็ต mật khẩu รหัสจะถูกบันทึกไว้ในบันทึกของคอนเทนเนอร์ Docker", - "resetCode": "รีเซ็ตโค้ด", - "verifyCodeButton": "ยืนยันรหัส", - "enterResetCode": "ป้อนรหัส 6 หลักจากบันทึกคอนเทนเนอร์ Docker สำหรับผู้ใช้:", - "newPassword": "รหัสผ่านใหม่", - "confirmNewPassword": "ยืนยันรหัสผ่าน", - "enterNewPassword": "ป้อนรหัสผ่านใหม่สำหรับผู้ใช้:", - "signUp": "ลงทะเบียน", - "desktopApp": "แอปเดสก์ท็อป", - "loggingInToDesktopApp": "การเข้าสู่ระบบแอปพลิเคชันบนเดสก์ท็อป", - "loadingServer": "กำลังโหลดเซิร์ฟเวอร์...", - "dataLossWarning": "การรีเซ็ตรหัสผ่านด้วยวิธีนี้จะลบข้อมูลโฮสต์ SSH ข้อมูลประจำตัว และข้อมูลที่เข้ารหัสอื่นๆ ที่บันทึกไว้ทั้งหมด การกระทำนี้ไม่สามารถย้อนกลับได้ โปรดใช้วิธีนี้เฉพาะในกรณีที่คุณลืมรหัสผ่านและไม่ได้เข้าสู่ระบบเท่านั้น", - "authenticationDisabled": "การตรวจสอบสิทธิ์ถูกปิดใช้งาน", - "authenticationDisabledDesc": "ขณะนี้วิธีการยืนยันตัวตนทั้งหมดถูกปิดใช้งาน โปรดติดต่อผู้ดูแลระบบของคุณ", - "attemptsRemaining": "เหลือการลองอีก {{count}} ครั้ง" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "ตรวจสอบคีย์โฮสต์ SSH", - "keyChangedWarning": "คีย์โฮสต์ SSH เปลี่ยนแล้ว", - "firstConnectionTitle": "นี่เป็นครั้งแรกที่เชื่อมต่อกับโฮสต์นี้", - "firstConnectionDescription": "ไม่สามารถตรวจสอบความถูกต้องของโฮสต์นี้ได้ โปรดตรวจสอบว่าลายนิ้วมือตรงกับที่คุณคาดหวังหรือไม่", - "keyChangedDescription": "รหัสโฮสต์ของเซิร์ฟเวอร์นี้มีการเปลี่ยนแปลงนับตั้งแต่การเชื่อมต่อครั้งล่าสุดของคุณ ซึ่งอาจบ่งชี้ถึงปัญหาด้านความปลอดภัย", - "previousKey": "คีย์ก่อนหน้า", - "newFingerprint": "ลายนิ้วมือใหม่", - "fingerprint": "ลายนิ้วมือ", - "verifyInstructions": "หากคุณเชื่อถือโฮสต์นี้ โปรดคลิก ยอมรับ เพื่อดำเนินการต่อและบันทึกข้อมูลนี้สำหรับการเชื่อมต่อในอนาคต", - "securityWarning": "คำเตือนด้านความปลอดภัย", - "acceptAndContinue": "ยอมรับและดำเนินการต่อ", - "acceptNewKey": "ยอมรับรหัสใหม่และดำเนินการต่อ" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "ไม่สามารถเชื่อมต่อกับฐานข้อมูลได้", - "unknownError": "ข้อผิดพลาดที่ไม่ทราบสาเหตุ", - "loginFailed": "การเข้าสู่ระบบล้มเหลว", - "failedPasswordReset": "ไม่สามารถเริ่มการรีเซ็ตรหัสผ่านได้", - "failedVerifyCode": "ไม่สามารถตรวจสอบรหัสรีเซ็ตได้", - "failedCompleteReset": "การรีเซ็ตรหัสผ่านล้มเหลว", - "invalidTotpCode": "รหัส TOTP ไม่ถูกต้อง", - "failedOidcLogin": "ไม่สามารถเริ่มต้นการเข้าสู่ระบบ OIDC ได้", - "silentSigninOidcUnavailable": "มีการร้องขอการเข้าสู่ระบบแบบเงียบ แต่ไม่สามารถเข้าสู่ระบบ OIDC ได้", - "failedUserInfo": "ไม่สามารถดึงข้อมูลผู้ใช้ได้หลังจากเข้าสู่ระบบ", - "oidcAuthFailed": "การตรวจสอบสิทธิ์ OIDC ล้มเหลว", - "invalidAuthUrl": "ได้รับ URL การอนุญาตที่ไม่ถูกต้องจากแบ็กเอนด์", - "requiredField": "ช่องนี้จำเป็นต้องกรอก", - "minLength": "ความยาวขั้นต่ำคือ {{min}}", - "passwordMismatch": "รหัสผ่านไม่ตรงกัน", - "passwordLoginDisabled": "ขณะนี้การเข้าสู่ระบบด้วยชื่อผู้ใช้/รหัสผ่านถูกปิดใช้งานอยู่", - "sessionExpired": "เซッションหมดอายุแล้ว โปรดเข้าสู่ระบบอีกครั้ง", - "totpRateLimited": "จำกัดจำนวนครั้ง: พยายามยืนยัน TOTP มากเกินไป โปรดลองใหม่อีกครั้งในภายหลัง", - "totpRateLimitedWithTime": "จำกัดจำนวนครั้ง: พยายามยืนยัน TOTP มากเกินไป โปรดรอ {{time}} วินาทีก่อนลองอีกครั้ง", - "resetCodeRateLimited": "จำกัดจำนวนครั้งในการยืนยัน: มีการพยายามยืนยันมากเกินไป โปรดลองใหม่อีกครั้งในภายหลัง", - "resetCodeRateLimitedWithTime": "จำกัดจำนวนครั้ง: มีการพยายามยืนยันมากเกินไป โปรดรอ {{time}} วินาทีก่อนลองอีกครั้ง", - "authTokenSaveFailed": "ไม่สามารถบันทึกโทเค็นการตรวจสอบสิทธิ์ได้", - "failedToLoadServer": "ไม่สามารถโหลดเซิร์ฟเวอร์ได้" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "ขณะนี้ผู้ดูแลระบบได้ปิดใช้งานการลงทะเบียนบัญชีใหม่แล้ว โปรดเข้าสู่ระบบหรือติดต่อผู้ดูแลระบบ", - "userNotAllowed": "บัญชีของคุณไม่ได้รับอนุญาตให้ลงทะเบียน โปรดติดต่อผู้ดูแลระบบ", - "databaseConnectionFailed": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ฐานข้อมูลได้", - "resetCodeSent": "รหัสรีเซ็ตถูกส่งไปยังบันทึกของ Docker แล้ว", - "codeVerified": "ตรวจสอบรหัสสำเร็จแล้ว", - "passwordResetSuccess": "การรีเซ็ตรหัสผ่านสำเร็จแล้ว", - "loginSuccess": "เข้าสู่ระบบสำเร็จ", - "registrationSuccess": "การลงทะเบียนสำเร็จ" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "อุโมงค์เดสก์ท็อปภายในเครื่องที่กำหนดเป้าหมายไปยังโฮสต์ SSH ที่กำหนดค่าไว้", - "c2sTunnelPresets": "ค่าที่ตั้งไว้ล่วงหน้าสำหรับอุโมงค์ไคลเอ็นต์", - "c2sTunnelPresetsDesc": "บันทึกรายการอุโมงค์ภายในเครื่องของไคลเอนต์เดสก์ท็อปนี้เป็นค่าที่ตั้งไว้ล่วงหน้าของเซิร์ฟเวอร์ที่มีชื่อ หรือโหลดค่าที่ตั้งไว้ล่วงหน้ากลับไปยังไคลเอนต์นี้", - "c2sTunnelPresetsUnavailable": "การตั้งค่าล่วงหน้าสำหรับอุโมงค์ไคลเอ็นต์มีให้ใช้งานเฉพาะในไคลเอ็นต์เดสก์ท็อปเท่านั้น", - "c2sPresetName": "ชื่อพรีเซ็ต", - "c2sPresetNamePlaceholder": "ชื่อการตั้งค่าล่วงหน้าของลูกค้า", - "c2sPresetToLoad": "ตั้งค่าล่วงหน้าเพื่อโหลด", - "c2sNoPresetSelected": "ไม่ได้เลือกค่าที่ตั้งไว้ล่วงหน้า", - "c2sNoPresets": "ไม่มีการบันทึกค่าที่ตั้งไว้ล่วงหน้า", - "c2sLoadPreset": "โหลด", - "c2sCurrentLocalConfig": "{{count}} อุโมงค์ไคลเอ็นต์ภายในเครื่องที่กำหนดค่าไว้บนเดสก์ท็อปนี้", - "c2sPresetSyncNote": "ค่าที่ตั้งไว้ล่วงหน้าเป็นภาพรวมที่ถูกกำหนดไว้อย่างชัดเจน การโหลดค่าใดค่าหนึ่งจะแทนที่รายการอุโมงค์ไคลเอ็นต์ในเครื่องของไคลเอ็นต์เดสก์ท็อปนี้", - "c2sPresetSaved": "บันทึกการตั้งค่าอุโมงค์ไคลเอ็นต์ไว้แล้ว", - "c2sPresetLoaded": "โหลดค่าที่ตั้งไว้ล่วงหน้าของอุโมงค์ไคลเอ็นต์ในเครื่องแล้ว", - "c2sPresetRenamed": "เปลี่ยนชื่อพรีเซ็ตอุโมงค์ไคลเอ็นต์แล้ว", - "c2sPresetDeleted": "ลบการตั้งค่าล่วงหน้าของอุโมงค์ไคลเอ็นต์แล้ว", - "c2sPresetLoadError": "ไม่สามารถโหลดค่าที่ตั้งไว้ล่วงหน้าของอุโมงค์ไคลเอ็นต์ได้" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "ภาษา", - "keyPassword": "รหัสผ่านสำคัญ", - "pastePrivateKey": "วางรหัสส่วนตัวของคุณที่นี่...", - "localListenerHost": "127.0.0.1 (ฟังในพื้นที่ของคุณ)", - "localTargetHost": "127.0.0.1 (เป้าหมายบนคอมพิวเตอร์เครื่องนี้)", - "socksListenerHost": "127.0.0.1 (ผู้ฟัง SOCKS)", - "enterPassword": "ป้อนรหัสผ่านของคุณ", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "แดชบอร์ด", - "loading": "กำลังโหลดแดชบอร์ด...", - "github": "กิตฮับ", - "support": "สนับสนุน", - "discord": "ดิสคอร์ด", - "serverOverview": "ภาพรวมเซิร์ฟเวอร์", - "version": "เวอร์ชั่น", - "upToDate": "อัปเดตล่าสุด", - "updateAvailable": "มีการอัปเดตแล้ว", - "beta": "เบต้า", - "uptime": "เวลาใช้งาน", - "database": "ฐานข้อมูล", - "healthy": "สุขภาพดี", - "error": "ข้อผิดพลาด", - "totalHosts": "โฮสต์ทั้งหมด", - "totalTunnels": "อุโมงค์ทั้งหมด", - "totalCredentials": "ข้อมูลประจำตัวทั้งหมด", - "recentActivity": "กิจกรรมล่าสุด", - "reset": "รีเซ็ต", - "loadingRecentActivity": "กำลังโหลดกิจกรรมล่าสุด...", - "noRecentActivity": "ไม่มีกิจกรรมล่าสุด", - "quickActions": "การดำเนินการด่วน", - "addHost": "เพิ่มโฮสต์", - "addCredential": "เพิ่มข้อมูลรับรอง", - "adminSettings": "การตั้งค่าผู้ดูแลระบบ", - "userProfile": "โปรไฟล์ผู้ใช้", - "serverStats": "สถิติเซิร์ฟเวอร์", - "loadingServerStats": "กำลังโหลดข้อมูลสถิติเซิร์ฟเวอร์...", - "noServerData": "ไม่มีข้อมูลเซิร์ฟเวอร์", - "cpu": "ซีพียู", - "ram": "แรม", - "customizeLayout": "ปรับแต่งแดชบอร์ด", - "dashboardSettings": "การตั้งค่าแดชบอร์ด", - "enableDisableCards": "เปิดใช้งาน/ปิดใช้งานการ์ด", - "resetLayout": "รีเซ็ตเป็นค่าเริ่มต้น", - "serverOverviewCard": "ภาพรวมเซิร์ฟเวอร์", - "recentActivityCard": "กิจกรรมล่าสุด", - "networkGraphCard": "กราฟเครือข่าย", - "networkGraph": "กราฟเครือข่าย", - "quickActionsCard": "การดำเนินการด่วน", - "serverStatsCard": "สถิติเซิร์ฟเวอร์", - "panelMain": "หลัก", - "panelSide": "ด้านข้าง", - "justNow": "เมื่อสักครู่นี้เอง" + "title": "Dashboard", + "loading": "Loading dashboard...", + "github": "GitHub", + "support": "Support", + "discord": "Discord", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "มั่นคง", - "hostsOnline": "โฮสต์ออนไลน์", - "activeTunnels": "อุโมงค์ที่ใช้งานอยู่", - "registerNewServer": "ลงทะเบียนเซิร์ฟเวอร์ใหม่", - "storeSshKeysOrPasswords": "จัดเก็บคีย์ SSH หรือรหัสผ่าน", - "manageUsersAndRoles": "จัดการผู้ใช้และบทบาท", - "manageYourAccount": "จัดการบัญชีของคุณ", - "hostStatus": "สถานะโฮสต์", - "noHostsConfigured": "ไม่มีการกำหนดค่าโฮสต์", - "online": "ออนไลน์", - "offline": "ออฟไลน์", - "onlineLower": "ออนไลน์", - "nodes": "โหนด {{count}}", - "add": "เพิ่ม:", - "commandPalette": "พาเลทคำสั่ง", - "done": "เสร็จแล้ว", - "editModeInstructions": "ลากการ์ดเพื่อจัดเรียงลำดับใหม่ · ลากเส้นแบ่งคอลัมน์เพื่อปรับขนาดคอลัมน์ · ลากขอบด้านล่างของการ์ดเพื่อปรับขนาดความสูง · ลากไอคอนถังขยะเพื่อลบออก", - "empty": "ว่างเปล่า", - "clear": "ชัดเจน" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "เพิ่มโฮสต์", - "addGroup": "เพิ่มกลุ่ม", - "addLink": "เพิ่มลิงก์", - "zoomIn": "ซูมเข้า", - "zoomOut": "ซูมออก", - "resetView": "รีเซ็ตมุมมอง", - "selectHost": "เลือกโฮสต์", - "chooseHost": "เลือกเจ้าภาพ...", - "parentGroup": "กลุ่มผู้ปกครอง", - "noGroup": "ไม่มีกลุ่ม", - "groupName": "ชื่อกลุ่ม", - "color": "สี", - "source": "แหล่งที่มา", - "target": "เป้า", - "moveToGroup": "ย้ายไปยังกลุ่ม", - "selectGroup": "เลือกกลุ่ม...", - "addConnection": "เพิ่มการเชื่อมต่อ", - "hostDetails": "รายละเอียดโฮสต์", - "removeFromGroup": "ลบออกจากกลุ่ม", - "addHostHere": "เพิ่มโฮสต์ที่นี่", - "editGroup": "แก้ไขกลุ่ม", - "delete": "ลบ", - "add": "เพิ่ม", - "create": "สร้าง", - "move": "เคลื่อนไหว", - "connect": "เชื่อมต่อ", - "createGroup": "สร้างกลุ่ม", - "selectSourcePlaceholder": "เลือกแหล่งที่มา...", - "selectTargetPlaceholder": "เลือกเป้าหมาย...", - "invalidFile": "ไฟล์ไม่ถูกต้อง", - "hostAlreadyExists": "โฮสต์อยู่ในโทโพโลยีแล้ว", - "connectionExists": "การเชื่อมต่อมีอยู่แล้ว", - "unknown": "ไม่ทราบ", - "name": "ชื่อ", - "ip": "ไอพี", - "status": "สถานะ", - "failedToAddNode": "ไม่สามารถเพิ่มโหนดได้", - "sourceDifferentFromTarget": "แหล่งที่มาและเป้าหมายต้องแตกต่างกัน", - "exportJSON": "ส่งออก JSON", - "importJSON": "นำเข้า JSON", - "terminal": "เทอร์มินัล", - "fileManager": "ตัวจัดการไฟล์", - "tunnel": "อุโมงค์", - "docker": "ด็อกเกอร์", - "serverStats": "สถิติเซิร์ฟเวอร์", - "noNodes": "ยังไม่มีโหนด" + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker ไม่ได้เปิดใช้งานสำหรับโฮสต์นี้", - "validating": "กำลังตรวจสอบความถูกต้องของ Docker...", - "connecting": "กำลังเชื่อมต่อ...", - "error": "ข้อผิดพลาด", - "version": "ด็อกเกอร์ {{version}}", - "connectionFailed": "ไม่สามารถเชื่อมต่อกับ Docker ได้", - "containerStarted": "คอนเทนเนอร์ {{name}} เริ่มทำงานแล้ว", - "failedToStartContainer": "ไม่สามารถเริ่มต้นคอนเทนเนอร์ได้ {{name}}", - "containerStopped": "คอนเทนเนอร์ {{name}} หยุดทำงานแล้ว", - "failedToStopContainer": "ไม่สามารถหยุดคอนเทนเนอร์ได้ {{name}}", - "containerRestarted": "คอนเทนเนอร์ {{name}} รีสตาร์ทแล้ว", - "failedToRestartContainer": "ไม่สามารถรีสตาร์ทคอนเทนเนอร์ได้ {{name}}", - "containerPaused": "คอนเทนเนอร์ {{name}} หยุดชั่วคราว", - "containerUnpaused": "คอนเทนเนอร์ {{name}} ยกเลิกการหยุดชั่วคราว", - "failedToTogglePauseContainer": "ไม่สามารถสลับสถานะหยุดชั่วคราวสำหรับคอนเทนเนอร์ {{name}} ได้", - "containerRemoved": "คอนเทนเนอร์ {{name}} ถูกลบออกแล้ว", - "failedToRemoveContainer": "ไม่สามารถลบคอนเทนเนอร์ {{name}} ได้", - "image": "ภาพ", - "ports": "ท่าเรือ", - "noPorts": "ไม่มีพอร์ต", - "start": "เริ่ม", - "confirmRemoveContainer": "คุณแน่ใจหรือไม่ว่าต้องการลบคอนเทนเนอร์ '{{name}}'? การกระทำนี้ไม่สามารถย้อนกลับได้", - "runningContainerWarning": "คำเตือน: ขณะนี้คอนเทนเนอร์นี้กำลังทำงานอยู่ การลบคอนเทนเนอร์นี้จะหยุดการทำงานของคอนเทนเนอร์ก่อน", - "loadingContainers": "กำลังขนถ่ายตู้คอนเทนเนอร์...", - "manager": "ตัวจัดการ Docker", - "autoRefresh": "รีเฟรชอัตโนมัติ", - "timestamps": "ไทม์สแตมป์", - "lines": "เส้น", - "filterLogs": "กรองบันทึก...", - "refresh": "รีเฟรช", - "download": "ดาวน์โหลด", - "clear": "ชัดเจน", - "logsDownloaded": "ดาวน์โหลดบันทึกข้อมูลสำเร็จแล้ว", - "last50": "50 ครั้งสุดท้าย", - "last100": "100 ปีที่แล้ว", - "last500": "500 ครั้งสุดท้าย", - "last1000": "1000 ครั้งสุดท้าย", - "allLogs": "บันทึกทั้งหมด", - "noLogsMatching": "ไม่พบบันทึกใดที่ตรงกับ \"{{query}}\"", - "noLogsAvailable": "ไม่มีบันทึกข้อมูล", - "noContainersFound": "ไม่พบภาชนะบรรจุใดๆ", - "noContainersFoundHint": "ไม่มีคอนเทนเนอร์ Docker ให้บริการบนโฮสต์นี้", - "searchPlaceholder": "ค้นหาตู้คอนเทนเนอร์...", - "allStatuses": "สถานะทั้งหมด", - "stateRunning": "วิ่ง", - "statePaused": "หยุดชั่วคราว", - "stateExited": "ออกแล้ว", - "stateRestarting": "เริ่มใหม่", - "noContainersMatchFilters": "ไม่มีภาชนะใดตรงกับตัวกรองของคุณ", - "noContainersMatchFiltersHint": "ลองปรับเกณฑ์การค้นหาหรือตัวกรองของคุณดู", - "failedToFetchStats": "ไม่สามารถดึงข้อมูลสถิติของคอนเทนเนอร์ได้", - "containerNotRunning": "คอนเทนเนอร์ไม่ทำงาน", - "startContainerToViewStats": "เริ่มคอนเทนเนอร์เพื่อดูสถิติ", - "loadingStats": "กำลังโหลดสถิติ...", - "errorLoadingStats": "ข้อผิดพลาดในการโหลดสถิติ", - "noStatsAvailable": "ไม่มีสถิติให้ดู", - "cpuUsage": "การใช้งาน CPU", - "current": "ปัจจุบัน", - "memoryUsage": "การใช้งานหน่วยความจำ", - "networkIo": "อินพุต/เอาต์พุตเครือข่าย", - "input": "ป้อนข้อมูล", - "output": "เอาต์พุต", - "blockIo": "บล็อก I/O", - "read": "อ่าน", - "write": "เขียน", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "ข้อมูลเกี่ยวกับตู้คอนเทนเนอร์", - "name": "ชื่อ", - "id": "รหัสประจำตัว", - "state": "สถานะ", - "containerMustBeRunning": "ต้องเปิดใช้งานคอนเทนเนอร์ก่อนจึงจะสามารถเข้าถึงคอนโซลได้", - "verificationCodePrompt": "ป้อนรหัสยืนยัน", - "totpVerificationFailed": "การตรวจสอบ TOTP ล้มเหลว โปรดลองอีกครั้ง", - "warpgateVerificationFailed": "การตรวจสอบสิทธิ์ผ่านวาร์ปไม่สำเร็จ โปรดลองอีกครั้ง", - "connectedTo": "เชื่อมต่อกับ {{containerName}}", - "disconnected": "ตัดการเชื่อมต่อ", - "consoleError": "ข้อผิดพลาดของคอนโซล", - "errorMessage": "ข้อผิดพลาด: {{message}}", - "failedToConnect": "ไม่สามารถเชื่อมต่อกับคอนเทนเนอร์ได้", - "console": "คอนโซล", - "selectShell": "เลือกเปลือกหอย", - "bash": "ทุบตี", - "sh": "ช", - "ash": "เถ้า", - "connect": "เชื่อมต่อ", - "disconnect": "ตัดการเชื่อมต่อ", - "notConnected": "ไม่ได้เชื่อมต่อ", - "clickToConnect": "คลิกเชื่อมต่อเพื่อเริ่มเซสชันเชลล์", - "connectingTo": "กำลังเชื่อมต่อกับ {{containerName}}...", - "containerNotFound": "ไม่พบคอนเทนเนอร์", - "backToList": "กลับสู่รายการ", - "logs": "บันทึก", - "stats": "สถิติ", - "consoleTab": "คอนโซล", - "startContainerToAccess": "เริ่มคอนเทนเนอร์เพื่อเข้าถึงคอนโซล" + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "ทั่วไป", - "sectionOidc": "โอไอดีซี", - "sectionUsers": "ผู้ใช้", - "sectionSessions": "การประชุม", - "sectionRoles": "บทบาท", - "sectionDatabase": "ฐานข้อมูล", - "sectionApiKeys": "คีย์ API", - "allowRegistration": "อนุญาตให้ผู้ใช้ลงทะเบียน", - "allowRegistrationDesc": "อนุญาตให้ผู้ใช้ใหม่ลงทะเบียนด้วยตนเอง", - "allowPasswordLogin": "อนุญาตให้เข้าสู่ระบบด้วยรหัสผ่าน", - "allowPasswordLoginDesc": "เข้าสู่ระบบด้วยชื่อผู้ใช้/รหัสผ่าน", - "oidcAutoProvision": "การจัดเตรียมอัตโนมัติของ OIDC", - "oidcAutoProvisionDesc": "สร้างบัญชีผู้ใช้ OIDC โดยอัตโนมัติ แม้ว่าจะปิดใช้งานการลงทะเบียนไว้ก็ตาม", - "allowPasswordReset": "อนุญาตให้รีเซ็ตรหัสผ่าน", - "allowPasswordResetDesc": "รีเซ็ตโค้ดผ่านบันทึก Docker", - "sessionTimeout": "หมดเวลาเซสชัน", - "hours": "ชั่วโมง", - "sessionTimeoutRange": "ขั้นต่ำ 1 ชั่วโมง · ขั้นสูงสุด 720 ชั่วโมง", - "monitoringDefaults": "การตรวจสอบค่าเริ่มต้น", - "statusCheck": "ตรวจสอบสถานะ", - "metrics": "ตัวชี้วัด", - "sec": "วินาที", - "logLevel": "ระดับบันทึก", - "enableGuacamole": "เปิดใช้งาน Guacamole", - "enableGuacamoleDesc": "การเข้าถึงเดสก์ท็อประยะไกลผ่าน RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "ตั้งค่า OpenID Connect สำหรับ SSO ช่องที่มีเครื่องหมาย * กำกับไว้เป็นช่องที่ต้องกรอก", - "oidcClientId": "รหัสลูกค้า", - "oidcClientSecret": "ความลับของลูกค้า", - "oidcAuthUrl": "URL การอนุญาต", - "oidcIssuerUrl": "URL ของผู้ออก", - "oidcTokenUrl": "URL โทเค็น", - "oidcUserIdentifier": "เส้นทางตัวระบุผู้ใช้", - "oidcDisplayName": "ชื่อที่แสดง เส้นทาง", - "oidcScopes": "กล้องส่องทางไกล", - "oidcUserinfoUrl": "แทนที่ URL ข้อมูลผู้ใช้", - "oidcAllowedUsers": "ผู้ใช้ที่ได้รับอนุญาต", - "oidcAllowedUsersDesc": "อีเมลหนึ่งฉบับต่อหนึ่งบรรทัด หากเว้นว่างไว้จะอนุญาตให้ใช้ทั้งหมด", - "removeOidc": "ลบ", - "usersCount": "ผู้ใช้ {{count}} คน", - "createUser": "สร้าง", - "newRole": "บทบาทใหม่", - "roleName": "ชื่อ", - "roleDisplayName": "ชื่อที่แสดง", - "roleDescription": "คำอธิบาย", - "rolesCount": "{{count}} บทบาท", - "createRole": "สร้าง", - "creating": "กำลังสร้าง...", - "exportDatabase": "ส่งออกฐานข้อมูล", - "exportDatabaseDesc": "ดาวน์โหลดไฟล์สำรองข้อมูลของโฮสต์ ข้อมูลประจำตัว และการตั้งค่าทั้งหมด", - "export": "ส่งออก", - "exporting": "กำลังส่งออก...", - "importDatabase": "นำเข้าฐานข้อมูล", - "importDatabaseDesc": "กู้คืนจากไฟล์สำรองข้อมูล .sqlite", - "importDatabaseSelected": "เลือกแล้ว: {{name}}", - "selectFile": "เลือกไฟล์", - "changeFile": "เปลี่ยน", - "import": "นำเข้า", - "importing": "กำลังนำเข้า...", - "apiKeysCount": "{{count}} คีย์", - "newApiKey": "คีย์ API ใหม่", - "apiKeyCreatedWarning": "สร้างรหัสแล้ว - คัดลอกรหัสนี้ไว้ จะไม่แสดงอีกต่อไป", - "apiKeyName": "ชื่อ", - "apiKeyUser": "ผู้ใช้", - "apiKeySelectUser": "เลือกผู้ใช้...", - "apiKeyExpiresAt": "หมดอายุเวลา", - "createKey": "สร้างคีย์", - "apiKeyNoExpiry": "ไม่มีวันหมดอายุ", - "revokedBadge": "เพิกถอน", - "authTypeDual": "การตรวจสอบสิทธิ์แบบคู่", - "authTypeOidc": "โอไอดีซี", - "authTypeLocal": "ท้องถิ่น", - "adminStatusAdministrator": "ผู้ดูแลระบบ", - "adminStatusRegularUser": "ผู้ใช้ทั่วไป", - "adminBadge": "ผู้ดูแลระบบ", - "systemBadge": "ระบบ", - "customBadge": "กำหนดเอง", - "youBadge": "คุณ", - "sessionsActive": "{{count}} ใช้งานอยู่", - "sessionActive": "ใช้งานอยู่: {{time}}", + "sectionGeneral": "General", + "sectionOidc": "OIDC", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", + "authTypeOidc": "OIDC", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "ทั้งหมด", - "revokeAllSessionsSuccess": "เซสชันทั้งหมดสำหรับผู้ใช้ถูกยกเลิกแล้ว", - "revokeAllSessionsFailed": "ไม่สามารถยกเลิกเซสชันได้", - "revokeSessionFailed": "ไม่สามารถยกเลิกเซสชันได้", - "addRole": "เพิ่มบทบาท", - "noCustomRoles": "ไม่มีการกำหนดบทบาทที่กำหนดเอง", - "removeRoleFailed": "ไม่สามารถลบบทบาทได้", - "assignRoleFailed": "ไม่สามารถกำหนดบทบาทได้", - "deleteRoleFailed": "ไม่สามารถลบบทบาทได้", - "userAdminAccess": "ผู้ดูแลระบบ", - "userAdminAccessDesc": "เข้าถึงการตั้งค่าผู้ดูแลระบบทั้งหมดได้อย่างเต็มที่", - "userRoles": "บทบาท", - "revokeAllUserSessions": "ยกเลิกเซสชันทั้งหมด", - "revokeAllUserSessionsDesc": "บังคับล็อกอินใหม่บนอุปกรณ์ทุกเครื่อง", - "revoke": "ถอน", - "deleteUserWarning": "การลบผู้ใช้นี้เป็นการลบถาวร", - "deleteUser": "ลบ {{username}}", - "deleting": "กำลังลบ...", - "deleteUserFailed": "ไม่สามารถลบผู้ใช้ได้", - "deleteUserSuccess": "ผู้ใช้ \"{{username}}\" ถูกลบ", - "deleteRoleSuccess": "บทบาท \"{{name}}\" ถูกลบ", - "revokeKeySuccess": "คีย์ \"{{name}}\" ถูกยกเลิก", - "revokeKeyFailed": "ไม่สามารถยกเลิกคีย์ได้", - "copiedToClipboard": "คัดลอกไปยังคลิปบอร์ดแล้ว", - "done": "เสร็จแล้ว", - "createUserTitle": "สร้างผู้ใช้", - "createUserDesc": "สร้างบัญชีผู้ใช้ภายในเครื่องใหม่", - "createUserUsername": "ชื่อผู้ใช้", - "createUserPassword": "รหัสผ่าน", - "createUserPasswordHint": "อย่างน้อย 6 ตัวอักษร", - "createUserEnterUsername": "ป้อนชื่อผู้ใช้", - "createUserEnterPassword": "ป้อนรหัสผ่าน", - "createUserSubmit": "สร้างผู้ใช้", - "editUserTitle": "จัดการผู้ใช้: {{username}}", - "editUserDesc": "แก้ไขบทบาท สถานะผู้ดูแลระบบ เซสชัน และการตั้งค่าบัญชี", - "editUserUsername": "ชื่อผู้ใช้", - "editUserAuthType": "ประเภทการตรวจสอบสิทธิ์", - "editUserAdminStatus": "สถานะผู้ดูแลระบบ", - "editUserUserId": "รหัสผู้ใช้", - "linkAccountTitle": "เชื่อมโยง OIDC กับบัญชีรหัสผ่าน", - "linkAccountDesc": "ผสานบัญชี OIDC {{username}} กับบัญชีโลคัลที่มีอยู่แล้ว", - "linkAccountWarningTitle": "สิ่งนี้จะ:", - "linkAccountEffect1": "ลบบัญชีที่ใช้เฉพาะ OIDC", - "linkAccountEffect2": "เพิ่มการเข้าสู่ระบบ OIDC ไปยังบัญชีเป้าหมาย", - "linkAccountEffect3": "อนุญาตให้เข้าสู่ระบบทั้งด้วย OIDC และรหัสผ่าน", - "linkAccountTargetUsername": "ชื่อผู้ใช้เป้าหมาย", - "linkAccountTargetPlaceholder": "ป้อนชื่อผู้ใช้บัญชีท้องถิ่นเพื่อเชื่อมโยง", - "linkAccounts": "เชื่อมโยงบัญชี", - "linkAccountSuccess": "บัญชี OIDC เชื่อมโยงกับ \"{{username}}\"", - "linkAccountFailed": "ไม่สามารถเชื่อมโยงบัญชี OIDC ได้", - "linkAccountInProgress": "กำลังเชื่อมโยง...", - "saving": "ประหยัด...", - "updateRegistrationFailed": "ไม่สามารถอัปเดตการตั้งค่าการลงทะเบียนได้", - "updatePasswordLoginFailed": "ไม่สามารถอัปเดตการตั้งค่าการเข้าสู่ระบบด้วยรหัสผ่านได้", - "updateOidcAutoProvisionFailed": "ไม่สามารถอัปเดตการตั้งค่าการจัดเตรียมอัตโนมัติของ OIDC ได้", - "updatePasswordResetFailed": "ไม่สามารถอัปเดตการตั้งค่าการรีเซ็ตรหัสผ่านได้", - "sessionTimeoutRange2": "เวลาหมดอายุของเซสชันต้องอยู่ระหว่าง 1 ถึง 720 ชั่วโมง", - "sessionTimeoutSaved": "บันทึกการหมดเวลาของเซสชันแล้ว", - "sessionTimeoutSaveFailed": "ไม่สามารถบันทึกเซสชันได้เนื่องจากหมดเวลา", - "monitoringIntervalInvalid": "ค่าช่วงเวลาไม่ถูกต้อง", - "monitoringSaved": "บันทึกการตั้งค่าการตรวจสอบแล้ว", - "monitoringSaveFailed": "ไม่สามารถบันทึกการตั้งค่าการตรวจสอบได้", - "guacamoleSaved": "บันทึกการตั้งค่า Guacamole แล้ว", - "guacamoleSaveFailed": "ไม่สามารถบันทึกการตั้งค่า Guacamole ได้", - "guacamoleUpdateFailed": "ไม่สามารถอัปเดตการตั้งค่า Guacamole ได้", - "logLevelUpdateFailed": "ไม่สามารถอัปเดตระดับการบันทึกได้", - "oidcSaved": "บันทึกการตั้งค่า OIDC แล้ว", - "oidcSaveFailed": "ไม่สามารถบันทึกการตั้งค่า OIDC ได้", - "oidcRemoved": "การกำหนดค่า OIDC ถูกลบออกแล้ว", - "oidcRemoveFailed": "ไม่สามารถลบการตั้งค่า OIDC ได้", - "createUserRequired": "จำเป็นต้องระบุชื่อผู้ใช้และรหัสผ่าน", - "createUserPasswordTooShort": "รหัสผ่านต้องมีความยาวอย่างน้อย 6 ตัวอักษร", - "createUserSuccess": "ผู้ใช้ \"{{username}}\" สร้าง", - "createUserFailed": "ไม่สามารถสร้างผู้ใช้ได้", - "updateAdminStatusFailed": "ไม่สามารถอัปเดตสถานะผู้ดูแลระบบได้", - "allSessionsRevoked": "เซสชันทั้งหมดถูกยกเลิก", - "revokeSessionsFailed": "ไม่สามารถยกเลิกเซสชันได้", - "createRoleRequired": "ต้องระบุชื่อและชื่อที่แสดง", - "createRoleSuccess": "บทบาท \"{{name}}\" ถูกสร้างขึ้น", - "createRoleFailed": "ไม่สามารถสร้างบทบาทได้", - "apiKeyNameRequired": "จำเป็นต้องระบุชื่อคีย์", - "apiKeyUserRequired": "ต้องระบุรหัสผู้ใช้", - "apiKeyCreatedSuccess": "สร้างคีย์ API \"{{name}}\" แล้ว", - "apiKeyCreateFailed": "ไม่สามารถสร้างคีย์ API ได้", - "exportSuccess": "การส่งออกฐานข้อมูลสำเร็จแล้ว", - "exportFailed": "การส่งออกฐานข้อมูลล้มเหลว", - "importSelectFile": "โปรดเลือกไฟล์ก่อน", - "importCompleted": "การนำเข้าเสร็จสมบูรณ์: นำเข้า {{total}} รายการ ข้าม {{skipped}} รายการ", - "importFailed": "การนำเข้าล้มเหลว: {{error}}", - "importError": "การนำเข้าฐานข้อมูลล้มเหลว" + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "เจ้าภาพ", - "hostPlaceholder": "192.168.1.1 หรือ example.com", - "portLabel": "ท่าเรือ", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "ชื่อผู้ใช้", - "usernamePlaceholder": "ชื่อผู้ใช้", - "authLabel": "การตรวจสอบสิทธิ์", - "passwordLabel": "รหัสผ่าน", - "passwordPlaceholder": "รหัสผ่าน", - "privateKeyLabel": "กุญแจส่วนตัว", - "privateKeyPlaceholder": "วางรหัสส่วนตัว...", - "credentialLabel": "ใบรับรอง", - "credentialPlaceholder": "เลือกข้อมูลรับรองที่บันทึกไว้", - "connectToTerminal": "เชื่อมต่อกับเทอร์มินัล", - "connectToFiles": "เชื่อมต่อกับไฟล์" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "ไม่ได้เลือกเทอร์มินัล", - "noTerminalSelectedHint": "เปิดแท็บเทอร์มินัล SSH เพื่อดูประวัติคำสั่ง", - "searchPlaceholder": "ประวัติการค้นหา...", - "clearAll": "ล้างทั้งหมด", - "noHistoryEntries": "ไม่มีรายการประวัติ", - "trackingDisabled": "การติดตามประวัติถูกปิดใช้งานแล้ว", - "trackingDisabledHint": "เปิดใช้งานการบันทึกคำสั่งในตั้งค่าโปรไฟล์ของคุณ" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "การบันทึกคีย์", - "recordToTerminals": "บันทึกไปยังเทอร์มินัล", - "selectAll": "ทั้งหมด", - "selectNone": "ไม่มี", - "noTerminalTabsOpen": "ไม่มีแท็บเทอร์มินัลเปิดอยู่", - "selectTerminalsAbove": "เลือกเทอร์มินัลด้านบน", - "broadcastInputPlaceholder": "พิมพ์ที่นี่เพื่อถ่ายทอดการกดแป้นพิมพ์...", - "stopRecording": "หยุดบันทึก", - "startRecording": "เริ่มบันทึก", - "settingsTitle": "การตั้งค่า", - "enableRightClickCopyPaste": "เปิดใช้งานการคัดลอก/วางโดยคลิกขวา" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "เค้าโครง", - "selectLayoutAbove": "เลือกรูปแบบด้านบน", - "selectLayoutHint": "เลือกจำนวนบานหน้าต่างที่จะแสดง", - "panesTitle": "บานหน้าต่าง", - "openTabsTitle": "แท็บที่เปิดอยู่", - "dragTabsHint": "ลากแท็บลงในบานหน้าต่างด้านบน หรือใช้ฟังก์ชันกำหนดด่วน", - "dropHere": "ปล่อยตรงนี้", - "emptyPane": "ว่างเปล่า", - "dashboard": "แดชบอร์ด", - "clearSplitScreen": "ล้างหน้าจอแยก", - "quickAssign": "มอบหมายอย่างรวดเร็ว", - "alreadyAssigned": "บานหน้าต่าง {{index}}", - "splitTab": "แท็บแยก", - "addToSplit": "เพิ่มลงในการแบ่ง", - "removeFromSplit": "ลบออกจากการแบ่ง", - "assignToPane": "กำหนดให้กับบานหน้าต่าง" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "สร้างโค้ดสั้น", - "createSnippetDescription": "สร้างส่วนย่อยคำสั่งใหม่เพื่อเรียกใช้งานอย่างรวดเร็ว", - "nameLabel": "ชื่อ", - "namePlaceholder": "เช่น รีสตาร์ท Nginx", - "descriptionLabel": "คำอธิบาย", - "descriptionPlaceholder": "คำอธิบายเพิ่มเติม (ไม่บังคับ)", - "optional": "ไม่จำเป็น", - "folderLabel": "โฟลเดอร์", - "noFolder": "ไม่มีโฟลเดอร์ (ไม่ได้จัดหมวดหมู่)", - "commandLabel": "สั่งการ", - "commandPlaceholder": "เช่น sudo systemctl restart nginx", - "cancel": "ยกเลิก", - "createSnippetButton": "สร้างโค้ดสั้น", - "createFolderTitle": "สร้างโฟลเดอร์", - "createFolderDescription": "จัดระเบียบข้อความย่อของคุณลงในโฟลเดอร์", - "folderNameLabel": "ชื่อโฟลเดอร์", - "folderNamePlaceholder": "เช่น คำสั่งระบบ, สคริปต์ Docker", - "folderColorLabel": "สีของโฟลเดอร์", - "folderIconLabel": "ไอคอนโฟลเดอร์", - "previewLabel": "ตัวอย่าง", - "folderNameFallback": "ชื่อโฟลเดอร์", - "createFolderButton": "สร้างโฟลเดอร์", - "targetTerminals": "เทอร์มินัลเป้าหมาย", - "selectAll": "ทั้งหมด", - "selectNone": "ไม่มี", - "noTerminalTabsOpen": "ไม่มีแท็บเทอร์มินัลเปิดอยู่", - "searchPlaceholder": "ตัวอย่างการค้นหา...", - "newSnippet": "โค้ดตัวอย่างใหม่", - "newFolder": "โฟลเดอร์ใหม่", - "run": "วิ่ง", - "noSnippetsInFolder": "ไม่มีไฟล์ตัวอย่างในโฟลเดอร์นี้", - "uncategorized": "ไม่มีหมวดหมู่", - "editSnippetTitle": "แก้ไขส่วนย่อย", - "editSnippetDescription": "อัปเดตส่วนคำสั่งนี้", - "saveSnippetButton": "บันทึกการเปลี่ยนแปลง", - "createSuccess": "สร้างโค้ดตัวอย่างสำเร็จแล้ว", - "createFailed": "ไม่สามารถสร้างโค้ดตัวอย่างได้", - "updateSuccess": "อัปเดตข้อมูลตัวอย่างสำเร็จแล้ว", - "updateFailed": "ไม่สามารถอัปเดตโค้ดตัวอย่างได้", - "deleteFailed": "ไม่สามารถลบส่วนย่อยได้", - "folderCreateSuccess": "สร้างโฟลเดอร์สำเร็จแล้ว", - "folderCreateFailed": "ไม่สามารถสร้างโฟลเดอร์ได้", - "editFolderTitle": "แก้ไขโฟลเดอร์", - "editFolderDescription": "เปลี่ยนชื่อหรือเปลี่ยนลักษณะของโฟลเดอร์นี้", - "saveFolderButton": "บันทึกการเปลี่ยนแปลง", - "editFolder": "แก้ไขโฟลเดอร์", - "deleteFolder": "ลบโฟลเดอร์", - "folderDeleteSuccess": "โฟลเดอร์ \"{{name}}\" ถูกลบแล้ว", - "folderDeleteFailed": "ไม่สามารถลบโฟลเดอร์ได้", - "folderEditSuccess": "อัปเดตโฟลเดอร์สำเร็จแล้ว", - "folderEditFailed": "ไม่สามารถอัปเดตโฟลเดอร์ได้", - "confirmRunMessage": "เรียกใช้ \"{{name}}\"?", - "confirmRunButton": "วิ่ง", - "runSuccess": "Ran \"{{name}}\" ในเทอร์มินัล {{count}} เครื่อง", - "copySuccess": "คัดลอก \"{{name}}\" ไปยังคลิปบอร์ด", - "shareTitle": "แชร์ตัวอย่าง", - "shareUser": "ผู้ใช้", - "shareRole": "บทบาท", - "selectUser": "เลือกผู้ใช้...", - "selectRole": "เลือกบทบาท...", - "shareSuccess": "แชร์โค้ดตัวอย่างสำเร็จแล้ว", - "shareFailed": "ไม่สามารถแชร์ส่วนย่อยได้", - "revokeSuccess": "สิทธิ์การเข้าถึงถูกเพิกถอน", - "revokeFailed": "ไม่สามารถเพิกถอนสิทธิ์การเข้าถึงได้", - "currentAccess": "การเข้าถึงปัจจุบัน", - "shareLoadError": "ไม่สามารถโหลดข้อมูลการแชร์ได้", - "loading": "กำลังโหลด...", - "close": "ปิด", - "reorderFailed": "ไม่สามารถบันทึกลำดับของโค้ดตัวอย่างได้" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "บัญชี", - "sectionAppearance": "รูปร่าง", - "sectionSecurity": "ความปลอดภัย", - "sectionApiKeys": "คีย์ API", - "sectionC2sTunnels": "อุโมงค์ C2S", - "usernameLabel": "ชื่อผู้ใช้", - "roleLabel": "บทบาท", - "roleAdministrator": "ผู้ดูแลระบบ", - "authMethodLabel": "วิธีการตรวจสอบสิทธิ์", - "authMethodLocal": "ท้องถิ่น", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "บน", - "twoFaOff": "ปิด", - "versionLabel": "เวอร์ชั่น", - "deleteAccount": "ลบบัญชีผู้ใช้", - "deleteAccountDescription": "ลบบัญชีของคุณอย่างถาวร", - "deleteButton": "ลบ", - "deleteAccountPermanent": "การกระทำนี้เป็นการกระทำถาวรและไม่สามารถยกเลิกได้", - "deleteAccountWarning": "เซสชัน โฮสต์ ข้อมูลประจำตัว และการตั้งค่าทั้งหมดจะถูกลบอย่างถาวร", - "confirmPasswordDeletePlaceholder": "ป้อนรหัสผ่านของคุณเพื่อยืนยัน", - "languageLabel": "ภาษา", - "themeLabel": "ธีม", - "fontSizeLabel": "ขนาดตัวอักษร", - "accentColorLabel": "สีเน้น", - "settingsTerminal": "เทอร์มินัล", - "commandAutocomplete": "การเติมคำสั่งอัตโนมัติ", - "commandAutocompleteDesc": "แสดงคำแนะนำอัตโนมัติขณะพิมพ์", - "historyTracking": "การติดตามประวัติ", - "historyTrackingDesc": "ติดตามคำสั่งเทอร์มินัล", - "syntaxHighlighting": "การเน้นไวยากรณ์", - "syntaxHighlightingDesc": "เน้นเอาต์พุตเทอร์มินัล", - "commandPalette": "พาเลทคำสั่ง", - "commandPaletteDesc": "เปิดใช้งานปุ่มลัดแป้นพิมพ์", - "reopenTabsOnLogin": "เปิดแท็บใหม่เมื่อเข้าสู่ระบบ", - "reopenTabsOnLoginDesc": "กู้คืนแท็บที่เปิดอยู่ของคุณเมื่อคุณเข้าสู่ระบบหรือรีเฟรชหน้าเว็บ แม้กระทั่งจากอุปกรณ์อื่น", - "confirmTabClose": "ยืนยันการปิดแท็บ", - "confirmTabCloseDesc": "ถามก่อนปิดแท็บเทอร์มินัล", - "settingsSidebar": "แถบด้านข้าง", - "showHostTags": "แสดงแท็กโฮสต์", - "showHostTagsDesc": "แสดงแท็กในรายการโฮสต์", - "hostTrayOnClick": "คลิกเพื่อขยายการดำเนินการของโฮสต์", - "hostTrayOnClickDesc": "แสดงปุ่มเชื่อมต่อเสมอ คลิกเพื่อขยายตัวเลือกการจัดการแทนการวางเมาส์เหนือปุ่ม", - "pinAppRail": "พินแอปเรล", - "pinAppRailDesc": "ควรตั้งค่าให้แถบด้านข้างซ้ายของแอปแสดงอยู่ตลอดเวลาโดยไม่ต้องขยายเมื่อวางเมาส์เหนือแถบ", - "settingsSnippets": "เศษเสี้ยว", - "foldersCollapsed": "โฟลเดอร์ถูกยุบ", - "foldersCollapsedDesc": "ยุบโฟลเดอร์โดยค่าเริ่มต้น", - "confirmExecution": "ยืนยันการดำเนินการ", - "confirmExecutionDesc": "ยืนยันก่อนเรียกใช้โค้ดตัวอย่าง", - "settingsUpdates": "การอัปเดต", - "disableUpdateChecks": "ปิดใช้งานการตรวจสอบการอัปเดต", - "disableUpdateChecksDesc": "หยุดตรวจสอบการอัปเดต", - "totpAuthenticator": "ตัวตรวจสอบสิทธิ์ TOTP", - "totpEnabled": "เปิดใช้งาน 2FA แล้ว", - "totpDisabled": "เพิ่มระบบรักษาความปลอดภัยในการเข้าสู่ระบบเพิ่มเติม", - "disable": "ปิดใช้งาน", - "enable": "เปิดใช้งาน", - "setupTotp": "ตั้งค่า TOTP", - "qrCode": "รหัส QR", - "totpInstructions": "สแกนคิวอาร์โค้ดหรือป้อนรหัสลับในแอปยืนยันตัวตนของคุณ จากนั้นป้อนรหัส 6 หลัก", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "ตรวจสอบ", - "changePassword": "เปลี่ยนรหัสผ่าน", - "currentPasswordLabel": "รหัสผ่านปัจจุบัน", - "currentPasswordPlaceholder": "รหัสผ่านปัจจุบัน", - "newPasswordLabel": "รหัสผ่านใหม่", - "newPasswordPlaceholder": "รหัสผ่านใหม่", - "confirmPasswordLabel": "ยืนยันรหัสผ่านใหม่", - "confirmPasswordPlaceholder": "ยืนยันรหัสผ่านใหม่", - "updatePassword": "อัปเดตรหัสผ่าน", - "createApiKeyTitle": "สร้างคีย์ API", - "createApiKeyDescription": "สร้างคีย์ API ใหม่สำหรับการเข้าถึงแบบโปรแกรม", - "apiKeyNameLabel": "ชื่อ", - "apiKeyNamePlaceholder": "เช่น CI Pipeline", - "expiryDateLabel": "วันหมดอายุ", - "optional": "ไม่จำเป็น", - "cancel": "ยกเลิก", - "createKey": "สร้างคีย์", - "apiKeyCount": "{{count}} คีย์", - "newKey": "กุญแจใหม่", - "noApiKeys": "ยังไม่มีคีย์ API ในขณะนี้", - "apiKeyActive": "คล่องแคล่ว", - "apiKeyUsageHint": "ใส่กุญแจของคุณลงไปด้วย", - "apiKeyUsageHintHeader": "ส่วนหัว", - "apiKeyPermissionsHint": "คีย์จะได้รับสิทธิ์การใช้งานจากผู้ใช้ที่สร้างคีย์นั้นขึ้นมา", - "roleUser": "ผู้ใช้", - "authMethodDual": "การตรวจสอบสิทธิ์แบบคู่", - "authMethodOidc": "โอไอดีซี", - "totpSetupFailed": "ไม่สามารถเริ่มการตั้งค่า TOTP ได้", - "totpEnter6Digits": "ป้อนรหัส 6 หลัก", - "totpEnabledSuccess": "เปิดใช้งานการตรวจสอบสิทธิ์แบบสองขั้นตอน", - "totpInvalidCode": "รหัสไม่ถูกต้อง โปรดลองอีกครั้ง", - "totpDisableInputRequired": "ป้อนรหัส TOTP หรือรหัสผ่านของคุณ", - "totpDisabledSuccess": "ปิดใช้งานการตรวจสอบสิทธิ์แบบสองขั้นตอน", - "totpDisableFailed": "ไม่สามารถปิดใช้งาน 2FA ได้", - "totpDisableTitle": "ปิดใช้งาน 2FA", - "totpDisablePlaceholder": "ป้อนรหัส TOTP หรือรหัสผ่าน", - "totpDisableConfirm": "ปิดใช้งาน 2FA", - "totpContinueVerify": "ดำเนินการตรวจสอบต่อไป", - "totpVerifyTitle": "ยืนยันรหัส", - "totpBackupTitle": "รหัสสำรอง", - "totpDownloadBackup": "ดาวน์โหลดรหัสสำรอง", - "done": "เสร็จแล้ว", - "secretCopied": "คัดลอกข้อมูลลับไปยังคลิปบอร์ดแล้ว", - "apiKeyNameRequired": "จำเป็นต้องระบุชื่อคีย์", - "apiKeyCreated": "สร้างคีย์ API \"{{name}}\" แล้ว", - "apiKeyCreateFailed": "ไม่สามารถสร้างคีย์ API ได้", - "apiKeyUser": "ผู้ใช้", - "apiKeyExpires": "หมดอายุ", - "apiKeyRevoked": "คีย์ API \"{{name}}\" ถูกยกเลิกแล้ว", - "apiKeyRevokeFailed": "ไม่สามารถยกเลิกคีย์ API ได้", - "passwordFieldsRequired": "จำเป็นต้องระบุรหัสผ่านปัจจุบันและรหัสผ่านใหม่", - "passwordMismatch": "รหัสผ่านไม่ตรงกัน", - "passwordTooShort": "รหัสผ่านต้องมีความยาวอย่างน้อย 6 ตัวอักษร", - "passwordUpdated": "รหัสผ่านได้รับการอัปเดตเรียบร้อยแล้ว", - "passwordUpdateFailed": "ไม่สามารถอัปเดตรหัสผ่านได้", - "deletePasswordRequired": "ต้องใช้รหัสผ่านในการลบบัญชีของคุณ", - "deleteFailed": "ไม่สามารถลบบัญชีได้", - "deleting": "กำลังลบ...", - "colorPickerTooltip": "เปิดตัวเลือกสี", - "themeSystem": "ระบบ", - "themeLight": "แสงสว่าง", - "themeDark": "มืด", - "themeDracula": "แดรกคิวลา", - "themeCatppuccin": "แคทปุชชิน", - "themeNord": "นอร์ด", - "themeSolarized": "โซลาไรซ์", - "themeTokyoNight": "โตเกียวไนท์", - "themeOneDark": "มืดหนึ่งเดียว", - "themeGruvbox": "กรูฟบ็อกซ์" + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", + "authMethodOidc": "OIDC", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", + "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/tr_TR.json b/src/ui/locales/translated/tr_TR.json index 5b583001..9bc5af32 100644 --- a/src/ui/locales/translated/tr_TR.json +++ b/src/ui/locales/translated/tr_TR.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Klasörler", - "folder": "Dosya", - "password": "Şifre", - "key": "Anahtar", - "sshPrivateKey": "SSH Özel Anahtarı", - "upload": "Yüklemek", - "keyPassword": "Anahtar Parolası", - "sshKey": "SSH Anahtarı", - "uploadPrivateKeyFile": "Özel Anahtar Dosyasını Yükle", - "searchCredentials": "Arama kimlik bilgileri...", - "addCredential": "Kimlik Bilgisi Ekle", - "caCertificate": "CA Sertifikası (-cert.pub)", - "caCertificateDescription": "İsteğe bağlı: CA tarafından imzalanmış sertifika dosyasını yükleyin veya yapıştırın (örneğin id_ed25519-cert.pub). SSH sunucunuz sertifika tabanlı yetkilendirme kullanıyorsa gereklidir.", - "uploadCertFile": "Yükle -cert.pub Dosyası", - "clearCert": "Temizlemek", - "certLoaded": "Sertifika yüklendi", - "certPublicKeyLabel": "CA Sertifikası", - "certTypeLabel": "Sertifika türü", - "pasteOrUploadCert": "-cert.pub sertifikasını yapıştırın veya yükleyin...", - "hasCaCert": "Kaliforniya Sertifikasına sahip.", - "noCaCert": "CA Sertifikası Yok" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Uyarılar yüklenemedi.", - "failedToDismissAlert": "Uyarıyı kapatma başarısız oldu." + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Sunucu Yapılandırması", - "description": "Termix sunucu URL'sini arka uç servislerinize bağlanacak şekilde yapılandırın.", - "serverUrl": "Sunucu URL'si", - "enterServerUrl": "Lütfen bir sunucu URL'si girin.", - "saveFailed": "Yapılandırma kaydedilemedi.", - "saveError": "Yapılandırma kaydedilirken hata oluştu.", - "saving": "Tasarruf...", - "saveConfig": "Yapılandırmayı Kaydet", - "helpText": "Termix sunucunuzun çalıştığı URL adresini girin (örneğin, http://localhost:30001 veya https://your-server.com).", - "changeServer": "Sunucuyu Değiştir", - "mustIncludeProtocol": "Sunucu URL'si http:// veya https:// ile başlamalıdır.", - "allowInvalidCertificate": "Geçersiz sertifikaya izin ver", - "allowInvalidCertificateDesc": "Yalnızca kendinden imzalı veya IP adresi sertifikalarına sahip, güvenilir, kendi sunucunuzda barındırdığınız sunucular için kullanın.", - "useEmbedded": "Yerel Sunucuyu Kullanın", - "embeddedDesc": "Termix'i yerleşik yerel sunucu ile çalıştırın (uzaktan sunucuya gerek yok).", - "embeddedConnecting": "Yerel sunucuya bağlanılıyor...", - "embeddedNotReady": "Yerel sunucu henüz hazır değil. Lütfen bir süre bekleyin ve tekrar deneyin.", - "localServer": "Yerel Sunucu" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Sürüm Kontrol Hatası", - "checkFailed": "Güncellemeleri kontrol etme başarısız oldu.", - "upToDate": "Uygulama güncel.", - "currentVersion": "Şu anda {{version}} sürümünü çalıştırıyorsunuz.", - "updateAvailable": "Güncelleme Mevcut", - "newVersionAvailable": "Yeni bir sürüm mevcut! Şu anda {{current}}sürümünü çalıştırıyorsunuz, ancak {{latest}} sürümü mevcut.", - "betaVersion": "Beta Sürümü", - "betaVersionDesc": "Şu anda çalıştırdığınız sürüm {{current}}, en son kararlı sürüm olan {{latest}}'den daha yenidir.", - "releasedOn": "{{date}} tarihinde yayınlandı", - "downloadUpdate": "Güncellemeyi İndir", - "checking": "Güncellemeler kontrol ediliyor...", - "checkUpdates": "Güncellemeleri kontrol edin", - "checkingUpdates": "Güncellemeler kontrol ediliyor...", - "updateRequired": "Güncelleme Gerekli" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Kapalı", - "minimize": "En aza indirge", - "online": "Çevrimiçi", - "offline": "Çevrimdışı", - "continue": "Devam etmek", - "maintenance": "Bakım", - "degraded": "Bozulmuş", - "error": "Hata", - "warning": "Uyarı", - "unsavedChanges": "Kaydedilmemiş değişiklikler", - "dismiss": "Azletmek", - "loading": "Yükleniyor...", - "optional": "İsteğe bağlı", - "connect": "Bağlamak", - "copied": "Kopyalandı", - "connecting": "Bağlanıyor...", - "updateAvailable": "Güncelleme Mevcut", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Yeni sekmede aç", - "noReleases": "Yayın Yok", - "updatesAndReleases": "Güncellemeler ve Yayınlar", - "newVersionAvailable": "Yeni bir sürüm ({{version}}) mevcuttur.", - "failedToFetchUpdateInfo": "Güncelleme bilgilerini alma işlemi başarısız oldu.", - "preRelease": "Ön yayın", - "noReleasesFound": "Yayınlanmış hiçbir dosya bulunamadı.", - "cancel": "İptal etmek", - "username": "Kullanıcı adı", - "login": "Giriş yapmak", - "register": "Kayıt olmak", - "password": "Şifre", - "confirmPassword": "Şifreyi Onayla", - "back": "Geri", - "save": "Kaydetmek", - "saving": "Tasarruf...", - "delete": "Silmek", - "rename": "Yeniden isimlendirmek", - "edit": "Düzenlemek", - "add": "Eklemek", - "confirm": "Onaylamak", - "no": "HAYIR", - "or": "VEYA", - "next": "Sonraki", - "previous": "Öncesi", - "refresh": "Yenile", - "language": "Dil", - "checking": "Kontrol ediliyor...", - "checkingDatabase": "Veritabanı bağlantısı kontrol ediliyor...", - "checkingAuthentication": "Kimlik doğrulama kontrol ediliyor...", - "backendReconnected": "Sunucu bağlantısı yeniden kuruldu", - "connectionDegraded": "Sunucu bağlantısı kesildi, kurtarılıyor…", - "reload": "Yeniden yükle", - "remove": "Kaldırmak", - "create": "Yaratmak", - "update": "Güncelleme", - "copy": "Kopyala", - "copyFailed": "Panoya kopyalama başarısız oldu.", - "maximize": "Maksimize et", - "restore": "Eski haline getirmek", - "of": "ile ilgili" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Ev", - "terminal": "terminal", - "docker": "Liman işçisi", - "tunnels": "Tüneller", - "fileManager": "Dosya Yöneticisi", - "serverStats": "Sunucu İstatistikleri", - "admin": "Yönetici", - "userProfile": "Kullanıcı Profili", - "splitScreen": "Bölünmüş Ekran", - "confirmClose": "Bu aktif oturumu kapatmak ister misiniz?", - "close": "Kapalı", - "cancel": "İptal etmek", - "sshManager": "SSH Yöneticisi", - "cannotSplitTab": "Bu sekmeyi bölemezsiniz.", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Şifreyi Kopyala", - "copySudoPassword": "Sudo Parolasını Kopyala", - "passwordCopied": "Parola panoya kopyalandı", - "noPasswordAvailable": "Şifre mevcut değil.", - "failedToCopyPassword": "Parola kopyalama başarısız oldu.", - "refreshTab": "Bağlantıyı yenile", - "openFileManager": "Dosya Yöneticisini Açın", - "dashboard": "Kontrol Paneli", - "networkGraph": "Ağ Grafiği", - "quickConnect": "Hızlı Bağlantı", - "sshTools": "SSH Araçları", - "history": "Tarih", - "hosts": "Ev sahipleri", - "snippets": "Kısa bölümler", - "hostManager": "Sunucu Yöneticisi", - "credentials": "Kimlik Bilgileri", - "connections": "Bağlantılar", - "roleAdministrator": "Yönetici", - "roleUser": "Kullanıcı" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Ev sahipleri", - "noHosts": "SSH Sunucusu Yok", - "retry": "Tekrar dene", - "refresh": "Yenile", - "optional": "İsteğe bağlı", - "downloadSample": "Örnek İndir", - "failedToDeleteHost": "{{name}} silinemedi", - "importSkipExisting": "İçe aktar (mevcut olanı atla)", - "connectionDetails": "Bağlantı Ayrıntıları", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Uzaktan Masaüstü", - "port": "Liman", - "username": "Kullanıcı adı", - "folder": "Dosya", - "tags": "Etiketler", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", "pin": "Pin", - "addHost": "Sunucu Ekle", - "editHost": "Sunucuyu Düzenle", - "cloneHost": "Klon Ana Bilgisayar", - "enableTerminal": "Terminali Etkinleştir", - "enableTunnel": "Tüneli Etkinleştir", - "enableFileManager": "Dosya Yöneticisini Etkinleştir", - "enableDocker": "Docker'ı etkinleştirin", - "defaultPath": "Varsayılan Yol", - "connection": "Bağlantı", - "upload": "Yüklemek", - "authentication": "Kimlik doğrulama", - "password": "Şifre", - "key": "Anahtar", - "credential": "Kimlik belgesi", - "none": "Hiçbiri", - "sshPrivateKey": "SSH Özel Anahtarı", - "keyType": "Anahtar Türü", - "uploadFile": "Dosya Yükle", - "tabGeneral": "Genel", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Tüneller", - "tabDocker": "Liman işçisi", - "tabFiles": "Dosyalar", - "tabStats": "İstatistikler", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Paylaşım", - "tabAuthentication": "Kimlik doğrulama", - "terminal": "terminal", - "tunnel": "Tünel", - "fileManager": "Dosya Yöneticisi", - "serverStats": "Sunucu İstatistikleri", - "status": "Durum", - "folderRenamed": "\"{{oldName}}\" klasörü \"{{newName}}\" olarak başarıyla yeniden adlandırıldı.", - "failedToRenameFolder": "Klasörü yeniden adlandırma başarısız oldu.", - "movedToFolder": "\"{{folder}} \" adresine taşındı", - "editHostTooltip": "Sunucuyu düzenle", - "statusChecks": "Durum Kontrolleri", - "metricsCollection": "Metriklerin Toplanması", - "metricsInterval": "Ölçüm Toplama Aralığı", - "metricsIntervalDesc": "Sunucu istatistikleri ne sıklıkla toplanmalı (5 saniye - 1 saat)?", - "behavior": "Davranış", - "themePreview": "Tema Önizlemesi", - "theme": "Tema", - "fontFamily": "Yazı Tipi Ailesi", - "fontSize": "Yazı Tipi Boyutu", - "letterSpacing": "Harf Aralığı", - "lineHeight": "Çizgi Yüksekliği", - "cursorStyle": "İmleç Stili", - "cursorBlink": "İmleç Yanıp Sönmesi", - "scrollbackBuffer": "Geri Kaydırma Tamponu", - "bellStyle": "Çan Stili", - "rightClickSelectsWord": "Sağ Tıklama Word Seçer", - "fastScrollModifier": "Hızlı Kaydırma Değiştirici", - "fastScrollSensitivity": "Hızlı Kaydırma Hassasiyeti", - "sshAgentForwarding": "SSH Aracısı Yönlendirmesi", - "backspaceMode": "Geri Silme Modu", - "startupSnippet": "Başlangıç Kodu Parçası", - "selectSnippet": "Kod parçasını seçin", - "forceKeyboardInteractive": "Klavyeyle Etkileşimi Zorla", - "overrideCredentialUsername": "Kimlik Bilgisi Kullanıcı Adını Geçersiz Kıl", - "overrideCredentialUsernameDesc": "Kimlik bilgilerinin kullanıcı adı yerine yukarıda belirtilen kullanıcı adını kullanın.", - "jumpHostChain": "Ana Bilgisayar Zincirini Atla", - "portKnocking": "Liman Vuruşu", - "addKnock": "Bağlantı Noktası Ekle", - "addProxyNode": "Düğüm Ekle", - "proxyNode": "Proxy Düğümü", - "proxyType": "Vekil Türü", - "quickActions": "Hızlı İşlemler", - "sudoPasswordAutoFill": "Sudo Parolası Otomatik Doldurma", - "sudoPassword": "Sudo Parolası", - "keepaliveInterval": "Bağlantıyı Sürdürme Aralığı (ms)", - "moshCommand": "MOSH Komutu", - "environmentVariables": "Çevresel Değişkenler", - "addVariable": "Değişken Ekle", - "docker": "Liman işçisi", - "copyTerminalUrl": "Terminal URL'sini Kopyala", - "copyFileManagerUrl": "Dosya Yöneticisi URL'sini Kopyala", - "copyRemoteDesktopUrl": "Uzak Masaüstü URL'sini Kopyala", - "failedToConnect": "Konsola bağlanılamadı.", - "connect": "Bağlamak", - "disconnect": "Bağlantıyı kes", - "start": "Başlangıç", - "enableStatusCheck": "Durum Kontrolünü Etkinleştir", - "enableMetrics": "Ölçümleri Etkinleştir", - "bulkUpdateFailed": "Toplu güncelleme başarısız oldu", - "selectAll": "Tümünü Seç", - "deselectAll": "Tümünü Seçimi Kaldır", - "protocols": "Protokoller", - "secureShell": "Güvenli Kabuk", - "virtualNetwork": "Sanal Ağ", - "unencryptedShell": "Şifrelenmemiş kabuk", - "addressIp": "Adres / IP", - "friendlyName": "Dostça İsim", - "folderAndAdvanced": "Klasör ve Gelişmiş", - "privateNotes": "Özel Notlar", - "privateNotesPlaceholder": "Bu sunucuyla ilgili detaylar...", - "pinToTop": "En Üste Sabitle", - "pinToTopDesc": "Bu sunucuyu her zaman listenin en üstünde göster.", - "portKnockingSequence": "Liman Vurma Dizisi", - "addKnockBtn": "Vurma Ekle", - "noPortKnocking": "Herhangi bir port vuruşu yapılandırılmadı.", - "knockPort": "Knock Limanı", - "protocol": "Protokol", - "delayAfterMs": "Gecikme Sonrası (ms)", - "useSocks5Proxy": "SOCKS5 Proxy kullanın", - "useSocks5ProxyDesc": "Bağlantıyı bir proxy sunucusu üzerinden yönlendirin.", - "proxyHost": "Proxy Sunucusu", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", "proxyPort": "Proxy Port", - "proxyUsername": "Proxy Kullanıcı Adı", - "proxyPassword": "Proxy Parolası", - "proxySingleMode": "Tek Vekil", - "proxyChainMode": "Proxy Zinciri", - "you": "Sen", - "jumpHostChainLabel": "Ana Bilgisayar Zincirini Atla", - "addJumpBtn": "Atlama Ekle", - "noJumpHosts": "Hiçbir atlama sunucusu yapılandırılmamış.", - "selectAServer": "Bir sunucu seçin...", - "sshPort": "SSH Bağlantı Noktası", - "authMethod": "Kimlik Doğrulama Yöntemi", - "storedCredential": "Kaydedilmiş Kimlik Bilgileri", - "selectACredential": "Bir yeterlilik belgesi seçin...", - "keyTypeLabel": "Anahtar Türü", - "keyTypeAuto": "Otomatik Algılama", - "keyPasteTab": "Yapıştır", - "keyUploadTab": "Yüklemek", - "keyFileLoaded": "Anahtar dosyası yüklendi", - "keyUploadClick": ".pem / .key / .ppk dosyalarını yüklemek için tıklayın.", - "clearKey": "Anahtarı temizle", - "keySaved": "SSH anahtarı kaydedildi", - "keyReplaceNotice": "Eski anahtarı değiştirmek için aşağıya yeni bir anahtar yapıştırın.", - "keyPassphraseSaved": "Parola kaydedildi, değiştirmek için girin.", - "replaceKey": "Anahtarı değiştirin", - "forceKeyboardInteractiveLabel": "Klavye Etkileşimini Zorla", - "forceKeyboardInteractiveShortDesc": "Tuşlar mevcut olsa bile manuel parola girişini zorunlu kıl", - "terminalAppearance": "Terminal Görünüm", - "colorTheme": "Renk Teması", - "fontFamilyLabel": "Yazı Tipi Ailesi", - "fontSizeLabel": "Yazı Tipi Boyutu", - "cursorStyleLabel": "İmleç Stili", - "letterSpacingPx": "Harf Aralığı (px)", - "lineHeightLabel": "Çizgi Yüksekliği", - "bellStyleLabel": "Çan Stili", - "backspaceModeLabel": "Geri Silme Modu", - "cursorBlinking": "İmleç Yanıp Sönüyor", - "cursorBlinkingDesc": "Terminal imleci için yanıp sönme animasyonunu etkinleştirin.", - "rightClickSelectsWordLabel": "Sağ tıklama Word'ü seçer", - "rightClickSelectsWordShortDesc": "İmlecin altındaki kelimeyi sağ tıklayarak seçin.", - "behaviorAndAdvanced": "Davranış ve İleri Düzey", - "scrollbackBufferLabel": "Geri Kaydırma Tamponu", - "scrollbackMaxLines": "Tarihte saklanacak maksimum satır sayısı", - "sshAgentForwardingLabel": "SSH Aracısı Yönlendirmesi", - "sshAgentForwardingShortDesc": "Yerel SSH anahtarlarınızı bu sunucuya iletin.", - "enableAutoMosh": "Otomatik Mosh'u Etkinleştir", - "enableAutoMoshDesc": "Mümkünse SSH yerine Mosh'u tercih edin.", - "enableAutoTmux": "Otomatik Tmux'ı etkinleştirin", - "enableAutoTmuxDesc": "Tmux oturumunu otomatik olarak başlat veya ona bağlan", - "sudoPasswordAutoFillLabel": "Sudo Parolası Otomatik Doldurma", - "sudoPasswordAutoFillShortDesc": "İstendiğinde otomatik olarak sudo parolasını girin.", - "sudoPasswordLabel": "Sudo Parolası", - "environmentVariablesLabel": "Çevresel Değişkenler", - "addVariableBtn": "Değişken Ekle", - "noEnvVars": "Hiçbir ortam değişkeni yapılandırılmamış.", - "fastScrollModifierLabel": "Hızlı Kaydırma Değiştirici", - "fastScrollSensitivityLabel": "Hızlı Kaydırma Hassasiyeti", - "moshCommandLabel": "Mosh Komutanlığı", - "startupSnippetLabel": "Başlangıç Kodu Parçası", - "keepaliveIntervalLabel": "Keepalive Aralığı (saniye)", - "maxKeepaliveMisses": "Max Keepalive'ı kaçırdı", - "tunnelSettings": "Tünel Ayarları", - "enableTunneling": "Tünelleme özelliğini etkinleştirin", - "enableTunnelingDesc": "Bu sunucu için SSH tüneli işlevini etkinleştirin.", - "serverTunnelsSection": "Sunucu Tünelleri", - "addTunnelBtn": "Tünel Ekle", - "noTunnelsConfigured": "Hiçbir tünel yapılandırılmamış.", - "tunnelLabel": "Tünel {{number}}", - "tunnelType": "Tünel Tipi", - "tunnelModeLocalDesc": "Yerel bir portu, uzak sunucudaki (veya ondan erişilebilen bir ana bilgisayardaki) bir porta yönlendirin.", - "tunnelModeRemoteDesc": "Uzak sunucudaki bir portu, kendi bilgisayarınızdaki yerel bir porta yönlendirin.", - "tunnelModeDynamicDesc": "Dinamik port yönlendirmesi için yerel bir portta SOCKS5 proxy'si oluşturun.", - "sameHost": "Bu sunucu (doğrudan tünel)", - "endpointHost": "Uç Nokta Sunucusu", - "endpointPort": "Uç Nokta Bağlantı Noktası", - "bindHost": "Ana Bilgisayarı Bağla", - "sourcePort": "Kaynak Bağlantı Noktası", - "maxRetries": "Maksimum Yeniden Deneme Sayısı", - "retryIntervalS": "Tekrar Deneme Aralığı (s)", - "autoStartLabel": "Otomatik başlatma", - "autoStartDesc": "Sunucu yüklendiğinde bu tüneli otomatik olarak bağla.", - "tunnelConnecting": "Tünel bağlantısı...", - "tunnelDisconnected": "Tünel bağlantısı kesildi", - "failedToConnectTunnel": "Bağlantı kurulamadı.", - "failedToDisconnectTunnel": "Bağlantı kesilemedi", - "dockerIntegration": "Docker Entegrasyonu", - "enableDockerMonitor": "Docker'ı etkinleştirin", - "enableDockerMonitorDesc": "Bu sunucudaki konteynerleri Docker aracılığıyla izleyin ve yönetin.", - "enableFileManagerMonitor": "Dosya Yöneticisini Etkinleştir", - "enableFileManagerMonitorDesc": "Bu sunucudaki dosyaları SFTP üzerinden tarayın ve yönetin.", - "defaultPathLabel": "Varsayılan Yol", - "fileManagerPathHint": "Bu sunucu için dosya yöneticisi başlatıldığında açılacak dizin.", - "statusChecksLabel": "Durum Kontrolleri", - "enableStatusChecks": "Durum Kontrollerini Etkinleştir", - "enableStatusChecksDesc": "Kullanılabilirliğini doğrulamak için periyodik olarak bu sunucuya ping gönderin.", - "useGlobalInterval": "Küresel Aralığı Kullanın", - "useGlobalIntervalDesc": "Sunucu genelindeki durum kontrol aralığıyla geçersiz kılın.", - "checkIntervalS": "Kontrol Aralığı(ları)", - "checkIntervalDesc": "Her bağlantı pingi arasındaki saniye sayısı", - "metricsCollectionLabel": "Metriklerin Toplanması", - "enableMetricsLabel": "Ölçümleri Etkinleştir", - "enableMetricsDesc": "Bu sunucudan CPU, RAM, disk ve ağ kullanımını toplayın.", - "useGlobalMetrics": "Küresel Aralığı Kullanın", - "useGlobalMetricsDesc": "Sunucu genelindeki ölçüm aralığıyla geçersiz kılın.", - "metricsIntervalS": "Ölçüm Aralığı(ları)", - "metricsIntervalDesc2": "Metrik anlık görüntüler arasındaki saniyeler", - "visibleWidgets": "Görünür Widget'lar", - "cpuUsageLabel": "CPU Kullanımı", - "cpuUsageDesc": "CPU yüzdesi, yük ortalamaları, mini grafik", - "memoryLabel": "Bellek Kullanımı", - "memoryDesc": "RAM kullanımı, takas alanı, önbelleğe alınmış bellek", - "storageLabel": "Disk Kullanımı", - "storageDesc": "Bağlantı noktası başına disk kullanımı", - "networkLabel": "Ağ Arayüzleri", - "networkDesc": "Arayüz listesi ve bant genişliği", - "uptimeLabel": "Çalışma süresi", - "uptimeDesc": "Sistem çalışma süresi ve önyükleme süresi", - "systemInfoLabel": "Sistem Bilgileri", - "systemInfoDesc": "İşletim sistemi, çekirdek, ana bilgisayar adı, mimari", - "recentLoginsLabel": "Son Girişler", - "recentLoginsDesc": "Başarılı ve başarısız giriş olayları", - "topProcessesLabel": "En İyi Süreçler", - "topProcessesDesc": "PID, CPU%, MEM%, komut", - "listeningPortsLabel": "Dinleme Portları", - "listeningPortsDesc": "İşlem ve durum ile açık portlar", - "firewallLabel": "Güvenlik Duvarı", - "firewallDesc": "Güvenlik duvarı, AppArmor, SELinux durumu", - "quickActionsLabel": "Hızlı İşlemler", - "quickActionsToolbar": "Hızlı işlemler, tek tıklamayla komut yürütme için Sunucu İstatistikleri araç çubuğunda düğmeler olarak görünür.", - "noQuickActions": "Henüz hızlı bir işlem yapılmadı.", - "buttonLabel": "Düğme etiketi", - "selectSnippetPlaceholder": "Kod parçasını seçin...", - "addActionBtn": "Eylem Ekle", - "hostSharedSuccessfully": "Sunucu başarıyla paylaşıldı.", - "failedToShareHost": "Sunucuyu paylaşma başarısız oldu.", - "accessRevoked": "Erişim iptal edildi", - "failedToRevokeAccess": "Erişimi iptal etme başarısız oldu.", - "cancelBtn": "İptal etmek", - "savingBtn": "Tasarruf...", - "addHostBtn": "Sunucu Ekle", - "hostUpdated": "Sunucu güncellendi", - "hostCreated": "Sunucu oluşturuldu", - "failedToSave": "Sunucuyu kaydetme başarısız oldu.", - "credentialUpdated": "Kimlik bilgileri güncellendi", - "credentialCreated": "Kimlik bilgisi oluşturuldu", - "failedToSaveCredential": "Kimlik bilgilerini kaydetme başarısız oldu.", - "backToHosts": "Ev sahiplerine geri dön", - "backToCredentials": "Kimlik bilgilerine geri dön", - "pinned": "Sabitlendi", - "noHostsFound": "Hiçbir sunucu bulunamadı.", - "tryDifferentTerm": "Farklı bir terim deneyin.", - "addFirstHost": "Başlamak için ilk sunucunuzu ekleyin.", - "noCredentialsFound": "Kimlik bilgisi bulunamadı.", - "addCredentialBtn": "Kimlik Bilgisi Ekle", - "updateCredentialBtn": "Kimlik Bilgilerini Güncelle", - "features": "Özellikler", - "noFolder": "(Klasör yok)", - "deleteSelected": "Silmek", - "exitSelection": "Çıkış seçimi", - "importSkip": "İçe aktar (mevcut olanı atla)", - "importOverwrite": "İçe aktar (üzerine yaz)", - "collapseBtn": "Yıkılmak", - "importExportBtn": "İthalat / İhracat", - "hostStatusesRefreshed": "Sunucu durumları güncellendi", - "failedToRefreshHosts": "Sunucuları yenileme başarısız oldu.", - "movedHostTo": "{{host}} \"{{folder}} \" konumuna taşındı", - "failedToMoveHost": "Sunucuyu taşıma başarısız oldu.", - "folderRenamedTo": "Klasörün adı \"{{name}} \" olarak değiştirildi.", - "deletedFolder": "Silinen klasör \"{{name}}\"", - "failedToDeleteFolder": "Klasör silme işlemi başarısız oldu.", - "deleteAllInFolder": "\"{{name}}\" içindeki tüm sunucuları silmek mi istiyorsunuz? Bu işlem geri alınamaz.", - "deletedHost": "Silindi {{name}}", - "copiedToClipboard": "Panoya kopyalandı", - "terminalUrlCopied": "Terminal URL'si kopyalandı", - "fileManagerUrlCopied": "Dosya Yöneticisi URL'si kopyalandı", - "tunnelUrlCopied": "Tünel URL'si kopyalandı", - "dockerUrlCopied": "Docker URL'si kopyalandı", - "serverStatsUrlCopied": "Sunucu İstatistikleri URL'si kopyalandı", - "rdpUrlCopied": "RDP URL'si kopyalandı", - "vncUrlCopied": "VNC URL'si kopyalandı", - "telnetUrlCopied": "Telnet URL'si kopyalandı", - "remoteDesktopUrlCopied": "Uzak Masaüstü URL'si kopyalandı", - "expandActions": "Eylemleri genişlet", - "collapseActions": "İşlemleri daralt", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "{{name}} adresine sihirli paket gönderildi.", - "wakeOnLanError": "Sihirli paket gönderilemedi.", - "cloneHostAction": "Klon Ana Bilgisayar", - "copyAddress": "Adresi Kopyala", - "copyLink": "Bağlantıyı Kopyala", - "copyTerminalUrlAction": "Terminal URL'sini Kopyala", - "copyFileManagerUrlAction": "Dosya Yöneticisi URL'sini Kopyala", - "copyTunnelUrlAction": "Tünel URL'sini Kopyala", - "copyDockerUrlAction": "Docker URL'sini kopyala", - "copyServerStatsUrlAction": "Sunucu İstatistikleri URL'sini Kopyala", - "copyRdpUrlAction": "RDP URL'sini kopyala", - "copyVncUrlAction": "VNC URL'sini kopyala", - "copyTelnetUrlAction": "Telnet URL'sini kopyala", - "copyRemoteDesktopUrlAction": "Uzak Masaüstü URL'sini Kopyala", - "deleteCredentialConfirm": "Kimlik bilgilerini sil \"{{name}}\"?", - "deletedCredential": "Silindi {{name}}", - "deploySSHKeyTitle": "SSH Anahtarını Dağıt", - "deployingBtn": "Dağıtıma başlanıyor...", - "deployBtn": "Dağıt", - "failedToDeployKey": "Anahtar dağıtımı başarısız oldu.", - "deleteHostsConfirm": "{{count}} ana bilgisayarını{{plural}}silmek mi? Bu işlem geri alınamaz.", - "movedToRoot": "Kök dizine taşındı", - "failedToMoveHosts": "Sunucuları taşıma başarısız oldu.", - "enableTerminalFeature": "Terminali Etkinleştir", - "disableTerminalFeature": "Terminali Devre Dışı Bırak", - "enableFilesFeature": "Dosyaları Etkinleştir", - "disableFilesFeature": "Dosyaları Devre Dışı Bırak", - "enableTunnelsFeature": "Tünelleri Etkinleştir", - "disableTunnelsFeature": "Tünelleri Devre Dışı Bırak", - "enableDockerFeature": "Docker'ı etkinleştirin", - "disableDockerFeature": "Docker'ı devre dışı bırak", - "addTagsPlaceholder": "Etiket ekle...", - "authDetails": "Kimlik Doğrulama Ayrıntıları", - "credType": "Tip", - "generateKeyPairDesc": "Yeni bir anahtar çifti oluşturun; hem özel hem de genel anahtarlar otomatik olarak doldurulacaktır.", - "generatingKey": "Oluşturuluyor...", - "generateLabel": "{{label}} oluştur", - "uploadFileBtn": "Dosya yükle", - "keyPassphraseOptional": "Ana Parola (İsteğe bağlı)", - "sshPublicKeyOptional": "SSH Genel Anahtarı (İsteğe bağlı)", - "publicKeyGenerated": "Oluşturulan açık anahtar", - "failedToGeneratePublicKey": "Açık anahtar türetilemedi.", - "publicKeyCopied": "Genel anahtar kopyalandı", - "keyPairGenerated": "{{label}} anahtar çifti oluşturuldu", - "failedToGenerateKeyPair": "Anahtar çifti oluşturulamadı.", - "searchHostsPlaceholder": "Sunucuları, adresleri, etiketleri ara…", - "searchCredentialsPlaceholder": "Arama kimlik bilgileri…", - "refreshBtn": "Yenile", - "addTag": "Etiket ekle...", - "deleteConfirmBtn": "Silmek", - "tunnelRequirementsText": "SSH sunucusunda /etc/ssh/sshd_config dosyasında GatewayPorts yes, AllowTcpForwarding yes ve PermitRootLogin yes ayarlarının yapılmış olması gerekir.", - "deleteHostConfirm": "\"{{name}} \" silinecek mi?", - "enableAtLeastOneProtocol": "Kimlik doğrulama ve bağlantı ayarlarını yapılandırmak için yukarıdaki protokollerden en az birini etkinleştirin.", - "keyPassphrase": "Anahtar Parola", - "connectBtn": "Bağlamak", - "disconnectBtn": "Bağlantıyı kes", - "basicInformation": "Temel Bilgiler", - "authDetailsSection": "Kimlik Doğrulama Ayrıntıları", - "credTypeLabel": "Tip", - "hostsTab": "Ev sahipleri", - "credentialsTab": "Kimlik Bilgileri", - "selectMultiple": "Birden fazla seçenek seçin", - "selectHosts": "Sunucuları seçin", - "connectionLabel": "Bağlantı", - "authenticationLabel": "Kimlik doğrulama", - "generateKeyPairTitle": "Anahtar Çifti Oluştur", - "generateKeyPairDescription": "Yeni bir anahtar çifti oluşturun; hem özel hem de genel anahtarlar otomatik olarak doldurulacaktır.", - "generateFromPrivateKey": "Özel Anahtardan Oluştur", - "refreshBtn2": "Yenile", - "exitSelectionTitle": "Çıkış seçimi", - "exportAll": "Tümünü Dışa Aktar", - "addHostBtn2": "Sunucu Ekle", - "addCredentialBtn2": "Kimlik Bilgisi Ekle", - "checkingHostStatuses": "Sunucu durumları kontrol ediliyor...", - "pinnedSection": "Sabitlendi", - "hostsExported": "Sunucular başarıyla dışa aktarıldı.", - "exportFailed": "Ana bilgisayarları dışa aktarma başarısız oldu.", - "sampleDownloaded": "Örnek dosya indirildi.", - "failedToDeleteCredential2": "Kimlik bilgilerini silme işlemi başarısız oldu.", - "noFolderOption": "(Klasör yok)", - "nSelected": "{{count}} seçildi", - "featuresMenu": "Özellikler", - "moveMenu": "Taşınmak", - "cancelSelection": "İptal etmek", - "deployDialogDesc": "{{name}} öğesini bir sunucunun authorized_keys'ine dağıtın.", - "targetHostLabel": "Hedef Sunucu", - "selectHostOption": "Bir sunucu seçin...", - "keyDeployedSuccess": "Anahtar başarıyla yerleştirildi.", - "failedToDeployKey2": "Anahtar dağıtımı başarısız oldu.", - "deletedCount": "Silinen {{count}} sunucular", - "failedToDeleteCount": "{{count}} ana bilgisayarı silme işlemi başarısız oldu.", - "duplicatedHost": "\"{{name}} \" kopyalandı", - "failedToDuplicateHost": "Sunucu çoğaltılamadı.", - "updatedCount": "Güncellenen {{count}} sunucular", - "friendlyNameLabel": "Dostça İsim", - "descriptionLabel": "Tanım", - "loadingHost": "Sunucu yükleniyor...", - "loadingHosts": "Sunucular yükleniyor...", - "loadingCredentials": "Kimlik bilgileri yükleniyor...", - "noHostsYet": "Henüz ev sahibi yok.", - "noHostsMatchSearch": "Arama kriterlerinize uyan ev sahibi bulunamadı.", - "hostNotFound": "Sunucu bulunamadı.", - "searchHosts": "Sunucu ara...", - "sortHosts": "Sunucuları Sırala", - "sortDefault": "Varsayılan Sipariş", - "sortNameAsc": "İsim (A → Z)", - "sortNameDesc": "İsim (Z → A)", - "sortIpAsc": "IP Adresi (Artan)", - "sortIpDesc": "IP Adresi (Açıklama)", - "sortOnlineFirst": "Çevrimiçi Öncelikli", - "sortOfflineFirst": "Çevrimdışı Önce", - "sortPinnedFirst": "İlk olarak sabitlendi", - "filterHosts": "Sunucuları Filtrele", - "filterClearAll": "Filtreleri Temizle", - "filterStatusGroup": "Durum", - "filterOnline": "Çevrimiçi", - "filterOffline": "Çevrimdışı", - "filterPinned": "Sabitlendi", - "filterAuthGroup": "Kimlik Doğrulama Türü", - "filterAuthPassword": "Şifre", - "filterAuthKey": "SSH Anahtarı", - "filterAuthCredential": "Kimlik belgesi", - "filterAuthNone": "Hiçbiri", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Protokol", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Özellikler", - "filterFeatureTerminal": "terminal", - "filterFeatureFileManager": "Dosya Yöneticisi", - "filterFeatureTunnel": "Tünel", - "filterFeatureDocker": "Liman işçisi", - "filterTagsGroup": "Etiketler", - "shareHost": "Paylaşımlı Sunucu", - "shareHostTitle": "Paylaş: {{name}}", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Bu sunucunun paylaşımı etkinleştirmek için bir kimlik bilgisi kullanması gerekir. Öncelikle sunucuyu düzenleyin ve bir kimlik bilgisi atayın." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Bağlantı", - "authentication": "Kimlik doğrulama", - "connectionSettings": "Bağlantı Ayarları", - "displaySettings": "Ekran Ayarları", - "audioSettings": "Ses Ayarları", - "rdpPerformance": "RDP Performansı", - "deviceRedirection": "Cihaz Yönlendirmesi", - "session": "Oturum", - "gateway": "Geçit", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Panoya", - "sessionRecording": "Oturum Kaydı", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "VNC Ayarları", - "terminalSettings": "Terminal Ayarları", - "rdpPort": "RDP Bağlantı Noktası", - "username": "Kullanıcı adı", - "password": "Şifre", - "domain": "İhtisas", - "securityMode": "Güvenlik Modu", - "colorDepth": "Renk Derinliği", - "width": "Genişlik", - "height": "Yükseklik", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Yeniden Boyutlandırma Yöntemi", - "clientName": "Müşteri Adı", - "initialProgram": "Başlangıç Programı", - "serverLayout": "Sunucu Düzeni", - "timezone": "Saat dilimi", - "gatewayHostname": "Ağ Geçidi Ana Bilgisayar Adı", - "gatewayPort": "Ağ Geçidi Bağlantı Noktası", - "gatewayUsername": "Ağ Geçidi Kullanıcı Adı", - "gatewayPassword": "Ağ Geçidi Şifresi", - "gatewayDomain": "Ağ Geçidi Alanı", - "remoteAppProgram": "RemoteApp Programı", - "workingDirectory": "Çalışma Dizini", - "arguments": "Tartışmalar", - "normalizeLineEndings": "Satır Sonlarını Normalleştir", - "recordingPath": "Kayıt Yolu", - "recordingName": "Kayıt Adı", - "macAddress": "MAC Adresi", - "broadcastAddress": "Yayın Adresi", - "udpPort": "UDP Bağlantı Noktası", - "waitTimeS": "Bekleme Süresi(leri)", - "driveName": "Sürücü Adı", - "drivePath": "Sürüş Yolu", - "ignoreCertificate": "Sertifikayı Yok Say", - "ignoreCertificateDesc": "Kendinden imzalı sertifikalara sahip sunuculara bağlantılara izin ver.", - "forceLossless": "Kuvvet Kayıpsız", - "forceLosslessDesc": "Kayıpsız görüntü kodlamayı zorla (daha yüksek kalite, daha fazla bant genişliği)", - "disableAudio": "Sesi Devre Dışı Bırak", - "disableAudioDesc": "Uzaktan oturumdaki tüm sesleri kapatın.", - "enableAudioInput": "Ses Girişini (Mikrofon) Etkinleştir", - "enableAudioInputDesc": "Yerel mikrofonu uzaktaki oturuma ilet", - "wallpaper": "Duvar Kağıdı", - "wallpaperDesc": "Masaüstü duvar kağıdını göster (devre dışı bırakmak performansı artırır)", - "theming": "Temalandırma", - "themingDesc": "Görsel temaları ve stilleri etkinleştirin", - "fontSmoothing": "Yazı Tipi Yumuşatma", - "fontSmoothingDesc": "ClearType yazı tipinin oluşturulmasını etkinleştirin", - "fullWindowDrag": "Tam Pencere Sürükleme", - "fullWindowDragDesc": "Sürüklerken pencere içeriğini göster", - "desktopComposition": "Masaüstü Kompozisyonu", - "desktopCompositionDesc": "Aero cam efektlerini etkinleştirin", - "menuAnimations": "Menü Animasyonları", - "menuAnimationsDesc": "Menü geçiş ve kaydırma animasyonlarını etkinleştirin", - "disableBitmapCaching": "Bitmap önbelleklemesini devre dışı bırak", - "disableBitmapCachingDesc": "Bitmap önbelleğini kapatın (bu, aksaklıkları gidermeye yardımcı olabilir).", - "disableOffscreenCaching": "Ekran Dışı Önbellekleme'yi Devre Dışı Bırak", - "disableOffscreenCachingDesc": "Ekran dışı önbelleği devre dışı bırakın.", - "disableGlyphCaching": "Glif Önbelleklemesini Devre Dışı Bırak", - "disableGlyphCachingDesc": "Glif önbelleğini kapatın.", - "enableGfx": "GFX'i etkinleştirin", - "enableGfxDesc": "RemoteFX grafik işlem hattını kullanın.", - "enablePrinting": "Yazdırmayı Etkinleştir", - "enablePrintingDesc": "Yerel yazıcıları uzak oturuma yönlendirin.", - "enableDriveRedirection": "Sürücü Yönlendirmesini Etkinleştir", - "enableDriveRedirectionDesc": "Yerel bir klasörü uzak oturumda sürücü olarak eşleştirin.", - "createDrivePath": "Sürüş Yolu Oluştur", - "createDrivePathDesc": "Klasör mevcut değilse otomatik olarak oluştur.", - "disableDownload": "İndirmeyi Devre Dışı Bırak", - "disableDownloadDesc": "Uzak oturumdan dosya indirilmesini engelleyin.", - "disableUpload": "Yüklemeyi Devre Dışı Bırak", - "disableUploadDesc": "Uzak oturuma dosya yüklenmesini engelleyin.", - "enableTouch": "Dokunmatik özelliği etkinleştirin", - "enableTouchDesc": "Dokunmatik giriş yönlendirmesini etkinleştirin", - "consoleSession": "Konsol Oturumu", - "consoleSessionDesc": "Yeni bir oturum açmak yerine konsola (oturum 0) bağlanın.", - "sendWolPacket": "WOL Paketini Gönder", - "sendWolPacketDesc": "Bağlantı kurmadan önce bu sunucuyu uyandırmak için sihirli bir paket gönderin.", - "disableCopy": "Kopyalamayı Devre Dışı Bırak", - "disableCopyDesc": "Uzak oturumdan metin kopyalamayı engelleyin.", - "disablePaste": "Yapıştırmayı Devre Dışı Bırak", - "disablePasteDesc": "Uzak oturuma metin yapıştırmayı engelleyin.", - "createPathIfMissing": "Eksikse Yol Oluştur", - "createPathIfMissingDesc": "Kayıt dizinini otomatik olarak oluştur", - "excludeOutput": "Çıktıyı Hariç Tut", - "excludeOutputDesc": "Ekran çıktısını kaydetmeyin (yalnızca meta veriler).", - "excludeMouse": "Fareyi Hariç Tut", - "excludeMouseDesc": "Fare hareketlerini kaydetmeyin.", - "includeKeystrokes": "Tuş vuruşlarını dahil et", - "includeKeystrokesDesc": "Ekran çıktısına ek olarak ham tuş vuruşlarını da kaydedin.", - "vncPort": "VNC Bağlantı Noktası", - "vncPassword": "VNC Şifresi", - "vncUsernameOptional": "Kullanıcı adı (isteğe bağlı)", - "vncLeaveBlank": "Gerekli değilse boş bırakın.", - "cursorMode": "İmleç Modu", - "swapRedBlue": "Kırmızı/Maviyi Değiştir", - "swapRedBlueDesc": "Kırmızı ve mavi renk kanallarının yerini değiştirin (bazı renk sorunlarını düzeltir).", - "readOnly": "salt okunur", - "readOnlyDesc": "Herhangi bir giriş göndermeden uzaktaki ekranı görüntüleyin.", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", "telnetPort": "Telnet Port", - "terminalType": "Terminal Tipi", - "fontName": "Yazı Tipi Adı", - "fontSize": "Yazı Tipi Boyutu", - "colorScheme": "Renk Şeması", - "backspaceKey": "Geri tuşu", - "saveHostFirst": "Önce sunucuyu kurtarın.", - "sharingOptionsAfterSave": "Sunucu kaydedildikten sonra paylaşım seçenekleri kullanılabilir hale gelir.", - "sharingLoadError": "Paylaşım verileri yüklenemedi. Bağlantınızı kontrol edin ve tekrar deneyin.", - "shareHostSection": "Paylaşımlı Sunucu", - "shareWithUser": "Kullanıcıyla paylaş", - "shareWithRole": "Rol ile paylaş", - "selectUser": "Kullanıcı Seç", - "selectRole": "Rol Seçin", - "selectUserOption": "Bir kullanıcı seçin...", - "selectRoleOption": "Bir rol seçin...", - "permissionLevel": "İzin Seviyesi", - "expiresInHours": "(Saat) içinde sona eriyor", - "noExpiryPlaceholder": "Son kullanma tarihi olmaması için boş bırakın.", - "shareBtn": "Paylaşmak", - "currentAccess": "Mevcut Erişim", - "typeHeader": "Tip", - "targetHeader": "Hedef", - "permissionHeader": "İzin", - "grantedByHeader": "Veren", - "expiresHeader": "Süresi doluyor", - "noAccessEntries": "Henüz erişim kaydı yok.", - "expiredLabel": "Günü geçmiş", - "neverLabel": "Asla", - "revokeBtn": "Geri çekmek", - "cancelBtn": "İptal etmek", - "savingBtn": "Tasarruf...", - "updateHostBtn": "Sunucuyu Güncelle", - "addHostBtn": "Sunucu Ekle" + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Sunucuları, komutları veya ayarları arayın...", - "quickActions": "Hızlı İşlemler", - "hostManager": "Sunucu Yöneticisi", - "hostManagerDesc": "Sunucuları yönetin, ekleyin veya düzenleyin.", - "addNewHost": "Yeni Sunucu Ekle", - "addNewHostDesc": "Yeni bir ev sahibi kaydedin", - "adminSettings": "Yönetici Ayarları", - "adminSettingsDesc": "Sistem tercihlerini ve kullanıcıları yapılandırın.", - "userProfile": "Kullanıcı Profili", - "userProfileDesc": "Hesabınızı ve tercihlerinizi yönetin", - "addCredential": "Kimlik Bilgisi Ekle", - "addCredentialDesc": "SSH anahtarlarını veya şifrelerini saklayın.", - "recentActivity": "Son Aktiviteler", - "serversAndHosts": "Sunucular ve Hostlar", - "noHostsFound": "\"{{search}} \" ile eşleşen hiçbir sunucu bulunamadı.", - "links": "Bağlantılar", - "navigate": "Gezinti", - "select": "Seçme", - "toggleWith": "ile değiştir" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Bölme {{index}} - boş", - "noTabAssigned": "Hiçbir sekme atanmadı.", - "focusedPane": "Aktif bölme" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Bağlantı yok.", - "noConnectionsDesc": "Buradaki bağlantıları görmek için bir terminal, dosya yöneticisi veya uzak masaüstü açın.", - "connectedFor": "{{duration}} için bağlantı kuruldu.", - "connected": "Bağlı", - "disconnected": "Bağlantı kesildi", - "closeTab": "Sekmeyi kapat", - "closeConnection": "Yakın bağlantı", - "forgetTab": "Unutmak", - "removeBackground": "Kaldırmak", - "reconnect": "Yeniden bağlan", - "reopenTab": "Yeniden açın", - "sectionOpen": "Açık", - "sectionBackground": "Arka plan", - "backgroundDesc": "Bağlantı kesildikten sonra oturumlar 30 dakika boyunca devam eder ve yeniden bağlanabilir.", - "persisted": "Arka planda devam etti", - "expiresIn": "{{duration}} içinde sona eriyor", - "search": "Arama bağlantıları...", - "noSearchResults": "Arama kriterlerinize uyan bağlantı bulunamadı." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "{{type}} oturumuna bağlanılıyor...", - "connectionError": "Bağlantı hatası", - "connectionFailed": "Bağlantı başarısız oldu.", - "failedToConnect": "Bağlantı belirteci alınamadı.", - "hostNotFound": "Sunucu bulunamadı.", - "noHostSelected": "Sunucu seçilmedi.", - "reconnect": "Yeniden bağlan", - "retry": "Tekrar dene", - "guacdUnavailable": "Uzak masaüstü hizmeti (guacd) kullanılamıyor. Lütfen guacd'nin çalıştığından, erişilebilir olduğundan ve yönetici ayarlarında doğru şekilde yapılandırıldığından emin olun.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Kilit Ekranı)", - "winKey": "Windows Anahtarı", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "Alternatif", - "shift": "Vardiya", - "win": "Kazanç", - "stickyActive": "{{key}} (kilitli - serbest bırakmak için tıklayın)", - "stickyInactive": "{{key}} (kilitlemek için tıklayın)", - "esc": "Kaçmak", - "tab": "Sekme", - "home": "Ev", - "end": "Son", - "pageUp": "Sayfa Yukarı", - "pageDown": "Sayfa Aşağı", - "arrowUp": "Yukarı Ok", - "arrowDown": "Aşağı Ok", - "arrowLeft": "Ok Sol", - "arrowRight": "Sağ Ok", - "fnToggle": "Fonksiyon Tuşları", - "reconnect": "Yeniden Bağlantı Oturumu", - "collapse": "Araç çubuğunu daralt", - "expand": "Araç çubuğunu genişletin", - "dragHandle": "Konumunu değiştirmek için sürükleyin." + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Sunucuya bağlan", - "clear": "Temizlemek", - "paste": "Yapıştır", - "reconnect": "Yeniden bağlan", - "connectionLost": "Bağlantı kesildi", - "connected": "Bağlı", - "clipboardWriteFailed": "Kopyalama işlemi panoya yapılamadı. Sayfanın HTTPS veya localhost üzerinden sunulduğundan emin olun.", - "clipboardReadFailed": "Panodan okuma işlemi başarısız oldu. Lütfen pano izinlerinin verildiğinden emin olun.", - "clipboardHttpWarning": "Yapıştırma işlemi HTTPS gerektirir. Ctrl+Shift+V tuşlarını kullanın veya Termix'i HTTPS üzerinden sunun.", - "unknownError": "Bilinmeyen bir hata oluştu.", - "websocketError": "WebSocket bağlantı hatası", - "connecting": "Bağlanıyor...", - "noHostSelected": "Sunucu seçilmedi.", - "reconnecting": "Yeniden bağlanılıyor... ({{attempt}}/{{max}})", - "reconnected": "Bağlantı başarıyla yeniden kuruldu.", - "tmuxSessionCreated": "tmux oturumu oluşturuldu: {{name}}", - "tmuxSessionAttached": "tmux oturumu eklendi: {{name}}", - "tmuxUnavailable": "Uzak sunucuda tmux kurulu değil, standart kabuğa geri dönülüyor.", - "tmuxSessionPickerTitle": "tmux Oturumları", - "tmuxSessionPickerDesc": "Bu sunucuda mevcut tmux oturumları bulundu. Yeniden bağlanmak veya yeni bir oturum oluşturmak için birini seçin.", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", "tmuxWindows": "Windows", - "tmuxWindowCount": "{{count}} pencere", - "tmuxAttached": "Ekli müşteriler", - "tmuxAttachedCount": "{{count}} eklendi", - "tmuxLastActivity": "Son aktivite", - "tmuxTimeJustNow": "Şu anda", - "tmuxTimeMinutes": "{{count}}m önce", - "tmuxTimeHours": "{{count}}saat önce", - "tmuxTimeDays": "{{count}}gün önce", - "tmuxCreateNew": "Yeni oturum başlat", - "tmuxCopyHint": "Seçimi ayarlayın ve panoya kopyalamak için Enter tuşuna basın.", - "tmuxDetach": "Tmux oturumundan ayrıl", - "tmuxDetached": "Tmux oturumundan ayrıldı", - "maxReconnectAttemptsReached": "Maksimum yeniden bağlantı deneme sayısına ulaşıldı.", - "closeTab": "Kapalı", - "connectionTimeout": "Bağlantı zaman aşımı", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Çalıştırılıyor {{command}} - {{host}}", - "totpRequired": "İki Faktörlü Kimlik Doğrulama Gerekli", - "totpCodeLabel": "Doğrulama Kodu", - "totpVerify": "Doğrulamak", - "warpgateAuthRequired": "Warpgate Kimlik Doğrulaması Gerekli", - "warpgateSecurityKey": "Güvenlik Anahtarı", - "warpgateAuthUrl": "Kimlik Doğrulama URL'si", - "warpgateOpenBrowser": "Tarayıcıda aç", - "warpgateContinue": "Kimlik doğrulama işlemini tamamladım.", - "opksshAuthRequired": "OPKSSH Kimlik Doğrulaması Gerekli", - "opksshAuthDescription": "Devam etmek için tarayıcınızda kimlik doğrulama işlemini tamamlayın. Bu oturum 24 saat boyunca geçerli olacaktır.", - "opksshOpenBrowser": "Kimlik doğrulaması için tarayıcıyı açın.", - "opksshWaitingForAuth": "Tarayıcıda kimlik doğrulaması bekleniyor...", - "opksshAuthenticating": "Kimlik doğrulama işlemi gerçekleştiriliyor...", - "opksshTimeout": "Kimlik doğrulama işlemi zaman aşımına uğradı. Lütfen tekrar deneyin.", - "opksshAuthFailed": "Kimlik doğrulama başarısız oldu. Lütfen kimlik bilgilerinizi kontrol edin ve tekrar deneyin.", - "opksshSignInWith": "{{provider}} ile giriş yapın", - "sudoPasswordPopupTitle": "Şifrenizi girin?", - "websocketAbnormalClose": "Bağlantı beklenmedik şekilde kapatıldı. Bu durum, ters proxy veya SSL yapılandırma sorunundan kaynaklanıyor olabilir. Lütfen sunucu günlüklerini kontrol edin.", - "connectionLogTitle": "Bağlantı Günlüğü", - "connectionLogCopy": "Günlükleri panoya kopyala", - "connectionLogEmpty": "Henüz bağlantı günlükleri yok.", - "connectionLogWaiting": "Bağlantı günlükleri bekleniyor...", - "connectionLogCopied": "Bağlantı günlükleri panoya kopyalandı", - "connectionLogCopyFailed": "Günlük kayıtlarını panoya kopyalama başarısız oldu.", - "connectionRejected": "Sunucu bağlantıyı reddetti. Lütfen kimlik doğrulama ve ağ yapılandırmanızı kontrol edin.", - "hostKeyRejected": "SSH sunucu anahtarı doğrulama işlemi reddedildi. Bağlantı iptal edildi.", - "sessionTakenOver": "Oturum başka bir sekmede açıldı. Yeniden bağlanılıyor..." + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Sunucu seçilmedi.", - "initializingEditor": "Editör başlatılıyor...", - "file": "Dosya", - "folder": "Dosya", - "uploadFile": "Dosya Yükle", - "downloadFile": "İndirmek", - "extractArchive": "Arşivden Çıkarma", - "extractingArchive": "{{name}} çıkarılıyor...", - "archiveExtractedSuccessfully": "{{name}} başarıyla çıkarıldı", - "extractFailed": "Çıkarma işlemi başarısız oldu.", - "compressFile": "Dosyayı Sıkıştır", - "compressFiles": "Dosyaları Sıkıştır", - "compressFilesDesc": "{{count}} öğeyi bir arşive sıkıştırın", - "archiveName": "Arşiv Adı", - "enterArchiveName": "Arşiv adını girin...", - "compressionFormat": "Sıkıştırma Formatı", - "selectedFiles": "Seçilen dosyalar", - "andMoreFiles": "ve {{count}} daha fazlası...", - "compress": "Kompres", - "compressingFiles": "{{count}} öğeyi {{name}} öğeye sıkıştırılıyor...", - "filesCompressedSuccessfully": "{{name}} başarıyla oluşturuldu", - "compressFailed": "Sıkıştırma başarısız oldu", - "edit": "Düzenlemek", - "preview": "Önizleme", - "previous": "Öncesi", - "next": "Sonraki", - "pageXOfY": "Sayfa {{current}} / {{total}}", - "zoomOut": "Uzaklaştır", - "zoomIn": "Yakınlaştır", - "newFile": "Yeni Dosya", - "newFolder": "Yeni Klasör", - "rename": "Yeniden isimlendirmek", - "uploading": "Yükleniyor...", - "uploadingFile": "{{name}} yükleniyor...", - "fileName": "Dosya adı", - "folderName": "Klasör Adı", - "fileUploadedSuccessfully": "\"{{name}}\" dosyası başarıyla yüklendi", - "failedToUploadFile": "Dosya yükleme başarısız oldu.", - "fileDownloadedSuccessfully": "\"{{name}}\" dosyası başarıyla indirildi", - "failedToDownloadFile": "Dosya indirme başarısız oldu.", - "fileCreatedSuccessfully": "\"{{name}}\" dosyası başarıyla oluşturuldu", - "folderCreatedSuccessfully": "\"{{name}}\" klasörü başarıyla oluşturuldu", - "failedToCreateItem": "Öğe oluşturulamadı.", - "operationFailed": "{{operation}} işlemi {{name}}için başarısız oldu: {{error}}", - "failedToResolveSymlink": "Sembolik bağlantı çözümlenemedi.", - "itemsDeletedSuccessfully": "{{count}} öğe başarıyla silindi", - "failedToDeleteItems": "Öğeleri silme işlemi başarısız oldu.", - "sudoPasswordRequired": "Yönetici Parolası Gerekli", - "enterSudoPassword": "Bu işleme devam etmek için sudo parolasını girin.", - "sudoPassword": "Sudo şifresi", - "sudoOperationFailed": "Sudo işlemi başarısız oldu", - "sudoAuthFailed": "Sudo kimlik doğrulaması başarısız oldu", - "dragFilesToUpload": "Yüklemek için dosyaları buraya sürükleyin.", - "emptyFolder": "Bu klasör boş.", - "searchFiles": "Dosyaları ara...", - "upload": "Yüklemek", - "selectHostToStart": "Dosya yönetimini başlatmak için bir sunucu seçin.", - "sshRequiredForFileManager": "Dosya yöneticisi SSH gerektiriyor. Bu sunucuda SSH etkinleştirilmemiş.", - "failedToConnect": "SSH bağlantısı kurulamadı.", - "failedToLoadDirectory": "Dizin yüklenemedi.", - "noSSHConnection": "SSH bağlantısı mevcut değil.", - "copy": "Kopyala", - "cut": "Kesmek", - "paste": "Yapıştır", - "copyPath": "Kopyala Yolu", - "copyPaths": "Kopyala Yolları", - "delete": "Silmek", - "properties": "Özellikler", - "refresh": "Yenile", - "downloadFiles": "{{count}} dosyayı Tarayıcıya İndir", - "copyFiles": "{{count}} öğeyi kopyala", - "cutFiles": "{{count}} öğeyi kes", - "deleteFiles": "{{count}} öğeyi sil", - "filesCopiedToClipboard": "{{count}} öğe panoya kopyalandı", - "filesCutToClipboard": "{{count}} öğe panoya kopyalandı", - "pathCopiedToClipboard": "Yol panoya kopyalandı", - "pathsCopiedToClipboard": "{{count}} yollar panoya kopyalandı", - "failedToCopyPath": "Yol kopyalanıp panoya aktarılamadı.", - "movedItems": "{{count}} öğe taşındı", - "failedToDeleteItem": "Öğeyi silme işlemi başarısız oldu.", - "itemRenamedSuccessfully": "{{type}} başarıyla yeniden adlandırıldı", - "failedToRenameItem": "Öğeyi yeniden adlandırma başarısız oldu.", - "download": "İndirmek", - "permissions": "İzinler", - "size": "Boyut", - "modified": "Değiştirildi", - "path": "Yol", - "confirmDelete": "{{name}} öğesini silmek istediğinizden emin misiniz?", - "permissionDenied": "İzin reddedildi", - "serverError": "Sunucu Hatası", - "fileSavedSuccessfully": "Dosya başarıyla kaydedildi.", - "failedToSaveFile": "Dosya kaydedilemedi.", - "confirmDeleteSingleItem": "\"{{name}} \" öğesini kalıcı olarak silmek istediğinizden emin misiniz?", - "confirmDeleteMultipleItems": "{{count}} öğeyi kalıcı olarak silmek istediğinizden emin misiniz?", - "confirmDeleteMultipleItemsWithFolders": "{{count}} öğeyi kalıcı olarak silmek istediğinizden emin misiniz? Bu, klasörleri ve içeriklerini de içerir.", - "confirmDeleteFolder": "\"{{name}}\" klasörünü ve tüm içeriğini kalıcı olarak silmek istediğinizden emin misiniz?", - "permanentDeleteWarning": "Bu işlem geri alınamaz. Öğeler sunucudan kalıcı olarak silinecektir.", - "recent": "Son", - "pinned": "Sabitlendi", - "folderShortcuts": "Klasör Kısayolları", - "failedToReconnectSSH": "SSH oturumu yeniden bağlanamadı.", - "openTerminalHere": "Terminali buradan açın.", - "run": "Koşmak", - "openTerminalInFolder": "Bu klasörde terminali açın.", - "openTerminalInFileLocation": "Dosya konumunda terminali açın.", - "runningFile": "Koşuyor - {{file}}", - "onlyRunExecutableFiles": "Yalnızca çalıştırılabilir dosyaları çalıştırabilir.", - "directories": "Dizinler", - "removedFromRecentFiles": "Son dosyalardan \"{{name}}\" kaldırıldı.", - "removeFailed": "Kaldırma işlemi başarısız oldu.", - "unpinnedSuccessfully": "\"{{name}}\" başarıyla sabitlenmiş özelliğini kaldırdı.", - "unpinFailed": "Sabitlemeyi kaldırma başarısız oldu", - "removedShortcut": "\"{{name}} \" kısayolu kaldırıldı.", - "removeShortcutFailed": "Kısayol kaldırma işlemi başarısız oldu.", - "clearedAllRecentFiles": "Son kullanılan tüm dosyalar silindi.", - "clearFailed": "Temizleme işlemi başarısız oldu.", - "removeFromRecentFiles": "Son kullanılan dosyalardan kaldır", - "clearAllRecentFiles": "Son kullanılan tüm dosyaları temizle", - "unpinFile": "Dosyanın sabitlemesini kaldır", - "removeShortcut": "Kısayolu kaldır", - "pinFile": "Pin dosyası", - "addToShortcuts": "Kısayollara ekle", - "pasteFailed": "Yapıştırma işlemi başarısız oldu.", - "noUndoableActions": "Geri alınamaz işlem yok.", - "undoCopySuccess": "Kopyalama işlemi geri alındı: Kopyalanan {{count}} dosya silindi.", - "undoCopyFailedDelete": "Geri alma başarısız oldu: Kopyalanan dosyaların hiçbiri silinemedi.", - "undoCopyFailedNoInfo": "Geri alma başarısız oldu: Kopyalanan dosya bilgileri bulunamadı.", - "undoMoveSuccess": "Taşıma işlemi geri alındı: {{count}} dosya orijinal konumuna geri taşındı.", - "undoMoveFailedMove": "Geri alma başarısız oldu: Hiçbir dosya geri taşınamadı.", - "undoMoveFailedNoInfo": "Geri alma başarısız oldu: Taşınan dosya bilgisi bulunamadı.", - "undoDeleteNotSupported": "Silme işlemi geri alınamaz: Dosyalar sunucudan kalıcı olarak silindi.", - "undoTypeNotSupported": "Desteklenmeyen geri alma işlemi türü", - "undoOperationFailed": "Geri alma işlemi başarısız oldu", - "unknownError": "Bilinmeyen hata", - "confirm": "Onaylamak", - "find": "Bulmak...", - "replace": "Yer değiştirmek", - "downloadInstead": "Bunun yerine indirin", - "keyboardShortcuts": "Klavye Kısayolları", - "searchAndReplace": "Arama ve Değiştirme", - "editing": "Düzenleme", - "search": "Aramak", - "findNext": "Sonrakini Bul", - "findPrevious": "Öncekini Bul", - "save": "Kaydetmek", - "selectAll": "Tümünü Seç", - "undo": "Geri al", - "redo": "Tekrarla", - "moveLineUp": "Hareket Sıralaması", - "moveLineDown": "Satırı Aşağı Taşı", - "toggleComment": "Yorumu Aç/Kapat", - "autoComplete": "Otomatik Tamamlama", - "imageLoadError": "Görüntü yüklenemedi.", - "startTyping": "Yazmaya başlayın...", - "unknownSize": "Boyutu bilinmiyor", - "fileIsEmpty": "Dosya boş.", - "largeFileWarning": "Büyük Dosya Uyarısı", - "largeFileWarningDesc": "Bu dosya {{size}} boyutundadır ve metin olarak açıldığında performans sorunlarına neden olabilir.", - "fileNotFoundAndRemoved": "\"{{name}}\" dosyası bulunamadı ve son/sabitlenmiş dosyalar listesinden kaldırıldı.", - "failedToLoadFile": "Dosya yüklenemedi: {{error}}", - "serverErrorOccurred": "Sunucu hatası oluştu. Lütfen daha sonra tekrar deneyin.", - "autoSaveFailed": "Otomatik kaydetme başarısız oldu.", - "fileAutoSaved": "Dosya otomatik olarak kaydedildi", - "moveFileFailed": "{{name}} taşınamadı", - "moveOperationFailed": "Taşıma işlemi başarısız oldu", - "canOnlyCompareFiles": "Yalnızca iki dosya karşılaştırılabilir.", - "comparingFiles": "Dosyalar karşılaştırılıyor: {{file1}} ve {{file2}}", - "dragFailed": "Sürükleme işlemi başarısız oldu", - "filePinnedSuccessfully": "\"{{name}}\" dosyası başarıyla sabitlendi", - "pinFileFailed": "Dosyayı sabitleme başarısız oldu.", - "fileUnpinnedSuccessfully": "\"{{name}}\" dosyasının sabitlemesi başarıyla kaldırıldı.", - "unpinFileFailed": "Dosyanın sabitlemesi kaldırılamadı.", - "shortcutAddedSuccessfully": "Klasör kısayolu \"{{name}}\" başarıyla eklendi", - "addShortcutFailed": "Kısayol ekleme başarısız oldu.", - "operationCompletedSuccessfully": "{{operation}} {{count}} öğe başarıyla", - "operationCompleted": "{{operation}} {{count}} öğeler", - "downloadFileSuccess": "{{name}} dosyası başarıyla indirildi.", - "downloadFileFailed": "İndirme başarısız oldu", - "moveTo": "{{name}} adresine geçin", - "diffCompareWith": "{{name}} ile karşılaştırın", - "dragOutsideToDownload": "İndirmek için pencerenin dışına sürükleyin ({{count}} dosya)", - "newFolderDefault": "YeniKlasör", - "newFileDefault": "YeniDosya.txt", - "successfullyMovedItems": "{{count}} öğe başarıyla {{target}} konumuna taşındı.", - "move": "Taşınmak", - "searchInFile": "Dosyada arama yap (Ctrl+F)", - "showKeyboardShortcuts": "Klavye kısayollarını göster", - "startWritingMarkdown": "Markdown içeriğinizi yazmaya başlayın...", - "loadingFileComparison": "Dosya karşılaştırması yükleniyor...", - "reload": "Yeniden yükle", - "compare": "Karşılaştırmak", - "sideBySide": "Yan yana", - "inline": "Çizgide", - "fileComparison": "Dosya Karşılaştırması: {{file1}} ile {{file2}}", - "fileTooLarge": "Dosya çok büyük: {{error}}", - "sshConnectionFailed": "SSH bağlantısı başarısız oldu. Lütfen {{name}} ({{ip}}:{{port}} ) ile olan bağlantınızı kontrol edin.", - "loadFileFailed": "Dosya yüklenemedi: {{error}}", - "connecting": "Bağlanıyor...", - "connectedSuccessfully": "Bağlantı başarıyla kuruldu.", - "totpVerificationFailed": "TOTP doğrulaması başarısız oldu", - "warpgateVerificationFailed": "Warpgate kimlik doğrulaması başarısız oldu", - "authenticationFailed": "Kimlik doğrulama başarısız oldu", - "verificationCodePrompt": "Doğrulama kodu:", - "changePermissions": "İzinleri Değiştir", - "currentPermissions": "Mevcut İzinler", - "owner": "Mal sahibi", - "group": "Grup", - "others": "Diğerleri", - "read": "Okumak", - "write": "Yazmak", - "execute": "Uygulamak", - "permissionsChangedSuccessfully": "İzinler başarıyla değiştirildi.", - "failedToChangePermissions": "İzinleri değiştirme işlemi başarısız oldu.", - "name": "İsim", - "sortByName": "İsim", - "sortByDate": "Değiştirilme Tarihi", - "sortBySize": "Boyut", - "ascending": "Yükselen", - "descending": "İnen", - "root": "Kök", - "new": "Yeni", - "sortBy": "Göre sırala", - "items": "Öğeler", - "selected": "Seçildi", - "editor": "Editör", - "octal": "Sekizli", - "storage": "Depolamak", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", "disk": "Disk", - "used": "Kullanılmış", - "of": "ile ilgili", - "toggleSidebar": "Kenar Çubuğunu Aç/Kapat", - "cannotLoadPdf": "PDF yüklenemiyor.", - "pdfLoadError": "Bu PDF dosyasını yüklerken bir hata oluştu.", - "loadingPdf": "PDF yükleniyor...", - "loadingPage": "Sayfa yükleniyor..." + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Sunucuya kopyala…", - "moveToHost": "Ana bilgisayara geç…", - "copyItemsToHost": "{{count}} öğeyi… sunucuya kopyala", - "moveItemsToHost": "{{count}} öğeyi… ana bilgisayara taşı", - "noHostsConnected": "Başka dosya yöneticisi sunucusu mevcut değil.", - "noHostsConnectedHint": "Host Manager'da Dosya Yöneticisi etkinleştirilmiş başka bir SSH sunucusu ekleyin.", - "selectDestinationHost": "Hedef sunucuyu seçin", - "destinationPath": "Hedef yol", - "recentDestinations": "Son destinasyonlar", - "collapseRecentDestinations": "Son gidilen yerleri gizle", - "expandRecentDestinations": "Son gidilen yerleri genişlet", - "browseFolders": "Hedef klasörlere göz atın", - "browseDestination": "Yola göz atın veya yolu girin", - "confirmCopy": "Kopyala", - "confirmMove": "Taşınmak", - "transferring": "… aktarılıyor", - "compressing": "… sıkıştırılıyor", - "extracting": "… çıkarılıyor", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", "transferringItems": "Transferring {{current}} of {{total}} items…", - "transferSuccess": "Transfer tamamlandı", - "transferError": "Aktarım başarısız oldu", - "transferPartial": "Transfer {{count}} hatayla tamamlandı", - "transferPartialHint": "Aktarım başarısız oldu: {{paths}}", - "itemsSummary": "{{count}} öğe", - "destMustBeDirectory": "Çoklu öğe transferleri için hedef bir dizin olmalıdır.", - "selectThisFolder": "Bu klasörü seçin", - "browsePathWillBeCreated": "Bu klasör henüz mevcut değil. Aktarım başladığında oluşturulacaktır.", - "browsePathError": "Hedef sunucuda bu yolu açamadım.", - "goUp": "Yukarı çık", - "copyFolderToHost": "Klasörü sunucuya kopyala…", - "moveFolderToHost": "Klasörü sunucuya taşı…", - "hostReady": "Hazır", - "hostConnecting": "… Bağlanıyor", - "hostDisconnected": "Bağlı değil", - "hostAuthRequired": "Kimlik doğrulaması gerekiyor — önce bu sunucuda Dosya Yöneticisini açın.", - "hostConnectionFailed": "Bağlantı başarısız oldu.", - "metricsTitle": "Aktarma zamanlamaları", - "metricsPrepare": "Hedefi hazırlayın: {{duration}}", - "metricsCompress": "Kaynak kodda sıkıştır: {{duration}}", - "metricsHopSourceRead": "Kaynak → sunucu: {{throughput}}", - "metricsHopDestSftpWrite": "Sunucu → hedef (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Sunucu → hedef (yerel): {{throughput}}", - "metricsTransfer": "Uçtan uca: {{throughput}} ({{duration}})", - "metricsExtract": "Hedefe çıkar: {{duration}}", - "metricsSourceDelete": "Kaynaktan kaldır: {{duration}}", - "metricsTotal": "Toplam: {{duration}}", - "progressCompressing": "Kaynak sunucuda sıkıştırma işlemi…", - "progressExtracting": "Hedefe çıkarılıyor…", - "progressTransferring": "Veri aktarılıyor…", - "progressReconnecting": "Yeniden Bağlanıyor…", - "parallelSegmentsLabel": "Paralel aktarma şeritleri", - "parallelSegmentsOption": "{{count}} şeritler", - "parallelSegmentsHint": "Büyük dosyalar 256 MB'lık parçalara bölünür. Çoklu şeritler, daha yüksek toplam verim için ayrı bağlantılar kullanır (birkaç aktarım başlatmak gibi).", - "progressTotalSpeed": "{{speed}} toplam ({{lanes}} şerit)", - "progressTransferringItems": "Dosyalar aktarılıyor ({{current}} / {{total}})…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} dosyaları", - "sourceNotDeletedPartial": "Kaynak dosyalar saklandı (kısmi aktarım)", - "jumpHostLimitation": "Her iki sunucuya da Termix sunucusundan erişilebilmelidir. Doğrudan sunucular arası yönlendirme desteklenmemektedir.", - "cancel": "İptal etmek", - "methodLabel": "Aktarım yöntemi", - "methodAuto": "Otomatik", - "methodTar": "Tar arşivi", - "methodItemSftp": "Dosya başına SFTP", - "methodAutoHint": "Dosya sayısına, boyutuna ve sıkıştırılabilirliğine bağlı olarak tar veya dosya bazlı SFTP'yi seçer. Tek dosyalar her zaman akışlı SFTP kullanır.", - "methodTarHint": "Kaynakta sıkıştır, tek bir arşiv olarak aktar, hedefte çıkar. Her iki Unix sunucusunda da tar programı gereklidir.", - "methodItemSftpHint": "Her dosyayı SFTP üzerinden ayrı ayrı aktarın. Windows dahil tüm işletim sistemlerinde çalışır.", - "methodPreviewLoading": "Aktarım yöntemini hesaplama…", - "methodPreviewError": "Aktarım yönteminin önizlemesi yapılamadı. Sunucu, siz başlattığınızda yine de bir yöntem seçecektir.", - "methodPreviewWillUseTar": "Kullanılacak dosya: Tar arşivi", - "methodPreviewWillUseItemSftp": "Kullanılacak yöntem: Dosya başına SFTP", - "methodPreviewScanSummary": "{{fileCount}} dosya, {{totalSize}} toplam (kaynak sunucuda tarandı).", - "methodItemSftpLimitation": "Her dosya, tek bir dosya kopyası gibi aynı SFTP akışını kullanır ve bu işlem art arda gerçekleşir. İlerleme tüm dosyalarda birleştirilir, bu nedenle büyük dosyalarda ilerleme çubuğu yavaş hareket eder.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Dosya bazlı SFTP'yi seçtiniz.", - "user_tar": "Tar arşivini seçtiniz.", - "tar_unavailable": "Tar, sunuculardan birinde veya her ikisinde de mevcut değil; bunun yerine dosya başına SFTP kullanılacaktır.", - "windows_host": "Windows işletim sistemli bir sunucu söz konusu; tar kullanılmıyor.", - "auto_multi_large": "Otomatik: Sıkıştırılabilir veriler içeren büyük bir dosya ({{largestSize}}) dahil olmak üzere birden fazla dosya — tar, dosyaları tek bir aktarımda birleştirir.", - "auto_single_large_in_archive": "Otomatik: Bu kümede bir büyük dosya ({{largestSize}}) — dosya başına SFTP.", - "auto_many_incompressible": "Otomatik: çoğunlukla sıkıştırılamayan veriler — dosya başına SFTP.", - "auto_many_files": "Otomatik: birçok dosya ({{fileCount}}) — tar, dosya başına ek yükü azaltır.", - "auto_default": "Otomatik: Bu set için dosya başına SFTP." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "İptal etmek", - "progressCancelling": "… iptal ediliyor", - "progressStalled": "Durakladı", - "resumedHint": "Başka bir pencerede başlatılan aktif bir aktarıma yeniden bağlandı.", - "transferCancelled": "Transfer iptal edildi.", - "transferCancelledCopyHint": "Dosyaların bir kısmı hedef klasörde kalabilir.", - "transferCancelledMoveHint": "Hedef klasörde kısmi dosyalar kalmış olabilir. Kaynak dosyalar silinmemiştir.", - "cleanupDestFiles": "Temizlik hedefi", - "cleanupDestFilesSuccess": "Hedef konumdan kısmi dosyalar kaldırıldı.", - "cleanupDestFilesPartial": "Bazı kısmi dosyalar silinemedi.", - "cleanupDestFilesNothing": "Varış noktasında temizlenecek bir şey yok.", - "cleanupDestFilesError": "Temizleme işlemi başarısız oldu", - "retryTransfer": "Tekrar dene", - "retryTransferError": "Yeniden deneme başarısız oldu.", - "transferFailedRetryHint": "Hedef cihazda kısmi veriler saklandı. Bağlantı yeniden kurulduğunda yeniden deneme işlemi devam edecektir." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "SSH Tüneli Yok", - "createFirstTunnelMessage": "Henüz hiçbir SSH tüneli oluşturmadınız. Başlamak için Ana Bilgisayar Yöneticisi'nde tünel bağlantılarını yapılandırın.", - "connected": "Bağlı", - "disconnected": "Bağlantı kesildi", - "connecting": "Bağlanıyor...", - "error": "Hata", - "canceling": "İptal...", - "connect": "Bağlamak", - "disconnect": "Bağlantıyı kes", - "cancel": "İptal etmek", - "port": "Liman", - "localPort": "Yerel Liman", - "remotePort": "Uzak Liman", - "currentHostPort": "Geçerli Ana Bilgisayar Bağlantı Noktası", - "endpointPort": "Uç Nokta Bağlantı Noktası", - "bindIp": "Yerel IP", - "endpointSshConfig": "Uç Nokta SSH Yapılandırması", - "endpointSshHost": "Uç Nokta SSH Sunucusu", - "endpointSshHostPlaceholder": "Yapılandırılmış bir ana bilgisayar seçin", - "endpointSshHostRequired": "Her istemci tüneli için bir uç nokta SSH sunucusu seçin.", - "attempt": "{{max}}'in {{current}} denemesi", - "nextRetryIn": "Sonraki deneme {{seconds}} saniye sonra", - "clientTunnels": "İstemci Tünelleri", - "clientTunnel": "İstemci Tüneli", - "addClientTunnel": "İstemci Tüneli Ekle", - "noClientTunnels": "Bu masaüstünde yapılandırılmış istemci tüneli yok.", - "tunnelName": "Tünel Adı", - "remoteHost": "Uzak Sunucu", - "autoStart": "Otomatik Başlatma", - "clientAutoStartDesc": "Bu masaüstü istemcisi açıldığında başlar ve bağlantı sürekli devam eder.", - "clientManualStartDesc": "Başlat ve Durdur düğmelerini bu satırdan kullanın. Termix bunu otomatik olarak açmayacaktır.", - "clientRemoteServerNote": "Uzak yönlendirme, uç nokta SSH sunucusunda AllowTcpForwarding ve GatewayPorts ayarlarının etkinleştirilmesini gerektirebilir. Bu masaüstü bağlantısı kesildiğinde uzak port kapanır.", - "clientTunnelStarted": "İstemci tüneli başlatıldı", - "clientTunnelStopped": "İstemci tüneli durduruldu", - "tunnelTestSucceeded": "Tünel testi başarılı oldu.", - "tunnelTestFailed": "Tünel testi başarısız oldu", - "localSaved": "İstemci tünelleri kaydedildi", - "localSaveError": "Yerel istemci tünelleri kaydedilemedi.", - "invalidBindIp": "Yerel IP adresi geçerli bir IPv4 adresi olmalıdır.", - "invalidLocalTargetIp": "Yerel hedef IP adresi geçerli bir IPv4 adresi olmalıdır.", - "invalidLocalPort": "Yerel port numarası 1 ile 65535 arasında olmalıdır.", - "invalidRemotePort": "Uzak port numarası 1 ile 65535 arasında olmalıdır.", - "invalidLocalTargetPort": "Yerel hedef port numarası 1 ile 65535 arasında olmalıdır.", - "invalidEndpointPort": "Uç nokta bağlantı noktası 1 ile 65535 arasında olmalıdır.", - "duplicateAutoStartBind": "Yalnızca bir otomatik başlatma istemci tüneli {{bind}} kullanabilir.", - "manualControlError": "Tünel durumunu güncelleme başarısız oldu.", - "active": "Aktif", - "start": "Başlangıç", - "stop": "Durmak", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", "test": "Test", - "type": "Tünel Tipi", - "typeLocal": "Yerel (-L)", - "typeRemote": "Uzaktan (-R)", - "typeDynamic": "Dinamik (-D)", - "typeServerLocalDesc": "Mevcut sunucudan uç noktaya.", - "typeServerRemoteDesc": "Uç nokta mevcut sunucuya geri dönüyor.", - "typeClientLocalDesc": "Yerel bilgisayardan uç noktaya.", - "typeClientRemoteDesc": "Uç nokta yerel bilgisayara geri döndü.", - "typeClientDynamicDesc": "Yerel bilgisayarda SOCKS.", - "typeDynamicDesc": "SOCKS5 CONNECT trafiğini SSH üzerinden ilet", - "forwardDescriptionServerLocal": "Geçerli sunucu {{sourcePort}} → uç nokta {{endpointPort}}.", - "forwardDescriptionServerRemote": "Uç nokta {{endpointPort}} → geçerli sunucu {{sourcePort}}.", - "forwardDescriptionServerDynamic": "Mevcut sunucuda SOCKS {{sourcePort}}.", - "forwardDescriptionClientLocal": "Yerel {{sourcePort}} → uzak {{endpointPort}}.", - "forwardDescriptionClientRemote": "Uzak {{sourcePort}} → yerel {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS yerel portta {{sourcePort}}.", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → Çoraplar {{endpoint}} aracılığıyla", - "autoNameClientLocal": "Yerel {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → yerel {{localPort}}", - "autoNameClientDynamic": "ÇORAPLAR {{localPort}} aracılığıyla {{endpoint}}", - "route": "Rota:", - "lastStarted": "Son başlangıç", - "lastTested": "Son test tarihi:", - "lastError": "Son hata", - "maxRetries": "Maksimum Yeniden Deneme Sayısı", - "maxRetriesDescription": "Maksimum yeniden deneme sayısı.", - "retryInterval": "Tekrar Deneme Aralığı (saniye)", - "retryIntervalDescription": "Yeniden deneme girişimleri arasında beklenecek süre.", - "local": "Yerel", - "remote": "Uzak", - "destination": "Varış noktası", - "host": "Ev sahibi", - "mode": "Mod", - "noHostSelected": "Sunucu seçilmedi.", - "working": "Çalışma..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "İşlemci", - "memory": "Hafıza", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", "disk": "Disk", - "network": "Ağ", - "uptime": "Çalışma süresi", - "processes": "Süreçler", - "available": "Mevcut", - "free": "Özgür", - "connecting": "Bağlanıyor...", - "connectionFailed": "Sunucuya bağlanılamadı.", - "naCpus": "N/A CPU(lar)", - "cpuCores_one": "{{count}} Çekirdek", - "cpuCores_other": "{{count}} Çekirdekler", - "cpuUsage": "CPU Kullanımı", - "memoryUsage": "Bellek Kullanımı", - "diskUsage": "Disk Kullanımı", - "failedToFetchHostConfig": "Ana bilgisayar yapılandırması alınamadı.", - "serverOffline": "Sunucu Çevrimdışı", - "cannotFetchMetrics": "Çevrimdışı sunucudan ölçümler alınamıyor.", - "totpFailed": "TOTP doğrulaması başarısız oldu", - "noneAuthNotSupported": "Sunucu İstatistikleri 'none' kimlik doğrulama türünü desteklemiyor.", - "load": "Yük", - "systemInfo": "Sistem Bilgileri", - "hostname": "Ana bilgisayar adı", - "operatingSystem": "İşletim Sistemi", - "kernel": "Çekirdek", - "seconds": "saniyeler", - "networkInterfaces": "Ağ Arayüzleri", - "noInterfacesFound": "Ağ arayüzü bulunamadı.", - "noProcessesFound": "Hiçbir işlem bulunamadı.", - "loginStats": "SSH Giriş İstatistikleri", - "noRecentLoginData": "Son oturum açma verisi yok.", - "executingQuickAction": "{{name}} yürütülüyor...", - "quickActionSuccess": "{{name}} başarıyla tamamlandı", - "quickActionFailed": "{{name}} başarısız oldu", - "quickActionError": "{{name}} yürütülemedi", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Dinleme Portları", - "protocol": "Protokol", - "port": "Liman", - "address": "Adres", - "process": "İşlem", - "noData": "Dinleme portlarına ait veri yok." + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Güvenlik Duvarı", - "inactive": "Etkin değil", - "policy": "Politika", - "rules": "tüzük", - "noData": "Güvenlik duvarı verisi mevcut değil.", - "action": "Aksiyon", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Liman", - "source": "Kaynak", - "anywhere": "Herhangi bir yer" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Yük Ortalaması", - "swap": "Takas", - "architecture": "Mimari", - "refresh": "Yenile", - "retry": "Tekrar dene" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Kendi sunucunuzda barındırılan SSH ve uzaktan masaüstü yönetimi", - "loginTitle": "Termix'e giriş yapın", - "registerTitle": "Hesap oluşturmak", - "forgotPassword": "Parolanızı mı unuttunuz?", - "rememberMe": "Cihazı 30 Gün Boyunca Hatırlayın (TOTP Dahil)", - "noAccount": "Hesabınız yok mu?", - "hasAccount": "Zaten hesabınız var mı?", - "twoFactorAuth": "İki Faktörlü Kimlik Doğrulama", - "enterCode": "Doğrulama kodunu girin", - "backupCode": "Veya yedek kod kullanın.", - "verifyCode": "Kodu Doğrula", - "redirectingToApp": "Uygulamaya yönlendiriliyor...", - "sshAuthenticationRequired": "SSH Kimlik Doğrulaması Gerekli", - "sshNoKeyboardInteractive": "Klavye Etkileşimli Kimlik Doğrulama Kullanılamıyor", - "sshAuthenticationFailed": "Kimlik doğrulama başarısız oldu.", - "sshAuthenticationTimeout": "Kimlik Doğrulama Zaman Aşımı", - "sshNoKeyboardInteractiveDescription": "Sunucu, klavye etkileşimli kimlik doğrulamasını desteklemiyor. Lütfen parolanızı veya SSH anahtarınızı girin.", - "sshAuthFailedDescription": "Girdiğiniz kimlik bilgileri hatalıydı. Lütfen geçerli kimlik bilgileriyle tekrar deneyin.", - "sshTimeoutDescription": "Kimlik doğrulama denemesi zaman aşımına uğradı. Lütfen tekrar deneyin.", - "sshProvideCredentialsDescription": "Lütfen bu sunucuya bağlanmak için SSH kimlik bilgilerinizi girin.", - "sshPasswordDescription": "Bu SSH bağlantısı için şifreyi girin.", - "sshKeyPasswordDescription": "SSH anahtarınız şifrelenmişse, parolayı buraya girin.", - "passphraseRequired": "Parola Gerekli", - "passphraseRequiredDescription": "SSH anahtarı şifrelenmiştir. Lütfen kilidi açmak için parola girin.", - "back": "Geri", - "firstUser": "İlk Kullanıcı", - "firstUserMessage": "İlk kullanıcı sizsiniz ve yönetici olarak atanacaksınız. Yönetici ayarlarını yan menüdeki kullanıcı açılır menüsünden görüntüleyebilirsiniz. Bunun bir hata olduğunu düşünüyorsanız, Docker günlüklerini kontrol edin veya GitHub'da bir sorun bildirin.", - "external": "Harici", - "loginWithExternal": "Harici Sağlayıcı ile Giriş Yap", - "loginWithExternalDesc": "Yapılandırdığınız harici kimlik sağlayıcınızı kullanarak giriş yapın.", - "externalNotSupportedInElectron": "Electron uygulamasında harici kimlik doğrulama henüz desteklenmemektedir. OIDC girişi için lütfen web sürümünü kullanın.", - "resetPasswordButton": "Şifreyi Sıfırla", - "sendResetCode": "Sıfırlama kodunu gönder", - "resetCodeDesc": "Parola sıfırlama kodu almak için kullanıcı adınızı girin. Kod, Docker konteyner günlüklerine kaydedilecektir.", - "resetCode": "Sıfırlama Kodu", - "verifyCodeButton": "Kodu Doğrula", - "enterResetCode": "Kullanıcıya ait Docker konteyner günlüklerinden 6 haneli kodu girin:", - "newPassword": "Yeni Şifre", - "confirmNewPassword": "Şifreyi Onayla", - "enterNewPassword": "Kullanıcı için yeni şifrenizi girin:", - "signUp": "Üye olmak", - "desktopApp": "Masaüstü Uygulaması", - "loggingInToDesktopApp": "Masaüstü uygulamasına giriş yapılıyor.", - "loadingServer": "Sunucu yükleniyor...", - "dataLossWarning": "Parolanızı bu şekilde sıfırlamak, kaydedilmiş tüm SSH sunucularınızı, kimlik bilgilerinizi ve diğer şifrelenmiş verilerinizi silecektir. Bu işlem geri alınamaz. Bunu yalnızca parolanızı unuttuysanız ve oturum açmadıysanız kullanın.", - "authenticationDisabled": "Kimlik Doğrulama Devre Dışı Bırakıldı", - "authenticationDisabledDesc": "Tüm kimlik doğrulama yöntemleri şu anda devre dışı bırakılmıştır. Lütfen yöneticinizle iletişime geçin.", - "attemptsRemaining": "{{count}} deneme kaldı" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "SSH Ana Bilgisayar Anahtarını Doğrulayın", - "keyChangedWarning": "SSH Ana Bilgisayar Anahtarı Değiştirildi", - "firstConnectionTitle": "Bu sunucuya ilk kez bağlanıyorum.", - "firstConnectionDescription": "Bu sunucunun kimliği doğrulanamamıştır. Parmak izinin beklediğinizle eşleştiğini doğrulayın.", - "keyChangedDescription": "Bu sunucunun anahtar kodu son bağlantınızdan bu yana değişti. Bu bir güvenlik sorununa işaret edebilir.", - "previousKey": "Önceki Anahtar", - "newFingerprint": "Yeni Parmak İzi", - "fingerprint": "Parmak izi", - "verifyInstructions": "Bu sunucuya güveniyorsanız, devam etmek ve bu parmak izini gelecekteki bağlantılar için kaydetmek üzere Kabul Et'e tıklayın.", - "securityWarning": "Güvenlik Uyarısı", - "acceptAndContinue": "Kabul Et ve Devam Et", - "acceptNewKey": "Yeni Anahtarı Kabul Et ve Devam Et" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Veritabanına bağlanılamadı.", - "unknownError": "Bilinmeyen hata", - "loginFailed": "giriş başarısız oldu", - "failedPasswordReset": "Parola sıfırlama işlemi başlatılamadı.", - "failedVerifyCode": "Sıfırlama kodunu doğrulama başarısız oldu.", - "failedCompleteReset": "Parola sıfırlama işlemi tamamlanamadı.", - "invalidTotpCode": "Geçersiz TOTP kodu", - "failedOidcLogin": "OIDC oturum açma işlemi başlatılamadı.", - "silentSigninOidcUnavailable": "Sessiz oturum açma isteği yapıldı, ancak OIDC oturum açma seçeneği mevcut değil.", - "failedUserInfo": "Giriş yaptıktan sonra kullanıcı bilgilerini alma işlemi başarısız oldu.", - "oidcAuthFailed": "OIDC kimlik doğrulaması başarısız oldu", - "invalidAuthUrl": "Arka uçtan geçersiz yetkilendirme URL'si alındı.", - "requiredField": "Bu alan zorunludur.", - "minLength": "Minimum uzunluk {{min}}", - "passwordMismatch": "Şifreler eşleşmiyor.", - "passwordLoginDisabled": "Kullanıcı adı/şifre ile giriş şu anda devre dışı bırakılmıştır.", - "sessionExpired": "Oturumunuzun süresi doldu - lütfen tekrar giriş yapın.", - "totpRateLimited": "Hız sınırlaması: Çok fazla TOTP doğrulama denemesi yapıldı. Lütfen daha sonra tekrar deneyin.", - "totpRateLimitedWithTime": "Hız sınırlaması: Çok fazla TOTP doğrulama denemesi yapıldı. Lütfen tekrar denemeden önce {{time}} saniye bekleyin.", - "resetCodeRateLimited": "Hız sınırlaması: Çok fazla doğrulama denemesi yapıldı. Lütfen daha sonra tekrar deneyin.", - "resetCodeRateLimitedWithTime": "Hız sınırlaması: Çok fazla doğrulama denemesi yapıldı. Lütfen tekrar denemeden önce {{time}} saniye bekleyin.", - "authTokenSaveFailed": "Kimlik doğrulama belirteci kaydedilemedi.", - "failedToLoadServer": "Sunucu yüklenemedi." + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Yeni hesap kaydı şu anda bir yönetici tarafından devre dışı bırakılmıştır. Lütfen giriş yapın veya bir yöneticiyle iletişime geçin.", - "userNotAllowed": "Hesabınızın kayıt yetkisi bulunmamaktadır. Lütfen bir yöneticiyle iletişime geçin.", - "databaseConnectionFailed": "Veritabanı sunucusuna bağlanılamadı.", - "resetCodeSent": "Docker günlüklerine sıfırlama kodu gönderildi.", - "codeVerified": "Kod başarıyla doğrulandı.", - "passwordResetSuccess": "Parola sıfırlama işlemi başarıyla tamamlandı.", - "loginSuccess": "Giriş başarılı", - "registrationSuccess": "Kayıt işlemi başarılı." + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Yapılandırılmış SSH sunucularını hedefleyen yerel masaüstü tünelleri.", - "c2sTunnelPresets": "İstemci Tüneli Ön Ayarları", - "c2sTunnelPresetsDesc": "Bu masaüstü istemcisinin yerel tünel listesini adlandırılmış bir sunucu ön ayarı olarak kaydedin veya bu istemciye bir ön ayar yükleyin.", - "c2sTunnelPresetsUnavailable": "İstemci tüneli ön ayarları yalnızca masaüstü istemcisinde mevcuttur.", - "c2sPresetName": "Ön Ayar Adı", - "c2sPresetNamePlaceholder": "Müşteri ön ayar adı", - "c2sPresetToLoad": "Yüklemek için ön ayar", - "c2sNoPresetSelected": "Önceden seçilmiş bir seçenek yok.", - "c2sNoPresets": "Kaydedilmiş ön ayar yok.", - "c2sLoadPreset": "Yük", - "c2sCurrentLocalConfig": "Bu masaüstünde yapılandırılmış {{count}} yerel istemci tüneli.", - "c2sPresetSyncNote": "Ön ayarlar, açıkça belirtilmiş anlık görüntülerdir; bunlardan birini yüklemek, bu masaüstü istemcisinin yerel istemci tünel listesini değiştirir.", - "c2sPresetSaved": "İstemci tüneli ön ayarı kaydedildi", - "c2sPresetLoaded": "İstemci tüneli ön ayarı yerel olarak yüklendi.", - "c2sPresetRenamed": "İstemci tüneli ön ayarı yeniden adlandırıldı.", - "c2sPresetDeleted": "İstemci tüneli ön ayarı silindi", - "c2sPresetLoadError": "İstemci tünel ön ayarları yüklenemedi." + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Dil", - "keyPassword": "anahtar şifresi", - "pastePrivateKey": "Özel anahtarınızı buraya yapıştırın...", - "localListenerHost": "127.0.0.1 (yerel olarak dinleyin)", - "localTargetHost": "127.0.0.1 (bu bilgisayardaki hedef)", - "socksListenerHost": "127.0.0.1 (Çorap dinleyicisi)", - "enterPassword": "Şifrenizi girin", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Kontrol Paneli", - "loading": "Kontrol paneli yükleniyor...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Destek", + "support": "Support", "discord": "Discord", - "serverOverview": "Sunucuya Genel Bakış", - "version": "Sürüm", - "upToDate": "Güncel", - "updateAvailable": "Güncelleme Mevcut", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "Çalışma süresi", - "database": "Veritabanı", - "healthy": "Sağlıklı", - "error": "Hata", - "totalHosts": "Toplam Ev Sahibi Sayısı", - "totalTunnels": "Toplam Tüneller", - "totalCredentials": "Toplam Kimlik Bilgileri", - "recentActivity": "Son Aktiviteler", - "reset": "Sıfırla", - "loadingRecentActivity": "Son etkinlikler yükleniyor...", - "noRecentActivity": "Son zamanlarda herhangi bir aktivite yok.", - "quickActions": "Hızlı İşlemler", - "addHost": "Sunucu Ekle", - "addCredential": "Kimlik Bilgisi Ekle", - "adminSettings": "Yönetici Ayarları", - "userProfile": "Kullanıcı Profili", - "serverStats": "Sunucu İstatistikleri", - "loadingServerStats": "Sunucu istatistikleri yükleniyor...", - "noServerData": "Sunucu verisi mevcut değil.", - "cpu": "İşlemci", - "ram": "Veri deposu", - "customizeLayout": "Kontrol Panelini Özelleştir", - "dashboardSettings": "Kontrol Paneli Ayarları", - "enableDisableCards": "Kartları Etkinleştir/Devre Dışı Bırak", - "resetLayout": "Varsayılan ayarlara sıfırla", - "serverOverviewCard": "Sunucuya Genel Bakış", - "recentActivityCard": "Son Aktiviteler", - "networkGraphCard": "Ağ Grafiği", - "networkGraph": "Ağ Grafiği", - "quickActionsCard": "Hızlı İşlemler", - "serverStatsCard": "Sunucu İstatistikleri", - "panelMain": "Ana", - "panelSide": "Taraf", - "justNow": "Şu anda" + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "STABİL", - "hostsOnline": "Çevrimiçi Sunucular", - "activeTunnels": "Aktif Tüneller", - "registerNewServer": "Yeni bir sunucu kaydet", - "storeSshKeysOrPasswords": "SSH anahtarlarını veya şifrelerini saklayın.", - "manageUsersAndRoles": "Kullanıcıları ve rolleri yönetin", - "manageYourAccount": "Hesabınızı yönetin", - "hostStatus": "Sunucu Durumu", - "noHostsConfigured": "Yapılandırılmış sunucu yok", - "online": "ÇEVRİMİÇİ", - "offline": "ÇEVRİMDIŞI", - "onlineLower": "Çevrimiçi", - "nodes": "{{count}} düğümler", - "add": "Eklemek:", - "commandPalette": "Komut Paleti", - "done": "Tamamlamak", - "editModeInstructions": "Kartları sürükleyerek yeniden sıralayın · Sütun ayırıcıyı sürükleyerek sütunların boyutunu değiştirin · Bir kartın alt kenarını sürükleyerek yüksekliğini değiştirin · Çöp kutusuna taşıyarak silin", - "empty": "Boş", - "clear": "Temizlemek" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Sunucu Ekle", - "addGroup": "Grup Ekle", - "addLink": "Bağlantı Ekle", - "zoomIn": "Yakınlaştır", - "zoomOut": "Uzaklaştır", - "resetView": "Görünümü Sıfırla", - "selectHost": "Sunucu Seçin", - "chooseHost": "Bir ev sahibi seçin...", - "parentGroup": "Ebeveyn Grubu", - "noGroup": "Grup Yok", - "groupName": "Grup Adı", - "color": "Renk", - "source": "Kaynak", - "target": "Hedef", - "moveToGroup": "Gruba Taşı", - "selectGroup": "Grup seçin...", - "addConnection": "Bağlantı Ekle", - "hostDetails": "Ev Sahibi Bilgileri", - "removeFromGroup": "Gruptan Kaldır", - "addHostHere": "Sunucu Ekle", - "editGroup": "Grup Düzenleme", - "delete": "Silmek", - "add": "Eklemek", - "create": "Yaratmak", - "move": "Taşınmak", - "connect": "Bağlamak", - "createGroup": "Grup Oluştur", - "selectSourcePlaceholder": "Kaynak Seçin...", - "selectTargetPlaceholder": "Hedefi Seç...", - "invalidFile": "Geçersiz Dosya", - "hostAlreadyExists": "Sunucu zaten topolojide yer alıyor.", - "connectionExists": "Bağlantı zaten mevcut.", - "unknown": "Bilinmiyor", - "name": "İsim", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "Durum", - "failedToAddNode": "Düğüm ekleme başarısız oldu.", - "sourceDifferentFromTarget": "Kaynak ve hedef farklı olmalıdır.", - "exportJSON": "JSON'u Dışa Aktar", - "importJSON": "JSON'u içe aktar", - "terminal": "terminal", - "fileManager": "Dosya Yöneticisi", - "tunnel": "Tünel", - "docker": "Liman işçisi", - "serverStats": "Sunucu İstatistikleri", - "noNodes": "Henüz düğüm yok." + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Bu sunucu için Docker etkinleştirilmemiş.", - "validating": "Docker doğrulaması yapılıyor...", - "connecting": "Bağlanıyor...", - "error": "Hata", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Docker'a bağlanılamadı.", - "containerStarted": "Konteyner {{name}} başlatıldı", - "failedToStartContainer": "{{name}} kapsayıcısı başlatılamadı.", - "containerStopped": "Konteyner {{name}} durduruldu", - "failedToStopContainer": "{{name}} kapsayıcısını durdurma başarısız oldu.", - "containerRestarted": "Konteyner {{name}} yeniden başlatıldı", - "failedToRestartContainer": "Kapsayıcıyı yeniden başlatma başarısız oldu {{name}}", - "containerPaused": "Konteyner {{name}} duraklatıldı", - "containerUnpaused": "Konteyner {{name}} duraklatılmamış", - "failedToTogglePauseContainer": "{{name}} kapsayıcısı için duraklatma durumunu değiştirme başarısız oldu.", - "containerRemoved": "Konteyner {{name}} kaldırıldı", - "failedToRemoveContainer": "{{name}} kapsayıcısını kaldırma işlemi başarısız oldu.", - "image": "Görüntü", - "ports": "Limanlar", - "noPorts": "Liman yok", - "start": "Başlangıç", - "confirmRemoveContainer": "'{{name}}' kapsayıcısını kaldırmak istediğinizden emin misiniz? Bu işlem geri alınamaz.", - "runningContainerWarning": "Uyarı: Bu konteyner şu anda çalışıyor. Kaldırılması durumunda konteyner önce durdurulacaktır.", - "loadingContainers": "Konteynerler yükleniyor...", - "manager": "Docker Yöneticisi", - "autoRefresh": "Otomatik Yenileme", - "timestamps": "Zaman damgaları", - "lines": "Çizgiler", - "filterLogs": "Günlükleri filtrele...", - "refresh": "Yenile", - "download": "İndirmek", - "clear": "Temizlemek", - "logsDownloaded": "Günlük dosyaları başarıyla indirildi.", - "last50": "Son 50", - "last100": "Son 100", - "last500": "Son 500", - "last1000": "Son 1000", - "allLogs": "Tüm Kayıtlar", - "noLogsMatching": "\"{{query}} \" ile eşleşen hiçbir günlük bulunamadı.", - "noLogsAvailable": "Kayıt bulunamadı.", - "noContainersFound": "Hiçbir konteyner bulunamadı.", - "noContainersFoundHint": "Bu sunucuda hiçbir Docker kapsayıcısı mevcut değil.", - "searchPlaceholder": "Konteynerleri ara...", - "allStatuses": "Tüm Durumlar", - "stateRunning": "Koşma", - "statePaused": "Duraklatıldı", - "stateExited": "Çıkış yapıldı", - "stateRestarting": "Yeniden Başlatılıyor", - "noContainersMatchFilters": "Filtrelerinizle eşleşen kap bulunamadı.", - "noContainersMatchFiltersHint": "Arama veya filtreleme kriterlerinizi değiştirmeyi deneyin.", - "failedToFetchStats": "Konteyner istatistikleri alınamadı.", - "containerNotRunning": "Konteyner çalışmıyor.", - "startContainerToViewStats": "İstatistikleri görüntülemek için konteyneri başlatın.", - "loadingStats": "İstatistikler yükleniyor...", - "errorLoadingStats": "İstatistikler yüklenirken hata oluştu.", - "noStatsAvailable": "İstatistik bilgisi mevcut değil.", - "cpuUsage": "CPU Kullanımı", - "current": "Akım", - "memoryUsage": "Bellek Kullanımı", - "networkIo": "Ağ G/Ç", - "input": "Giriş", - "output": "Çıktı", - "blockIo": "Blok G/Ç", - "read": "Okumak", - "write": "Yazmak", - "pids": "PID'ler", - "containerInformation": "Konteyner Bilgileri", - "name": "İsim", - "id": "İD", - "state": "Durum", - "containerMustBeRunning": "Konsola erişmek için konteynerin çalışır durumda olması gerekir.", - "verificationCodePrompt": "Doğrulama kodunu girin", - "totpVerificationFailed": "TOTP doğrulaması başarısız oldu. Lütfen tekrar deneyin.", - "warpgateVerificationFailed": "Warpgate kimlik doğrulaması başarısız oldu. Lütfen tekrar deneyin.", - "connectedTo": "{{containerName}} ile bağlantılı", - "disconnected": "Bağlantı kesildi", - "consoleError": "Konsol hatası", - "errorMessage": "Hata: {{message}}", - "failedToConnect": "Konteynere bağlanılamadı.", - "console": "Konsol", - "selectShell": "Kabuk seçin", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", - "sh": "ş", - "ash": "kül", - "connect": "Bağlamak", - "disconnect": "Bağlantıyı kes", - "notConnected": "Bağlı değil", - "clickToConnect": "Kabuk oturumu başlatmak için bağlan'a tıklayın.", - "connectingTo": "{{containerName}} ile bağlantı kuruluyor...", - "containerNotFound": "Konteyner bulunamadı.", - "backToList": "Listeye geri dön", - "logs": "Günlükler", - "stats": "İstatistikler", - "consoleTab": "Konsol", - "startContainerToAccess": "Konsola erişmek için konteyneri başlatın." + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Genel", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Kullanıcılar", - "sectionSessions": "Oturumlar", - "sectionRoles": "Roller", - "sectionDatabase": "Veritabanı", - "sectionApiKeys": "API Anahtarları", - "allowRegistration": "Kullanıcı Kaydına İzin Ver", - "allowRegistrationDesc": "Yeni kullanıcıların kendi kendilerine kayıt olmalarına izin verin.", - "allowPasswordLogin": "Şifreyle Girişe İzin Ver", - "allowPasswordLoginDesc": "Kullanıcı adı/şifre ile giriş yapın", - "oidcAutoProvision": "OIDC Otomatik Sağlama", - "oidcAutoProvisionDesc": "Kayıt devre dışı bırakılmış olsa bile OIDC kullanıcıları için otomatik olarak hesap oluşturun.", - "allowPasswordReset": "Parola Sıfırlamaya İzin Ver", - "allowPasswordResetDesc": "Docker günlükleri aracılığıyla kodu sıfırla", - "sessionTimeout": "Oturum Zaman Aşımı", - "hours": "saat", - "sessionTimeoutRange": "Minimum 1 saat · Maksimum 720 saat", - "monitoringDefaults": "Varsayılan Ayarları İzleme", - "statusCheck": "Durum Kontrolü", - "metrics": "Metrikler", - "sec": "saniye", - "logLevel": "Günlük Seviyesi", - "enableGuacamole": "Guacamole'yi etkinleştirin", - "enableGuacamoleDesc": "RDP/VNC uzaktan masaüstü", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "OpenID Connect'i SSO için yapılandırın. * ile işaretlenmiş alanlar zorunludur.", - "oidcClientId": "Müşteri Kimliği", - "oidcClientSecret": "Müşteri Gizli Bilgileri", - "oidcAuthUrl": "Yetkilendirme URL'si", - "oidcIssuerUrl": "Yayıncı URL'si", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", "oidcTokenUrl": "Token URL", - "oidcUserIdentifier": "Kullanıcı Tanımlayıcı Yolu", - "oidcDisplayName": "Görüntü Adı Yolu", - "oidcScopes": "Kapsamlar", - "oidcUserinfoUrl": "Kullanıcı bilgisi URL'sini geçersiz kıl", - "oidcAllowedUsers": "İzin Verilen Kullanıcılar", - "oidcAllowedUsersDesc": "Her satıra bir e-posta adresi yazın. Tüm e-posta adreslerinin görünmesi için boş bırakın.", - "removeOidc": "Kaldırmak", - "usersCount": "{{count}} kullanıcılar", - "createUser": "Yaratmak", - "newRole": "Yeni Rol", - "roleName": "İsim", - "roleDisplayName": "Ekran adı", - "roleDescription": "Tanım", - "rolesCount": "{{count}} roller", - "createRole": "Yaratmak", - "creating": "Oluşturuluyor...", - "exportDatabase": "Veritabanını Dışa Aktar", - "exportDatabaseDesc": "Tüm sunucuların, kimlik bilgilerinin ve ayarların yedek kopyasını indirin.", - "export": "İhracat", - "exporting": "İhracat...", - "importDatabase": "Veritabanını İçe Aktar", - "importDatabaseDesc": ".sqlite yedekleme dosyasından geri yükleme", - "importDatabaseSelected": "Seçilen: {{name}}", - "selectFile": "Dosya Seç", - "changeFile": "Değiştirmek", - "import": "İçe aktarmak", - "importing": "İçe aktarılıyor...", - "apiKeysCount": "{{count}} anahtarlar", - "newApiKey": "Yeni API Anahtarı", - "apiKeyCreatedWarning": "Anahtar oluşturuldu - şimdi kopyalayın, bir daha gösterilmeyecek.", - "apiKeyName": "İsim", - "apiKeyUser": "Kullanıcı", - "apiKeySelectUser": "Bir kullanıcı seçin...", - "apiKeyExpiresAt": "Son Geçerlilik Tarihi", - "createKey": "Anahtar Oluştur", - "apiKeyNoExpiry": "Son kullanma tarihi yok", - "revokedBadge": "İPTAL EDİLDİ", - "authTypeDual": "Çift Kimlik Doğrulama", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Yerel", - "adminStatusAdministrator": "Yönetici", - "adminStatusRegularUser": "Düzenli Kullanıcı", - "adminBadge": "YÖNETİCİ", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "GELENEK", - "youBadge": "SEN", - "sessionsActive": "{{count}} aktif", - "sessionActive": "Aktif: {{time}}", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tüm", - "revokeAllSessionsSuccess": "Kullanıcıya ait tüm oturumlar iptal edildi.", - "revokeAllSessionsFailed": "Oturumları iptal etme başarısız oldu.", - "revokeSessionFailed": "Oturumu iptal etme başarısız oldu.", - "addRole": "Rol ekle", - "noCustomRoles": "Tanımlanmış özel rol yok.", - "removeRoleFailed": "Rolü kaldırma işlemi başarısız oldu.", - "assignRoleFailed": "Rol atama işlemi başarısız oldu.", - "deleteRoleFailed": "Rol silme işlemi başarısız oldu.", - "userAdminAccess": "Yönetici", - "userAdminAccessDesc": "Tüm yönetici ayarlarına tam erişim", - "userRoles": "Roller", - "revokeAllUserSessions": "Tüm Oturumları İptal Et", - "revokeAllUserSessionsDesc": "Tüm cihazlarda yeniden oturum açmayı zorunlu kılın", - "revoke": "Geri çekmek", - "deleteUserWarning": "Bu kullanıcının silinmesi kalıcıdır.", - "deleteUser": "{{username}} silin", - "deleting": "Siliniyor...", - "deleteUserFailed": "Kullanıcı silme işlemi başarısız oldu.", - "deleteUserSuccess": "Kullanıcı \"{{username}}\" silindi", - "deleteRoleSuccess": "\"{{name}}\" rolü silindi", - "revokeKeySuccess": "\"{{name}}\" anahtarı iptal edildi", - "revokeKeyFailed": "Anahtarı iptal etme başarısız oldu.", - "copiedToClipboard": "Panoya kopyalandı", - "done": "Tamamlamak", - "createUserTitle": "Kullanıcı Oluştur", - "createUserDesc": "Yeni bir yerel hesap oluşturun.", - "createUserUsername": "Kullanıcı adı", - "createUserPassword": "Şifre", - "createUserPasswordHint": "En az 6 karakter.", - "createUserEnterUsername": "Kullanıcı adınızı girin", - "createUserEnterPassword": "Şifrenizi girin", - "createUserSubmit": "Kullanıcı Oluştur", - "editUserTitle": "Kullanıcıyı Yönet: {{username}}", - "editUserDesc": "Rolleri, yönetici durumunu, oturumları ve hesap ayarlarını düzenleyin.", - "editUserUsername": "Kullanıcı adı", - "editUserAuthType": "Kimlik Doğrulama Türü", - "editUserAdminStatus": "Yönetici Durumu", - "editUserUserId": "Kullanıcı kimliği", - "linkAccountTitle": "OIDC'yi Parola Hesabına Bağla", - "linkAccountDesc": "OIDC hesabını {{username}} mevcut bir yerel hesapla birleştirin.", - "linkAccountWarningTitle": "Bu şunları sağlayacak:", - "linkAccountEffect1": "Yalnızca OIDC'ye özel hesabı silin.", - "linkAccountEffect2": "Hedef hesaba OIDC oturum açma işlemini ekleyin.", - "linkAccountEffect3": "Hem OIDC hem de parola ile giriş yapılmasına izin verin.", - "linkAccountTargetUsername": "Hedef Kullanıcı Adı", - "linkAccountTargetPlaceholder": "Bağlantı kurmak istediğiniz yerel hesap kullanıcı adını girin.", - "linkAccounts": "Hesapları Bağla", - "linkAccountSuccess": "\"{{username}} \" ile bağlantılı OIDC hesabı", - "linkAccountFailed": "OIDC hesabının bağlanması başarısız oldu.", - "linkAccountInProgress": "Bağlantı kuruluyor...", - "saving": "Tasarruf...", - "updateRegistrationFailed": "Kayıt ayarlarını güncelleme başarısız oldu.", - "updatePasswordLoginFailed": "Parola giriş ayarlarını güncelleme başarısız oldu.", - "updateOidcAutoProvisionFailed": "OIDC otomatik sağlama ayarı güncellenemedi.", - "updatePasswordResetFailed": "Parola sıfırlama ayarı güncellenemedi.", - "sessionTimeoutRange2": "Oturum zaman aşımı 1 ile 720 saat arasında olmalıdır.", - "sessionTimeoutSaved": "Oturum zaman aşımı kaydedildi", - "sessionTimeoutSaveFailed": "Oturum zaman aşımını kaydetme başarısız oldu.", - "monitoringIntervalInvalid": "Geçersiz aralık değerleri", - "monitoringSaved": "İzleme ayarları kaydedildi.", - "monitoringSaveFailed": "İzleme ayarları kaydedilemedi.", - "guacamoleSaved": "Guacamole ayarları kaydedildi.", - "guacamoleSaveFailed": "Guacamole ayarları kaydedilemedi.", - "guacamoleUpdateFailed": "Guacamole ayarlarını güncelleme başarısız oldu.", - "logLevelUpdateFailed": "Günlük düzeyi güncellenemedi.", - "oidcSaved": "OIDC yapılandırması kaydedildi", - "oidcSaveFailed": "OIDC yapılandırması kaydedilemedi.", - "oidcRemoved": "OIDC yapılandırması kaldırıldı", - "oidcRemoveFailed": "OIDC yapılandırmasını kaldırma başarısız oldu.", - "createUserRequired": "Kullanıcı adı ve şifre gereklidir.", - "createUserPasswordTooShort": "Parola en az 6 karakter uzunluğunda olmalıdır.", - "createUserSuccess": "Kullanıcı \"{{username}}\" oluşturdu", - "createUserFailed": "Kullanıcı oluşturulamadı.", - "updateAdminStatusFailed": "Yönetici durumunu güncelleme başarısız oldu.", - "allSessionsRevoked": "Tüm oturumlar iptal edildi.", - "revokeSessionsFailed": "Oturumları iptal etme başarısız oldu.", - "createRoleRequired": "Ad ve görünen ad zorunludur.", - "createRoleSuccess": "\"{{name}}\" rolü oluşturuldu", - "createRoleFailed": "Rol oluşturulamadı.", - "apiKeyNameRequired": "Anahtar adı gereklidir.", - "apiKeyUserRequired": "Kullanıcı kimliği gereklidir.", - "apiKeyCreatedSuccess": "API anahtarı \"{{name}}\" oluşturuldu", - "apiKeyCreateFailed": "API anahtarı oluşturulamadı.", - "exportSuccess": "Veritabanı başarıyla dışa aktarıldı.", - "exportFailed": "Veritabanı dışa aktarma işlemi başarısız oldu.", - "importSelectFile": "Lütfen önce bir dosya seçin.", - "importCompleted": "İçe aktarma tamamlandı: {{total}} öğe içe aktarıldı, {{skipped}} öğe atlandı", - "importFailed": "İçe aktarma başarısız oldu: {{error}}", - "importError": "Veritabanı içe aktarma işlemi başarısız oldu." + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Ev sahibi", - "hostPlaceholder": "192.168.1.1 veya example.com", - "portLabel": "Liman", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Kullanıcı adı", - "usernamePlaceholder": "kullanıcı adı", - "authLabel": "Yetki", - "passwordLabel": "Şifre", - "passwordPlaceholder": "şifre", - "privateKeyLabel": "Özel Anahtar", - "privateKeyPlaceholder": "Özel anahtarı yapıştırın...", - "credentialLabel": "Kimlik belgesi", - "credentialPlaceholder": "Kaydedilmiş bir kimlik bilgisi seçin.", - "connectToTerminal": "Terminale bağlanın", - "connectToFiles": "Dosyalara bağlan" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Terminal seçilmedi.", - "noTerminalSelectedHint": "SSH terminal sekmesini açarak komut geçmişini görüntüleyin.", - "searchPlaceholder": "Arama geçmişi...", - "clearAll": "Tümünü Temizle", - "noHistoryEntries": "Geçmiş kaydı yok.", - "trackingDisabled": "Geçmiş izleme devre dışı bırakıldı", - "trackingDisabledHint": "Komutları kaydetmek için profil ayarlarınızda bu özelliği etkinleştirin." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Anahtar Kaydı", - "recordToTerminals": "Terminallere kayıt yapın", - "selectAll": "Tüm", - "selectNone": "Hiçbiri", - "noTerminalTabsOpen": "Açık terminal sekmesi yok.", - "selectTerminalsAbove": "Yukarıdaki terminalleri seçin", - "broadcastInputPlaceholder": "Tuş vuruşlarını yayınlamak için buraya yazın...", - "stopRecording": "Kaydı Durdur", - "startRecording": "Kaydı Başlat", - "settingsTitle": "Ayarlar", - "enableRightClickCopyPaste": "Sağ tıklama ile kopyala/yapıştır özelliğini etkinleştirin." + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Düzen", - "selectLayoutAbove": "Yukarıdan bir düzen seçin.", - "selectLayoutHint": "Görüntülenecek bölme sayısını seçin.", - "panesTitle": "Camlar", - "openTabsTitle": "Açık Sekmeler", - "dragTabsHint": "Sekmeleri yukarıdaki bölmelere sürükleyin veya Hızlı Atama özelliğini kullanın.", - "dropHere": "Buraya bırakın", - "emptyPane": "Boş", - "dashboard": "Kontrol Paneli", - "clearSplitScreen": "Ekranı Bölünmüş Hale Getir", - "quickAssign": "Hızlı Atama", - "alreadyAssigned": "Bölme {{index}}", - "splitTab": "Bölünmüş Sekme", - "addToSplit": "Bölünmüş bölüme ekle", - "removeFromSplit": "Bölünmüş halden çıkarın", - "assignToPane": "Bölmeye ata" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Kod Parçası Oluştur", - "createSnippetDescription": "Hızlı çalıştırma için yeni bir komut parçacığı oluşturun.", - "nameLabel": "İsim", - "namePlaceholder": "Örneğin, Nginx'i yeniden başlatın.", - "descriptionLabel": "Tanım", - "descriptionPlaceholder": "İsteğe bağlı açıklama", - "optional": "İsteğe bağlı", - "folderLabel": "Dosya", - "noFolder": "Klasör yok (Kategorilenmemiş)", - "commandLabel": "Emretmek", - "commandPlaceholder": "Örneğin, sudo systemctl restart nginx", - "cancel": "İptal etmek", - "createSnippetButton": "Kod Parçası Oluştur", - "createFolderTitle": "Klasör Oluştur", - "createFolderDescription": "Kod parçacıklarınızı klasörler halinde düzenleyin.", - "folderNameLabel": "Klasör Adı", - "folderNamePlaceholder": "Örneğin, Sistem Komutları, Docker Betikleri", - "folderColorLabel": "Klasör Rengi", - "folderIconLabel": "Klasör Simgesi", - "previewLabel": "Önizleme", - "folderNameFallback": "Klasör Adı", - "createFolderButton": "Klasör Oluştur", - "targetTerminals": "Hedef Terminaller", - "selectAll": "Tüm", - "selectNone": "Hiçbiri", - "noTerminalTabsOpen": "Açık terminal sekmesi yok.", - "searchPlaceholder": "Arama sonuçları...", - "newSnippet": "Yeni Parça", - "newFolder": "Yeni Klasör", - "run": "Koşmak", - "noSnippetsInFolder": "Bu klasörde kod parçacığı yok.", - "uncategorized": "Kategorilendirilmemiş", - "editSnippetTitle": "Kod Parçasını Düzenle", - "editSnippetDescription": "Bu komut parçasını güncelleyin.", - "saveSnippetButton": "Değişiklikleri Kaydet", - "createSuccess": "Kod parçası başarıyla oluşturuldu.", - "createFailed": "Kod parçacığı oluşturulamadı.", - "updateSuccess": "Kod parçası başarıyla güncellendi.", - "updateFailed": "Kod parçasını güncelleme başarısız oldu.", - "deleteFailed": "Kod parçasını silme işlemi başarısız oldu.", - "folderCreateSuccess": "Klasör başarıyla oluşturuldu.", - "folderCreateFailed": "Klasör oluşturulamadı.", - "editFolderTitle": "Klasörü Düzenle", - "editFolderDescription": "Bu klasörün adını değiştirin veya görünümünü düzenleyin.", - "saveFolderButton": "Değişiklikleri Kaydet", - "editFolder": "Klasörü düzenle", - "deleteFolder": "Klasörü sil", - "folderDeleteSuccess": "Klasör \"{{name}}\" silindi", - "folderDeleteFailed": "Klasör silme işlemi başarısız oldu.", - "folderEditSuccess": "Klasör başarıyla güncellendi.", - "folderEditFailed": "Klasör güncellemesi başarısız oldu.", - "confirmRunMessage": "\"{{name}} \" çalıştır?", - "confirmRunButton": "Koşmak", - "runSuccess": "\"{{name}}\" ifadesi {{count}} terminal(ler)inde çalıştırıldı", - "copySuccess": "\"{{name}}\" panoya kopyalandı", - "shareTitle": "Paylaşılan Parça", - "shareUser": "Kullanıcı", - "shareRole": "Rol", - "selectUser": "Bir kullanıcı seçin...", - "selectRole": "Bir rol seçin...", - "shareSuccess": "Kod parçası başarıyla paylaşıldı.", - "shareFailed": "Kod parçasını paylaşma başarısız oldu.", - "revokeSuccess": "Erişim iptal edildi", - "revokeFailed": "Erişimi iptal etme başarısız oldu.", - "currentAccess": "Mevcut Erişim", - "shareLoadError": "Paylaşılan veriler yüklenemedi.", - "loading": "Yükleniyor...", - "close": "Kapalı", - "reorderFailed": "Kod parçacığının sırasını kaydetme başarısız oldu." + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Hesap", - "sectionAppearance": "Dış görünüş", - "sectionSecurity": "Güvenlik", - "sectionApiKeys": "API Anahtarları", - "sectionC2sTunnels": "C2S Tünelleri", - "usernameLabel": "Kullanıcı adı", - "roleLabel": "Rol", - "roleAdministrator": "Yönetici", - "authMethodLabel": "Kimlik Doğrulama Yöntemi", - "authMethodLocal": "Yerel", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", "twoFaLabel": "2FA", - "twoFaOn": "Açık", - "twoFaOff": "Kapalı", - "versionLabel": "Sürüm", - "deleteAccount": "Hesabı Sil", - "deleteAccountDescription": "Hesabınızı kalıcı olarak silin.", - "deleteButton": "Silmek", - "deleteAccountPermanent": "Bu işlem kalıcıdır ve geri alınamaz.", - "deleteAccountWarning": "Tüm oturumlar, sunucular, kimlik bilgileri ve ayarlar kalıcı olarak silinecektir.", - "confirmPasswordDeletePlaceholder": "Onaylamak için şifrenizi girin.", - "languageLabel": "Dil", - "themeLabel": "Tema", - "fontSizeLabel": "Yazı Tipi Boyutu", - "accentColorLabel": "Vurgu Rengi", - "settingsTerminal": "terminal", - "commandAutocomplete": "Komut Otomatik Tamamlama", - "commandAutocompleteDesc": "Yazarken otomatik tamamlama özelliğini göster", - "historyTracking": "Geçmiş Takibi", - "historyTrackingDesc": "Terminal komutlarını izle", - "syntaxHighlighting": "Sözdizimi Vurgulama", - "syntaxHighlightingDesc": "Terminal çıktısını vurgula", - "commandPalette": "Komut Paleti", - "commandPaletteDesc": "Klavye kısayolunu etkinleştir", - "reopenTabsOnLogin": "Giriş yaptıktan sonra sekmeleri yeniden açın", - "reopenTabsOnLoginDesc": "Başka bir cihazdan giriş yapsanız bile, oturum açtığınızda veya sayfayı yenilediğinizde açık sekmelerinizi geri yükleyin.", - "confirmTabClose": "Sekmeyi Kapatmayı Onayla", - "confirmTabCloseDesc": "Terminal sekmelerini kapatmadan önce izin isteyin.", - "settingsSidebar": "Kenar Çubuğu", - "showHostTags": "Sunucu Etiketlerini Göster", - "showHostTagsDesc": "Ana bilgisayar listesinde etiketleri görüntüle", - "hostTrayOnClick": "Sunucu İşlemlerini Genişletmek için Tıklayın", - "hostTrayOnClickDesc": "Bağlantı düğmelerini her zaman göster; yönetim seçeneklerini genişletmek için fareyi üzerine getirmek yerine tıklayın.", - "pinAppRail": "Pin Uygulama Rayını", - "pinAppRailDesc": "Sol kenar çubuğundaki uygulama çubuğunu fareyle üzerine gelindiğinde genişletmek yerine her zaman açık tutun.", - "settingsSnippets": "Kısa bölümler", - "foldersCollapsed": "Klasörler Daraltıldı", - "foldersCollapsedDesc": "Klasörleri varsayılan olarak daralt", - "confirmExecution": "Yürütmeyi Onayla", - "confirmExecutionDesc": "Kod parçacıklarını çalıştırmadan önce onaylayın.", - "settingsUpdates": "Güncellemeler", - "disableUpdateChecks": "Güncelleme Kontrollerini Devre Dışı Bırak", - "disableUpdateChecksDesc": "Güncellemeleri kontrol etmeyi bırakın.", - "totpAuthenticator": "TOTP Kimlik Doğrulayıcı", - "totpEnabled": "İki faktörlü kimlik doğrulama (2FA) etkinleştirildi.", - "totpDisabled": "Giriş güvenliğine ek güvenlik ekleyin.", - "disable": "Devre dışı bırakmak", - "enable": "Olanak vermek", - "setupTotp": "TOTP kurulumu", - "qrCode": "QR Kodu", - "totpInstructions": "QR kodunu tarayın veya kimlik doğrulama uygulamanızda gizli anahtarı girin, ardından 6 haneli kodu girin.", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Doğrulamak", - "changePassword": "Şifre değiştir", - "currentPasswordLabel": "Mevcut Şifre", - "currentPasswordPlaceholder": "Mevcut Şifre", - "newPasswordLabel": "Yeni Şifre", - "newPasswordPlaceholder": "Yeni Şifre", - "confirmPasswordLabel": "Yeni Şifreyi Onayla", - "confirmPasswordPlaceholder": "Yeni şifreyi onaylayın", - "updatePassword": "Şifreyi Güncelle", - "createApiKeyTitle": "API Anahtarı Oluştur", - "createApiKeyDescription": "Programatik erişim için yeni bir API anahtarı oluşturun.", - "apiKeyNameLabel": "İsim", - "apiKeyNamePlaceholder": "Örneğin CI İşlem Hattı", - "expiryDateLabel": "Son kullanma tarihi", - "optional": "isteğe bağlı", - "cancel": "İptal etmek", - "createKey": "Anahtar Oluştur", - "apiKeyCount": "{{count}} anahtarlar", - "newKey": "Yeni Anahtar", - "noApiKeys": "Henüz API anahtarları yok.", - "apiKeyActive": "Aktif", - "apiKeyUsageHint": "Anahtarınızı içine koyun.", - "apiKeyUsageHintHeader": "başlık.", - "apiKeyPermissionsHint": "Anahtarlar, oluşturan kullanıcının izinlerini devralır.", - "roleUser": "Kullanıcı", - "authMethodDual": "Çift Kimlik Doğrulama", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP kurulumu başlatılamadı.", - "totpEnter6Digits": "6 haneli bir kod girin", - "totpEnabledSuccess": "İki faktörlü kimlik doğrulama etkinleştirildi", - "totpInvalidCode": "Geçersiz kod, lütfen tekrar deneyin.", - "totpDisableInputRequired": "TOTP kodunuzu veya şifrenizi girin.", - "totpDisabledSuccess": "İki faktörlü kimlik doğrulama devre dışı bırakıldı", - "totpDisableFailed": "İki faktörlü kimlik doğrulama devre dışı bırakılamadı.", - "totpDisableTitle": "İki faktörlü kimlik doğrulamayı devre dışı bırakın", - "totpDisablePlaceholder": "TOTP kodunu veya şifreyi girin.", - "totpDisableConfirm": "İki faktörlü kimlik doğrulamayı devre dışı bırakın", - "totpContinueVerify": "Doğrulamaya Devam Edin", - "totpVerifyTitle": "Kodu Doğrula", - "totpBackupTitle": "Yedekleme Kodları", - "totpDownloadBackup": "Yedekleme Kodlarını İndir", - "done": "Tamamlamak", - "secretCopied": "Gizli bilgi panoya kopyalandı", - "apiKeyNameRequired": "Anahtar adı gereklidir.", - "apiKeyCreated": "API anahtarı \"{{name}}\" oluşturuldu", - "apiKeyCreateFailed": "API anahtarı oluşturulamadı.", - "apiKeyUser": "Kullanıcı", - "apiKeyExpires": "Süresi doluyor", - "apiKeyRevoked": "API anahtarı \"{{name}}\" iptal edildi", - "apiKeyRevokeFailed": "API anahtarını iptal etme işlemi başarısız oldu.", - "passwordFieldsRequired": "Mevcut ve yeni şifreler gereklidir.", - "passwordMismatch": "Şifreler eşleşmiyor.", - "passwordTooShort": "Parola en az 6 karakter uzunluğunda olmalıdır.", - "passwordUpdated": "Parola başarıyla güncellendi.", - "passwordUpdateFailed": "Parola güncelleme başarısız oldu.", - "deletePasswordRequired": "Hesabınızı silmek için şifre gereklidir.", - "deleteFailed": "Hesap silme işlemi başarısız oldu.", - "deleting": "Siliniyor...", - "colorPickerTooltip": "Renk seçiciyi açın", - "themeSystem": "Sistem", - "themeLight": "Işık", - "themeDark": "Karanlık", - "themeDracula": "Drakula", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", - "themeNord": "Kuzey", - "themeSolarized": "Güneş enerjisiyle çalışan", - "themeTokyoNight": "Tokyo Gecesi", - "themeOneDark": "Bir Karanlık", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/uk_UA.json b/src/ui/locales/translated/uk_UA.json index 9de64ba8..9bc5af32 100644 --- a/src/ui/locales/translated/uk_UA.json +++ b/src/ui/locales/translated/uk_UA.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Папки", - "folder": "Тека", - "password": "Пароль", - "key": "Ключ", - "sshPrivateKey": "Приватний ключ SSH", - "upload": "Вивантажити", - "keyPassword": "Пароль ключа", - "sshKey": "SSH ключ", - "uploadPrivateKeyFile": "Завантажити файл приватного ключа", - "searchCredentials": "Пошук облікових даних...", - "addCredential": "Додати", - "caCertificate": "Сертифікат CA (-cert.pub)", - "caCertificateDescription": "Необов'язково: Завантажте або вставте файл підписаного клієнта сертифіката (наприклад, id_ed25519-cert.pub). Потрібно, коли ваш SSH сервер використовує авторизацію на основі сертифікатів.", - "uploadCertFile": "Завантажте -cert.pub файл", - "clearCert": "Очистити", - "certLoaded": "Сертифікат завантажений", - "certPublicKeyLabel": "Сертифікат центру сертифікації", - "certTypeLabel": "Тип сертифіката", - "pasteOrUploadCert": "Вставити або відвантажити -cert.pub сертифікат...", - "hasCaCert": "Має сертифікат центру сертифікації", - "noCaCert": "Немає сертифікату центру сертифікації" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Не вдалося завантажити сповіщення", - "failedToDismissAlert": "Не вдалося відхилити попередження" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Конфігурація сервера", - "description": "Налаштуйте URL сервера Termix для підключення до ваших сервісів backend", - "serverUrl": "URL-адреса сервера", - "enterServerUrl": "Будь ласка, введіть URL-адресу сервера", - "saveFailed": "Не вдалося зберегти конфігурацію", - "saveError": "Помилка під час збереження налаштувань", - "saving": "Збереження...", - "saveConfig": "Зберегти конфігурацію", - "helpText": "Введіть URL, на якому працює ваш Термікс сервер (e.g., http://localhost:30001 або https://your-server.com)", - "changeServer": "Змінити сервер", - "mustIncludeProtocol": "Адреса сервера має починатися з http:// або https://", - "allowInvalidCertificate": "Дозволити недійсний сертифікат", - "allowInvalidCertificateDesc": "Використовуйте лише для довірених серверів із власним хостингом та самопідписаними сертифікатами або сертифікатами IP-адреси.", - "useEmbedded": "Використовувати локальний сервер", - "embeddedDesc": "Запустити Termix з вбудованим локальним сервером (немає потреби віддаленого сервера)", - "embeddedConnecting": "Підключення до локального сервера...", - "embeddedNotReady": "Локальний сервер ще не готовий. Будь ласка, зачекайте трохи і спробуйте ще раз.", - "localServer": "Локальний сервер" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Помилка перевірки версії", - "checkFailed": "Не вдалося перевірити наявність оновлень", - "upToDate": "Додаток оновлено до дати", - "currentVersion": "Ваша версія {{version}}", - "updateAvailable": "Доступне оновлення", - "newVersionAvailable": "Доступна нова версія! Ви використовуєте {{current}}, але {{latest}} доступна.", - "betaVersion": "Бета версія", - "betaVersionDesc": "Ви використовуєте {{current}}, який є новішим, ніж останній стабільний реліз {{latest}}.", - "releasedOn": "Випущена на {{date}}", - "downloadUpdate": "Завантажити оновлення", - "checking": "Перевірка оновлень...", - "checkUpdates": "Перевірити наявність оновлень", - "checkingUpdates": "Перевірка оновлень...", - "updateRequired": "Необхідне оновлення" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Закрити", + "close": "Close", "minimize": "Minimize", - "online": "Онлайн", - "offline": "В автономному режимі", - "continue": "Продовжити", - "maintenance": "Ремонт", - "degraded": "Погіршення", - "error": "Помилка", - "warning": "Застереження", - "unsavedChanges": "Незбережені зміни", - "dismiss": "Відхилити", - "loading": "Завантажується...", - "optional": "За бажанням", - "connect": "Підключитися", - "copied": "Скопійовано", - "connecting": "З’єднання...", - "updateAvailable": "Доступне оновлення", - "appName": "Термі", - "openInNewTab": "Відкрити у новій вкладці", - "noReleases": "Немає релізів", - "updatesAndReleases": "Оновлення та релізи", - "newVersionAvailable": "Доступна нова версія ({{version}}).", - "failedToFetchUpdateInfo": "Неможливо отримати інформацію оновлення", - "preRelease": "Пре-реліз", - "noReleasesFound": "Не знайдено жодного релізу.", - "cancel": "Скасувати", - "username": "Ім'я користувача", - "login": "Логін", - "register": "Зареєструватися", - "password": "Пароль", - "confirmPassword": "Підтвердити пароль", - "back": "Відмінити", - "save": "Зберегти", - "saving": "Збереження...", - "delete": "Видалити", - "rename": "Перейменувати", - "edit": "Редагувати", - "add": "Додати", - "confirm": "Підтвердити", - "no": "Ні", - "or": "АБО", - "next": "Уперед", - "previous": "Попереднє", - "refresh": "Оновити", - "language": "Мова:", - "checking": "Перевірка...", - "checkingDatabase": "Перевірка підключення до бази даних...", - "checkingAuthentication": "Перевірка автентифікації...", - "backendReconnected": "З'єднання з сервером відновлено", - "connectionDegraded": "З'єднання з сервером втрачено, відновлення…", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "Видалити", - "create": "Створити", - "update": "Оновити", - "copy": "Копія", - "copyFailed": "Не вдалося скопіювати в буфер обміну", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "Відновити", - "of": "з" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Домашній екран", - "terminal": "Термінал", - "docker": "Докер", - "tunnels": "Тунельс", - "fileManager": "Файловий менеджер", - "serverStats": "Статистика на сервері", - "admin": "Адмін", - "userProfile": "Профіль користувача", - "splitScreen": "Розділений екран", - "confirmClose": "Закрити цю активну сесію?", - "close": "Закрити", - "cancel": "Скасувати", - "sshManager": "SSH менеджер", - "cannotSplitTab": "Неможливо розділити цю вкладку", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Копіювати пароль", - "copySudoPassword": "Копіювати пароль судо", - "passwordCopied": "Пароль скопійовано до буфера обміну", - "noPasswordAvailable": "Немає доступного пароля", - "failedToCopyPassword": "Не вдалося скопіювати пароль", - "refreshTab": "Оновити", - "openFileManager": "Відкрити файловий менеджер", - "dashboard": "Приладна дошка", - "networkGraph": "Графік мережі", - "quickConnect": "Швидке підключення", - "sshTools": "Інструменти SSH", - "history": "Історія", - "hosts": "Хости", - "snippets": "Сніпети", - "hostManager": "Менеджер хостів", - "credentials": "Дані доступу", - "connections": "З'єднання", - "roleAdministrator": "Адміністратор", - "roleUser": "Користувач" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Хости", - "noHosts": "Немає SSH хостів", - "retry": "Повторити спробу", - "refresh": "Оновити", - "optional": "За бажанням", - "downloadSample": "Завантажити приклад", - "failedToDeleteHost": "Не вдалося видалити {{name}}", - "importSkipExisting": "Імпорт (пропустити існує)", - "connectionDetails": "Відомості про підключення", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Віддалений робочий стіл", - "port": "Порт", - "username": "Ім'я користувача", - "folder": "Тека", - "tags": "Мітки", - "pin": "Закріплення повідомлень", - "addHost": "Додати хост", - "editHost": "Редагувати хост", - "cloneHost": "Клонувати хост", - "enableTerminal": "Увімкнути термінал", - "enableTunnel": "Увімкнути тунель", - "enableFileManager": "Увімкнути файловий менеджер", - "enableDocker": "Увімкнути Docker", - "defaultPath": "Типовий шлях", - "connection": "З'єднання", - "upload": "Вивантажити", - "authentication": "Автентифікація", - "password": "Пароль", - "key": "Ключ", - "credential": "Облікові дані", - "none": "Без ефекту", - "sshPrivateKey": "Приватний ключ SSH", - "keyType": "Тип ключа", - "uploadFile": "Завантажити файл", - "tabGeneral": "Загальні налаштування", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", - "tabVnc": "ВНС", - "tabTunnels": "Тунельс", - "tabDocker": "Докер", - "tabFiles": "Файли", - "tabStats": "Статистика", + "tabVnc": "VNC", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Спільне використання", - "tabAuthentication": "Автентифікація", - "terminal": "Термінал", - "tunnel": "Тунель", - "fileManager": "Файловий менеджер", - "serverStats": "Статистика на сервері", - "status": "Статус", - "folderRenamed": "Тека \"{{oldName}}\" перейменовано в \"{{newName}}\" успішно", - "failedToRenameFolder": "Не вдалося перейменувати теку", - "movedToFolder": "Переміщено до \"{{folder}}\"", - "editHostTooltip": "Редагувати хост", - "statusChecks": "Перевірка стану", - "metricsCollection": "Колекція метрик", - "metricsInterval": "Інтервал збору метрик", - "metricsIntervalDesc": "Як часто збирати статистику серверу (5 с - 1 год)", - "behavior": "Поведінка", - "themePreview": "Попередній перегляд теми", - "theme": "Тема", - "fontFamily": "Шрифт", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", "fontSize": "Font Size", - "letterSpacing": "Відстань між буквами", - "lineHeight": "Висота лінії", - "cursorStyle": "Стиль курсора", - "cursorBlink": "Блимання курсору", - "scrollbackBuffer": "Буфер прокручування", - "bellStyle": "Стиль Bell", - "rightClickSelectsWord": "Вибір правої кнопки миші слів", - "fastScrollModifier": "Швидкий модифікатор прокрутки", - "fastScrollSensitivity": "Чутливість швидкого прокручування", - "sshAgentForwarding": "Пересилання SSH Agent", - "backspaceMode": "Режим Backspace", - "startupSnippet": "Сніпет запуску", - "selectSnippet": "Виберіть сніпет", - "forceKeyboardInteractive": "Примусова інтерактивна клавіатура", - "overrideCredentialUsername": "Перевизначити ім'я користувача", - "overrideCredentialUsernameDesc": "Використовуйте ім'я користувача, зазначене вище замість імені користувача облікові дані", - "jumpHostChain": "Перейти до комп'ютера", - "portKnocking": "Порт-Нокінг", - "addKnock": "Додати порт", - "addProxyNode": "Добавити вузол", - "proxyNode": "Вузол проксі", - "proxyType": "Тип проксі", - "quickActions": "Швидкі дії", - "sudoPasswordAutoFill": "Автозаповнення судо-пароля", - "sudoPassword": "Пароль судо", - "keepaliveInterval": "Інтервал оновлення Keepalive (мс)", - "moshCommand": "Команда MOSH", - "environmentVariables": "Змінні середовища", - "addVariable": "Додати змінну", - "docker": "Докер", - "copyTerminalUrl": "Скопіювати URL терміналу", - "copyFileManagerUrl": "Копіювати URL менеджер файлів", - "copyRemoteDesktopUrl": "Копіювати URL-адреси віддаленого робочого столу", - "failedToConnect": "Не вдалося підключитися до консолі", - "connect": "Підключитися", - "disconnect": "Від'єднатись", - "start": "Старт", - "enableStatusCheck": "Увімкнути перевірку стану", - "enableMetrics": "Увімкнути метрики", - "bulkUpdateFailed": "Не вдалося оновити основну копію", - "selectAll": "Виділити все", - "deselectAll": "Відмінити все", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "Захищена оболонка", - "virtualNetwork": "Віртуальна мережа", - "unencryptedShell": "Незашифрована оболонка", - "addressIp": "Адреса / IP", - "friendlyName": "Зручна назва", - "folderAndAdvanced": "Папка та розширені налаштування", - "privateNotes": "Приватні нотатки", - "privateNotesPlaceholder": "Докладніше про цей сервер...", - "pinToTop": "Закріпити на початку", - "pinToTopDesc": "Завжди показувати цей хост у верхній частині списку", - "portKnockingSequence": "Порт-Нокінг Послідовність", - "addKnockBtn": "Добавити копію", - "noPortKnocking": "Порт не налаштовано.", - "knockPort": "Стук-Порт", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "Затримка після (мс)", - "useSocks5Proxy": "Використовувати проксі SOCKS5", - "useSocks5ProxyDesc": "Переставити з'єднання через проксі-сервер", - "proxyHost": "Вузол проксі", - "proxyPort": "Порт проксі-сервера", - "proxyUsername": "Ім'я користувача проксі-сервера", - "proxyPassword": "Пароль проксі-сервера", - "proxySingleMode": "Одиночний проксі", - "proxyChainMode": "Ланцюг Проксі", - "you": "Ви", - "jumpHostChainLabel": "Перейти до комп'ютера", - "addJumpBtn": "Додати стрибок", - "noJumpHosts": "Не налаштовано стрибаючих хостів.", - "selectAServer": "Виберіть сервер...", - "sshPort": "SSH порт", - "authMethod": "Метод автентифікації", - "storedCredential": "Збережені облікові дані", - "selectACredential": "Виберіть облікові дані...", - "keyTypeLabel": "Тип ключа", - "keyTypeAuto": "Автоматичне визначення", - "keyPasteTab": "Вставити", - "keyUploadTab": "Вивантажити", - "keyFileLoaded": "Ключовий файл завантажено", - "keyUploadClick": "Натисніть для завантаження .pem / .key / .ppk", - "clearKey": "Очистити ключ", - "keySaved": "SSH ключ збережено", - "keyReplaceNotice": "вставте новий ключ нижче, щоб замінити його", - "keyPassphraseSaved": "Пароль збережено, введіть для зміни", - "replaceKey": "Замінити ключ", - "forceKeyboardInteractiveLabel": "Примусова інтерактивна клавіатура", - "forceKeyboardInteractiveShortDesc": "Примусово використовувати ручний пароль навіть якщо ключі присутні", - "terminalAppearance": "Зовнішній вигляд терміналу", - "colorTheme": "Колір теми", - "fontFamilyLabel": "Шрифт", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "Стиль курсора", - "letterSpacingPx": "Відстань між символами (px)", - "lineHeightLabel": "Висота лінії", - "bellStyleLabel": "Стиль Bell", - "backspaceModeLabel": "Режим Backspace", - "cursorBlinking": "Курсор Блимання", - "cursorBlinkingDesc": "Увімкнути анімацію спливання для курсору", - "rightClickSelectsWordLabel": "Вибір правої кнопки миші вибирає слово", - "rightClickSelectsWordShortDesc": "Виберіть слово під курсором в правому натисканні", - "behaviorAndAdvanced": "Поведінка та розширені функції", - "scrollbackBufferLabel": "Буфер прокручування", - "scrollbackMaxLines": "Максимальна кількість рядків, що зберігаються в історії", - "sshAgentForwardingLabel": "Пересилання SSH Agent", - "sshAgentForwardingShortDesc": "Передавати свої локальні ключі SSH цьому хосту", - "enableAutoMosh": "Увімкнути авто-Mosh", - "enableAutoMoshDesc": "Віддавати перевагу Мосху над SSH якщо доступно", - "enableAutoTmux": "Увімкнути авто-твікс", - "enableAutoTmuxDesc": "Автоматично запускати або прикріплювати до сеансу", - "sudoPasswordAutoFillLabel": "Автозаповнення судо-пароля", - "sudoPasswordAutoFillShortDesc": "Автоматично надавати судо-пароль по запиті", - "sudoPasswordLabel": "Пароль судо", - "environmentVariablesLabel": "Змінні середовища", - "addVariableBtn": "Додати змінну", - "noEnvVars": "Змінні середовища не налаштовані.", - "fastScrollModifierLabel": "Швидкий модифікатор прокрутки", - "fastScrollSensitivityLabel": "Чутливість швидкого прокручування", - "moshCommandLabel": "Командування Мосха", - "startupSnippetLabel": "Сніпет запуску", - "keepaliveIntervalLabel": "Інтервал підтримки активності (секунди)", - "maxKeepaliveMisses": "Максимальна кількість пропусків Keepalive", - "tunnelSettings": "Налаштування тунелю", - "enableTunneling": "Увімкнути тунелювання", - "enableTunnelingDesc": "Увімкнути функцію SSH тунелю для цього хосту", - "serverTunnelsSection": "Налаштування сервера", - "addTunnelBtn": "Додати тунель", - "noTunnelsConfigured": "Тунелі не налаштовано.", - "tunnelLabel": "Тунель {{number}}", - "tunnelType": "Тип тунелю", - "tunnelModeLocalDesc": "Переслати локальний порт в порт на віддаленому сервері (або хост доступний з нього).", - "tunnelModeRemoteDesc": "Переслати порт на віддалений сервер назад до локального порту на вашому комп’ютері.", - "tunnelModeDynamicDesc": "Створіть проксі SOCKS5 на локальному порту для динамічного пересилання портів.", - "sameHost": "Цей хост (прямий тунель)", - "endpointHost": "Підключення хоста", - "endpointPort": "Порт кінцевої точки", - "bindHost": "Прив'язати хост", - "sourcePort": "Вихідний порт", - "maxRetries": "Кількість спроб", - "retryIntervalS": "Інтервал повтору", - "autoStartLabel": "Автозапуск", - "autoStartDesc": "Автоматично підключати цей тунель, коли хост завантажений", - "tunnelConnecting": "Тунель підключення...", - "tunnelDisconnected": "Тунель від’єднано", - "failedToConnectTunnel": "Не вдалося підключитися", - "failedToDisconnectTunnel": "Не вдалося від’єднати", - "dockerIntegration": "Docker інтеграція", - "enableDockerMonitor": "Увімкнути Docker", - "enableDockerMonitorDesc": "Монітор і керування контейнерами на цьому хості через Docker", - "enableFileManagerMonitor": "Увімкнути файловий менеджер", - "enableFileManagerMonitorDesc": "Перегляд та керування файлами на цьому хості через SFTP", - "defaultPathLabel": "Типовий шлях", - "fileManagerPathHint": "Каталог який відкривається, коли файловий менеджер запускається для цього хоста.", - "statusChecksLabel": "Перевірка стану", - "enableStatusChecks": "Увімкнути перевірки статусу", - "enableStatusChecksDesc": "Періодично наведення цього хоста для перевірки доступності", - "useGlobalInterval": "Використовувати глобальний інтервал", - "useGlobalIntervalDesc": "Перевизначити інтервал перевірки статусу сервера", - "checkIntervalS": "Інтервал перевірки (ів)", - "checkIntervalDesc": "Секунди між кожним підключенням", - "metricsCollectionLabel": "Колекція метрик", - "enableMetricsLabel": "Увімкнути метрики", - "enableMetricsDesc": "Збирайте використання ЦП, RAM, диска та мережевого використання на цьому хості", - "useGlobalMetrics": "Використовувати глобальний інтервал", - "useGlobalMetricsDesc": "Перевизначити інтервал загальної метрики сервера", - "metricsIntervalS": "Інтервал метрики(ів)", - "metricsIntervalDesc2": "Секунд між метричними знімками", - "visibleWidgets": "Видимі віджети", - "cpuUsageLabel": "Використання ЦП", - "cpuUsageDesc": "Відсоток ЦП, завантажує середні розміри, графік проживання", - "memoryLabel": "Використання пам'яті", - "memoryDesc": "Використання оперативної пам'яті, болота, кешовані", - "storageLabel": "Використання диску", - "storageDesc": "Диск використання за точку монтування", - "networkLabel": "Мережеві інтерфейси", - "networkDesc": "Інтерфейс списку і пропускна здатність", - "uptimeLabel": "Час роботи", - "uptimeDesc": "Системний час і час завантаження", - "systemInfoLabel": "Системна інформація", - "systemInfoDesc": "OS, ядро, хост, архітектура", - "recentLoginsLabel": "Останні входи", - "recentLoginsDesc": "Успішні та невдалі події входу", - "topProcessesLabel": "ТОП процесів", - "topProcessesDesc": "Команду PID, CPU%, MEM%, команду", - "listeningPortsLabel": "Прослуховування портів", - "listeningPortsDesc": "Відкрити порти з процесом і станом", - "firewallLabel": "Брандмауер", - "firewallDesc": "Firewall, AppArmor, статус SELinux", - "quickActionsLabel": "Швидкі дії", - "quickActionsToolbar": "Швидкі дії з'являються як кнопки в панелі інструментів інструментів сервера для виконання команд одним натисканням.", - "noQuickActions": "Швидких дій поки немає.", - "buttonLabel": "Мітка кнопки", - "selectSnippetPlaceholder": "Виберіть сніпет...", - "addActionBtn": "Додати дію", - "hostSharedSuccessfully": "Хост успішно відкрито", - "failedToShareHost": "Не вдалося поділитися хостом", - "accessRevoked": "Доступ відкликаний", - "failedToRevokeAccess": "Не вдалося відкликати доступ", - "cancelBtn": "Скасувати", - "savingBtn": "Збереження...", - "addHostBtn": "Додати хост", - "hostUpdated": "Хост оновлено", - "hostCreated": "Хост створено", - "failedToSave": "Не вдалося зберегти хост", - "credentialUpdated": "Облікові дані оновлено", - "credentialCreated": "Обліковку створено", - "failedToSaveCredential": "Не вдалося зберегти облікові дані", - "backToHosts": "Назад до хостів", - "backToCredentials": "Назад до облікових даних", - "pinned": "Закріплено", - "noHostsFound": "Не знайдено хостів", - "tryDifferentTerm": "Спробуйте інший вираз", - "addFirstHost": "Додайте свій перший хост щоб почати", - "noCredentialsFound": "Облікові дані не знайдені", - "addCredentialBtn": "Додати", - "updateCredentialBtn": "Оновити облікові дані", - "features": "Особливості", - "noFolder": "(Немає теки)", - "deleteSelected": "Видалити", - "exitSelection": "Вийти з вибору", - "importSkip": "Імпорт (пропустити існує)", - "importOverwrite": "Імпорт (перезапис)", - "collapseBtn": "Згорнути", - "importExportBtn": "Імпорт / експорт", - "hostStatusesRefreshed": "Статуси хостів оновлено", - "failedToRefreshHosts": "Не вдалося оновити хости", - "movedHostTo": "Переміщено {{host}} до \"{{folder}}\"", - "failedToMoveHost": "Не вдалося перемістити хост", - "folderRenamedTo": "Папку перейменовано на \"{{name}}\"", - "deletedFolder": "Видалена папка \"{{name}}\"", - "failedToDeleteFolder": "Не вдалося вилучити теку", - "deleteAllInFolder": "Видалити всі хости в \"{{name}}? Це неможливо скасувати.", - "deletedHost": "Видалено {{name}}", - "copiedToClipboard": "Скопійовано до буферу обміну", - "terminalUrlCopied": "Кінцева URL-адреса скопійована", - "fileManagerUrlCopied": "URL файлового менеджера скопійовано", - "tunnelUrlCopied": "URL тунелю скопійовано", - "dockerUrlCopied": "Docker URL скопійовано", - "serverStatsUrlCopied": "Статистику сервера скопійовано", - "rdpUrlCopied": "RDP URL скопійовано", - "vncUrlCopied": "VNC URL скопійовано", - "telnetUrlCopied": "URL Telnet скопійовано", - "remoteDesktopUrlCopied": "URL віддаленого робочого столу скопійовано", - "expandActions": "Розгорнути дії", - "collapseActions": "Згорнути дії", - "wakeOnLanAction": "Пробудження по локальній мережі", - "wakeOnLanSuccess": "Чарівний пакет надіслано на {{name}}", - "wakeOnLanError": "Не вдалося надіслати магічний пакет", - "cloneHostAction": "Клонувати хост", - "copyAddress": "Копіювати адресу", - "copyLink": "Копіювати посилання", - "copyTerminalUrlAction": "Скопіювати URL терміналу", - "copyFileManagerUrlAction": "Копіювати URL менеджер файлів", - "copyTunnelUrlAction": "Копіювати URL тунелю", - "copyDockerUrlAction": "Копіювати Docker URL", - "copyServerStatsUrlAction": "Скопіювати URL-адресу статистики сервера", - "copyRdpUrlAction": "Копіювати URL-адресу RDP", - "copyVncUrlAction": "Копіювати URL-АДРЕСА", - "copyTelnetUrlAction": "Копіювати URL Telnet", - "copyRemoteDesktopUrlAction": "Копіювати URL-адреси віддаленого робочого столу", - "deleteCredentialConfirm": "Видалити облікові дані \"{{name}}\"?", - "deletedCredential": "Видалено {{name}}", - "deploySSHKeyTitle": "Розгортати SSH ключ", - "deployingBtn": "Розгортання...", - "deployBtn": "Розгортання", - "failedToDeployKey": "Не вдалося розгорнути ключ", - "deleteHostsConfirm": "Видалити хост {{count}}{{plural}}? Цю дію не можна скасувати.", - "movedToRoot": "Переміщено в корінь", - "failedToMoveHosts": "Не вдалося перемістити хости", - "enableTerminalFeature": "Увімкнути термінал", - "disableTerminalFeature": "Вимкнути термінал", - "enableFilesFeature": "Увімкнути файли", - "disableFilesFeature": "Вимкнути файли", - "enableTunnelsFeature": "Увімкнути тунелі", - "disableTunnelsFeature": "Вимкнути тунелі", - "enableDockerFeature": "Увімкнути Docker", - "disableDockerFeature": "Вимкнути Docker", - "addTagsPlaceholder": "Додати теги...", - "authDetails": "Деталі автентифікації", - "credType": "Тип", - "generateKeyPairDesc": "Згенерувати нову ключову пару, як приватну, так і публічні ключі будуть заповнені автоматично.", - "generatingKey": "Створення...", - "generateLabel": "Згенерувати {{label}}", - "uploadFileBtn": "Завантажити файл", - "keyPassphraseOptional": "Ключова парольна фраза (необов'язково)", - "sshPublicKeyOptional": "Відкритий SSH ключ (необов'язково)", - "publicKeyGenerated": "Згенеровано відкритий ключ", - "failedToGeneratePublicKey": "Не вдалося отримати відкритий ключ", - "publicKeyCopied": "Скопійовано відкритий ключ", - "keyPairGenerated": "Створено пару ключів {{label}}", - "failedToGenerateKeyPair": "Не вдалося згенерувати пару ключів", - "searchHostsPlaceholder": "Пошук хостів, адрес, теґи…", - "searchCredentialsPlaceholder": "Пошук облікових даних…", - "refreshBtn": "Оновити", - "addTag": "Додати теги...", - "deleteConfirmBtn": "Видалити", - "tunnelRequirementsText": "SSH сервер повинен мати так, дозволені TcpForwarding так, а PermittLogin \"yes\" встановлених в /etc/ssh/sshd_config.", - "deleteHostConfirm": "Видалити \"{{name}}\"?", - "enableAtLeastOneProtocol": "Увімкніть принаймні один протокол для налаштування автентифікації та параметрів підключення.", - "keyPassphrase": "Ключова фраза", - "connectBtn": "Підключитися", - "disconnectBtn": "Від'єднатись", - "basicInformation": "Основна інформція", - "authDetailsSection": "Деталі автентифікації", - "credTypeLabel": "Тип", - "hostsTab": "Хости", - "credentialsTab": "Дані доступу", - "selectMultiple": "Вибрати кілька", - "selectHosts": "Виберіть хости", - "connectionLabel": "З'єднання", - "authenticationLabel": "Автентифікація", - "generateKeyPairTitle": "Згенерувати ключову пару", - "generateKeyPairDescription": "Згенерувати нову ключову пару, як приватну, так і публічні ключі будуть заповнені автоматично.", - "generateFromPrivateKey": "Генерувати з закритого ключа", - "refreshBtn2": "Оновити", - "exitSelectionTitle": "Вийти з вибору", - "exportAll": "Експортувати всі", - "addHostBtn2": "Додати хост", - "addCredentialBtn2": "Додати", - "checkingHostStatuses": "Перевірка статусів хоста...", - "pinnedSection": "Закріплено", - "hostsExported": "Хости успішно експортовано", - "exportFailed": "Не вдалося експортувати хости", - "sampleDownloaded": "Приклад файлу завантажено", - "failedToDeleteCredential2": "Не вдалося видалити облікові дані", - "noFolderOption": "(Немає теки)", - "nSelected": "Вибрано {{count}}", - "featuresMenu": "Особливості", - "moveMenu": "Пересунути", - "cancelSelection": "Скасувати", - "deployDialogDesc": "Розгортати {{name}} на авторизовані ключі хоста.", - "targetHostLabel": "Цільовий хост", - "selectHostOption": "Виберіть хост...", - "keyDeployedSuccess": "Ключ успішно розгорнуто", - "failedToDeployKey2": "Не вдалося розгорнути ключ", - "deletedCount": "Видалено {{count}} хостів", - "failedToDeleteCount": "Не вдалося видалити {{count}} хостів", - "duplicatedHost": "Дубльовані \"{{name}}\"", - "failedToDuplicateHost": "Не вдалося дублювати хост", - "updatedCount": "Оновлено {{count}} хостів", - "friendlyNameLabel": "Зручна назва", - "descriptionLabel": "Опис", - "loadingHost": "Завантаження хоста...", - "loadingHosts": "Завантаження хості...", - "loadingCredentials": "Завантаження облікових даних...", - "noHostsYet": "Немає хостів", - "noHostsMatchSearch": "Немає хостів, які відповідають вашому пошуку", - "hostNotFound": "Хост не знайдено", - "searchHosts": "Шукати...", - "sortHosts": "Сортувати хости", - "sortDefault": "Порядок за замовчуванням", - "sortNameAsc": "Ім'я (А → Я)", - "sortNameDesc": "Ім'я (Я → А)", - "sortIpAsc": "IP-адреса (зростання)", - "sortIpDesc": "IP-адреса (опис)", - "sortOnlineFirst": "Спочатку онлайн", - "sortOfflineFirst": "Спочатку офлайн", - "sortPinnedFirst": "Закріплено першим", - "filterHosts": "Фільтрувати хости", - "filterClearAll": "Очистити фільтри", - "filterStatusGroup": "Статус", - "filterOnline": "Онлайн", - "filterOffline": "Офлайн", - "filterPinned": "Закріплено", - "filterAuthGroup": "Тип автентифікації", - "filterAuthPassword": "Пароль", - "filterAuthKey": "SSH-ключ", - "filterAuthCredential": "Посвідчення", - "filterAuthNone": "Жоден", - "filterAuthOpkssh": "ОПКШ", - "filterProtocolGroup": "Протокол", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", + "filterAuthOpkssh": "OPKSSH", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "ПРП", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", - "filterProtocolTelnet": "Телнет", - "filterFeaturesGroup": "Особливості", - "filterFeatureTerminal": "Термінал", - "filterFeatureFileManager": "Файловий менеджер", - "filterFeatureTunnel": "Тунель", - "filterFeatureDocker": "Докер", - "filterTagsGroup": "Теги", - "shareHost": "Поділитися хостом", - "shareHostTitle": "Поділитися: {{name}}", + "filterProtocolTelnet": "Telnet", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", + "filterFeatureDocker": "Docker", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Цей хост повинен використовувати облікові дані. Спочатку відредагуйте хост і призначте облікові дані." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "З'єднання", - "authentication": "Автентифікація", - "connectionSettings": "Параметри підключення", - "displaySettings": "Налаштування відображення", - "audioSettings": "Налаштування звуку", - "rdpPerformance": "Продуктивність RDP", - "deviceRedirection": "Перенаправлення пристрою", - "session": "Сесія", - "gateway": "Шлюз", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "Буфер обміну", - "sessionRecording": "Запис сесії", - "wakeOnLan": "Вак-он-ЛАН", - "vncSettings": "Параметри VNC", - "terminalSettings": "Параметри терміналу", - "rdpPort": "RDP-порт", - "username": "Ім'я користувача", - "password": "Пароль", - "domain": "Домен", - "securityMode": "Режим безпеки", - "colorDepth": "Глибина кольору", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "Висота", - "dpi": "Точок на дюйм", - "resizeMethod": "Змінити розмір", - "clientName": "Ім'я клієнта", - "initialProgram": "Початкова програма", - "serverLayout": "Макет серверу", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "Порт шлюзу", - "gatewayUsername": "Ім'я користувача шлюзу", - "gatewayPassword": "Пароль шлюзу", - "gatewayDomain": "Домен шлюзу", - "remoteAppProgram": "Програма RemoteApp", - "workingDirectory": "Робочий каталог", - "arguments": "Аргументи", - "normalizeLineEndings": "Початки ліній Нормалізації", - "recordingPath": "Шлях до запису", - "recordingName": "Назва запису", - "macAddress": "MAC-адреса", - "broadcastAddress": "Адреса трансляції", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "Очікування часу(ів)", - "driveName": "Ім'я диску", - "drivePath": "Шлях", - "ignoreCertificate": "Ігнорувати сертифікат", - "ignoreCertificateDesc": "Дозволити підключення до хостів з самостійно підписаними сертифікатами", - "forceLossless": "Без втрат сили", - "forceLosslessDesc": "Примусово використовувати кодування без втрат зображень (вища якість, більша пропускна здатність)", - "disableAudio": "Вимкнути аудіо", - "disableAudioDesc": "Вимкнути звук для віддаленого сеансу", - "enableAudioInput": "Увімкнути введення звуку (мікрофон)", - "enableAudioInputDesc": "Переслати локальний мікрофон на віддалену сесію", - "wallpaper": "Шпалери", - "wallpaperDesc": "Показувати шпалери робочого столу (вимкнення підвищує продуктивність)", - "theming": "Теми", - "themingDesc": "Увімкнути візуальні теми і стилі", - "fontSmoothing": "Згладжування шрифту", - "fontSmoothingDesc": "Увімкнути рендеринг шрифту ClearType", - "fullWindowDrag": "Перетягування всього вікна", - "fullWindowDragDesc": "Показати вміст вікна під час перетягування", - "desktopComposition": "Склад робочого столу", - "desktopCompositionDesc": "Увімкнути ефекти скла в аеро", - "menuAnimations": "Анімація меню", - "menuAnimationsDesc": "Увімкнути затухання меню та анімацію слайду", - "disableBitmapCaching": "Вимкнути кешування растрових карт", - "disableBitmapCachingDesc": "Вимкнути растровий кеш (може допомогти із glitches)", - "disableOffscreenCaching": "Вимкнути кешування без екрану", - "disableOffscreenCachingDesc": "Вимкнути кеш в автономному режимі", - "disableGlyphCaching": "Вимкнути кешування гліфів", - "disableGlyphCachingDesc": "Вимкнути кеш гліфів", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "Використовувати графічний конвеєр RemoteFX", - "enablePrinting": "Увімкнути друк", - "enablePrintingDesc": "Перенаправити локальні принтери на віддалену сесію", - "enableDriveRedirection": "Включити перенаправлення диску", - "enableDriveRedirectionDesc": "Зв’язати локальну папку як диск у віддаленій сесії", - "createDrivePath": "Створити шлях до диску", - "createDrivePathDesc": "Автоматично створювати теку, якщо вона не існує", - "disableDownload": "Вимкнути завантаження", - "disableDownloadDesc": "Не дозволяти завантажувати файли з віддаленої сесії", - "disableUpload": "Заборонити вивантаження", - "disableUploadDesc": "Заборонити завантаження файлів на віддалену сесію", - "enableTouch": "Увімкнути дотик", - "enableTouchDesc": "Увімкнути переадресацію сенсорного вводу", - "consoleSession": "Консольна сесія", - "consoleSessionDesc": "Підключення до консолі (сеанс 0) замість нової сесії", - "sendWolPacket": "Надіслати WOL Packet", - "sendWolPacketDesc": "Надіслати магічний пакет для розбудови цього хосту перед підключенням", - "disableCopy": "Вимкнути копіювання", - "disableCopyDesc": "Заборонити копіювання тексту з віддаленої сесії", - "disablePaste": "Вимкнути вставку", - "disablePasteDesc": "Запобігає вставці тексту у віддалену сесію", - "createPathIfMissing": "Створити шлях, якщо відсутній", - "createPathIfMissingDesc": "Автоматично створювати каталог для запису", - "excludeOutput": "Виключити вихід", - "excludeOutputDesc": "Не записувати вилучень екрану (лише метаданні)", - "excludeMouse": "Виключити миші", - "excludeMouseDesc": "Не записуйте рухи миші", - "includeKeystrokes": "Включити клавіши", - "includeKeystrokesDesc": "Запис сирих натискань клавіш на додаток до виводу екрану", - "vncPort": "VNC-порт", - "vncPassword": "VNC пароль", - "vncUsernameOptional": "Ім'я користувача (не обов'язково)", - "vncLeaveBlank": "Залиште пустим, якщо не обов'язково", - "cursorMode": "Режим курсору", - "swapRedBlue": "Поміняти червоний/синій", - "swapRedBlueDesc": "Поміняти червоні та сині кольорові канали (виправити деякі проблеми з кольором)", - "readOnly": "Тільки для перегляду", - "readOnlyDesc": "Перегляд віддаленого екрану без відправлення жодного вводу", - "telnetPort": "Тельне Порт", - "terminalType": "Тип терміналу", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "Колірна схема", - "backspaceKey": "Ключ Backspace", - "saveHostFirst": "Спочатку збережіть хост.", - "sharingOptionsAfterSave": "Параметри спільного використання доступні після збереження хоста.", - "sharingLoadError": "Не вдалося завантажити передачу даних. Перевірте ваше з'єднання і спробуйте ще раз.", - "shareHostSection": "Поділитися хостом", - "shareWithUser": "Поділитися з користувачем", - "shareWithRole": "Поділитися із роллю", - "selectUser": "Вибрати користувача", - "selectRole": "Вибрати роль", - "selectUserOption": "Виберіть користувача...", - "selectRoleOption": "Виберіть роль...", - "permissionLevel": "Рівень дозволів", - "expiresInHours": "Закінчується через (години)", - "noExpiryPlaceholder": "Не заповнюйте, якщо немає терміну дії", - "shareBtn": "Поділитись", - "currentAccess": "Поточний доступ", - "typeHeader": "Тип", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "Дозвіл", - "grantedByHeader": "Надано", - "expiresHeader": "Закінчується", - "noAccessEntries": "Записи доступу поки немає.", - "expiredLabel": "Термін дії закінчився", - "neverLabel": "Ніколи", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "Скасувати", - "savingBtn": "Збереження...", - "updateHostBtn": "Оновити хост", - "addHostBtn": "Додати хост" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Пошук вузлів, команд або налаштувань...", - "quickActions": "Швидкі дії", - "hostManager": "Менеджер хостів", - "hostManagerDesc": "Налаштування вручну, додавання чи редагування хостів", - "addNewHost": "Додати новий хост", - "addNewHostDesc": "Реєстрація нового хоста", - "adminSettings": "Адміністративні налаштування", - "adminSettingsDesc": "Налаштування системних налаштувань і користувачів", - "userProfile": "Профіль користувача", - "userProfileDesc": "Керуйте вашим акаунтом і налаштуваннями", - "addCredential": "Додати", - "addCredentialDesc": "Зберігати SSH ключі або паролі", - "recentActivity": "Остання активність", - "serversAndHosts": "Сервери та хости", - "noHostsFound": "Не знайдено хостів відповідного \"{{search}}\"", - "links": "Посилання", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "Вибрати", - "toggleWith": "Розпочати з" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "Жодної вкладки не призначено", - "focusedPane": "Активна панель" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Немає з'єднань", - "noConnectionsDesc": "Відкрийте термінал, файловий менеджер або віддалений робочий стіл, щоб побачити підключення тут", - "connectedFor": "Підключено для {{duration}}", - "connected": "Підключено", - "disconnected": "Відключено", - "closeTab": "Закрити вкладку", - "closeConnection": "Закрити з'єднання", - "forgetTab": "Забути", - "removeBackground": "Видалити", - "reconnect": "Знову підключитися", - "reopenTab": "Знову відкрити", - "sectionOpen": "ВІДЧИНЕНО", - "sectionBackground": "Передісторія", - "backgroundDesc": "Сеанси тривають 30 хвилин після відключення та можуть бути підключені знову.", - "persisted": "Зберігається у фоновому режимі", - "expiresIn": "Термін дії закінчується через {{duration}}", - "search": "Пошук з'єднань...", - "noSearchResults": "Немає з'єднань, що відповідають вашому пошуку" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Підключення до сеансу {{type}}...", - "connectionError": "Помилка підключення", - "connectionFailed": "Не вдалося встановити з'єднання", - "failedToConnect": "Не вдалося отримати токен для підключення", - "hostNotFound": "Хост не знайдено", - "noHostSelected": "Не вибрано жодного хосту", - "reconnect": "Перепід'єднатись", - "retry": "Повторити спробу", - "guacdUnavailable": "Віддалена служба стільниці (guacd) недоступна. Переконайтеся, що гадоїд працює і налаштований належним чином в налаштуваннях адміністратора.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L (Заблокувати екран)", - "winKey": "Вікно", - "ctrl": "Сtrl", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "Зсув", - "win": "Перемога", - "stickyActive": "{{key}} (затримується - натисніть, щоб оновити)", - "stickyInactive": "{{key}} (натисніть, щоб зачинати)", - "esc": "Екранування", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Домашній екран", - "end": "Кінець", - "pageUp": "Сторінка вгору", - "pageDown": "Сторінка вниз", - "arrowUp": "Стрілка вгору", - "arrowDown": "Стрілка вниз", - "arrowLeft": "Стрілка вліво", - "arrowRight": "Стрілка праворуч", - "fnToggle": "Функціональні кнопки", - "reconnect": "Перепідключити сесію", - "collapse": "Згорнути панель інструментів", - "expand": "Розгорнути панель інструментів", - "dragHandle": "Перетягніть для перестановки" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "З'єднатися з хостом", - "clear": "Очистити", - "paste": "Вставити", - "reconnect": "Перепід'єднатись", - "connectionLost": "З'єднання втрачено", - "connected": "Підключено", - "clipboardWriteFailed": "Не вдалося скопіювати до буфера обміну. Переконайтеся, що сторінка подається через HTTPS або localhost.", - "clipboardReadFailed": "Не вдалося прочитати з буфера обміну. Переконайтеся, що дозволи доступу до буфера обміну надані.", - "clipboardHttpWarning": "Вставити HTTPS. Використовуйте Ctrl+Shift+V або службового терміналу по HTTPS.", - "unknownError": "Сталася невідома помилка", - "websocketError": "Помилка з'єднання WebSocket", - "connecting": "З’єднання...", - "noHostSelected": "Не вибрано жодного хосту", - "reconnecting": "Повторне підключення... ({{attempt}}/{{max}})", - "reconnected": "Перепід'єднано успішно", - "tmuxSessionCreated": "Створено сеанс tmux: {{name}}", - "tmuxSessionAttached": "tmux сесія: {{name}}", - "tmuxUnavailable": "tmux не встановлено на віддаленому хості, повертаючись до стандартної оболонки", - "tmuxSessionPickerTitle": "тмукс Сешнс", - "tmuxSessionPickerDesc": "Існуючі сесії виявлено на цьому хості. Виберіть одну для повторного приєднання або створення нового сесії.", - "tmuxWindows": "Вікна", - "tmuxWindowCount": "вікно {{count}}", - "tmuxAttached": "Прикріплені клієнти", - "tmuxAttachedCount": "{{count}} додається", - "tmuxLastActivity": "Остання активність", - "tmuxTimeJustNow": "прямо зараз", - "tmuxTimeMinutes": "{{count}}хв тому", - "tmuxTimeHours": "{{count}}год тому", - "tmuxTimeDays": "{{count}}d тому", - "tmuxCreateNew": "Почати нову сесію", - "tmuxCopyHint": "Відрегулюйте вибір і натисніть Enter для копіювання до буфера обміну", - "tmuxDetach": "Від'єднати від сесії tmux", - "tmuxDetached": "Від’єднано від звичайної сесії", - "maxReconnectAttemptsReached": "Досягнуто максимальної кількості спроб повторного з'єднання", - "closeTab": "Закрити", - "connectionTimeout": "Час очікування з'єднання", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", "terminalTitle": "Terminal - {{host}}", - "terminalWithPath": "Термінал - {{host}}:{{path}}", - "runTitle": "Запуск {{command}} — {{host}}", - "totpRequired": "Необхідна двофакторна аутентифікація", - "totpCodeLabel": "Код підтвердження", - "totpVerify": "Перевірити", - "warpgateAuthRequired": "Потрібен warpgate авторизація", - "warpgateSecurityKey": "Ключ безпеки", - "warpgateAuthUrl": "URL автентифікації", - "warpgateOpenBrowser": "Відкрити у браузері", - "warpgateContinue": "Я вже завершив автентифікацію", - "opksshAuthRequired": "Необхідна автентифікація OPKSSH", - "opksshAuthDescription": "Завершіть автентифікацію в вашому браузері, щоб продовжити. Ця сесія залишиться дійсною протягом 24 годин.", - "opksshOpenBrowser": "Відкрити браузер для аутентифікації", - "opksshWaitingForAuth": "Очікування аутентифікації в браузері...", - "opksshAuthenticating": "Обробка автентифікації...", - "opksshTimeout": "Час аутентифікації минув. Будь ласка, спробуйте ще раз.", - "opksshAuthFailed": "Помилка авторизації. Будь ласка, перевірте свої облікові дані та спробуйте ще раз.", - "opksshSignInWith": "Увійти за допомогою {{provider}}", - "sudoPasswordPopupTitle": "Введіть пароль?", - "websocketAbnormalClose": "Помилка підключення скінчилося. Можливо це через незворотню проблему конфігурації проксі-сервера чи SSL. Будь ласка, перевірте журнали серверів.", - "connectionLogTitle": "Журнал з’єднання", - "connectionLogCopy": "Скопіювати логи в буфер обміну", - "connectionLogEmpty": "Журнал підключень поки немає", - "connectionLogWaiting": "Очікування логів підключення...", - "connectionLogCopied": "Журнал підключень скопійовано до буфера обміну", - "connectionLogCopyFailed": "Не вдалося скопіювати логи до буферу обміну", - "connectionRejected": "З'єднання відхилено сервером. Будь ласка, перевірте налаштування аутентифікації та мережі.", - "hostKeyRejected": "SSH-ключ хоста відхилено. Підключення скасовано.", - "sessionTakenOver": "Сесію було відкрито на іншій вкладці. Перепід'єднання..." + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Не вибрано жодного хосту", - "initializingEditor": "Ініціалізація редактора...", - "file": "Файл", - "folder": "Тека", - "uploadFile": "Завантажити файл", - "downloadFile": "Звантажити", - "extractArchive": "Розпакувати архів", - "extractingArchive": "Видобування {{name}}...", - "archiveExtractedSuccessfully": "{{name}} успішно видобуто", - "extractFailed": "Помилка розпакування", - "compressFile": "Стискати файл", - "compressFiles": "Стискати файли", - "compressFilesDesc": "Стиснути елементи {{count}} в архів", - "archiveName": "Назва архіву", - "enterArchiveName": "Введіть назву архіву...", - "compressionFormat": "Формат стиснення", - "selectedFiles": "Вибрані файли", - "andMoreFiles": "та ще {{count}}...", - "compress": "Стиснути", - "compressingFiles": "Стиснення елементів {{count}} до {{name}}...", - "filesCompressedSuccessfully": "{{name}} успішно створено", - "compressFailed": "Збій стиснення", - "edit": "Редагувати", - "preview": "Прев'ю", - "previous": "Попереднє", - "next": "Уперед", - "pageXOfY": "Сторінка {{current}} з {{total}}", - "zoomOut": "Зменшити масштаб", - "zoomIn": "Збільшити масштаб", - "newFile": "Новий файл", - "newFolder": "Нова папка", - "rename": "Перейменувати", - "uploading": "Відвантаження...", - "uploadingFile": "Завантаження {{name}}...", - "fileName": "Назва файлу", - "folderName": "Ім'я папки", - "fileUploadedSuccessfully": "Файл \"{{name}}\" успішно завантажено", - "failedToUploadFile": "Не вдалося завантажити файл", - "fileDownloadedSuccessfully": "Файл \"{{name}}\" успішно завантажено", - "failedToDownloadFile": "Не вдалося завантажити файл", - "fileCreatedSuccessfully": "Файл \"{{name}}\" успішно створено", - "folderCreatedSuccessfully": "Теку \"{{name}}\" успішно створено", - "failedToCreateItem": "Не вдалося створити елемент", - "operationFailed": "Не вдалося виконати операцію {{operation}} для {{name}}: {{error}}", - "failedToResolveSymlink": "Не вдалося відкрити символічне посилання", - "itemsDeletedSuccessfully": "{{count}} елементів успішно видалено", - "failedToDeleteItems": "Не вдалося видалити елементи", - "sudoPasswordRequired": "Необхідно ввести пароль адміністратора", - "enterSudoPassword": "Введіть пароль sudo для продовження цієї операції", - "sudoPassword": "Пароль судо", - "sudoOperationFailed": "Не вдалося виконати операцію", - "sudoAuthFailed": "Помилка автентифікації Sudo", - "dragFilesToUpload": "Для завантаження перетягніть файли", - "emptyFolder": "Ця папка порожня", - "searchFiles": "Пошук файлів...", - "upload": "Вивантажити", - "selectHostToStart": "Виберіть хост для запуску керування файлами", - "sshRequiredForFileManager": "Файловий менеджер вимагає SSH. Цей хост не має включеного SSH.", - "failedToConnect": "Не вдалося підключитися до SSH", - "failedToLoadDirectory": "Не вдалося завантажити каталог", - "noSSHConnection": "Немає підключення до SSH", - "copy": "Копія", - "cut": "Вирізати", - "paste": "Вставити", - "copyPath": "Копіювати шлях", - "copyPaths": "Копіювати шляхи", - "delete": "Видалити", - "properties": "Властивості", - "refresh": "Оновити", - "downloadFiles": "Завантажити файли {{count}} для браузера", - "copyFiles": "Копіювати елементи {{count}}", - "cutFiles": "Вирізати {{count}} елементів", - "deleteFiles": "Видалити {{count}} елементів", - "filesCopiedToClipboard": "Елементи {{count}} скопійовано в буфер обміну", - "filesCutToClipboard": "{{count}} елементів, вирізаних у буфер обміну", - "pathCopiedToClipboard": "Шлях скопійований в буфер обміну", - "pathsCopiedToClipboard": "{{count}} шляхів скопійовано в буфер обміну", - "failedToCopyPath": "Не вдалося скопіювати шлях до буфера обміну", - "movedItems": "Переміщено {{count}} елементів", - "failedToDeleteItem": "Не вдалося видалити елемент", - "itemRenamedSuccessfully": "{{type}} успішно перейменовано", - "failedToRenameItem": "Не вдалося перейменувати елемент", - "download": "Звантажити", - "permissions": "Дозволи", - "size": "Розмір", - "modified": "Змінено", - "path": "Шлях", - "confirmDelete": "Ви дійсно бажаєте видалити {{name}}?", - "permissionDenied": "Відмовлено у доступі", - "serverError": "Помилка сервера", - "fileSavedSuccessfully": "Файл успішно збережений", - "failedToSaveFile": "Не вдалося зберегти файл", - "confirmDeleteSingleItem": "Ви впевнені, що хочете остаточно видалити \"{{name}}\"?", - "confirmDeleteMultipleItems": "Ви впевнені, що хочете остаточно видалити {{count}} елементів?", - "confirmDeleteMultipleItemsWithFolders": "Ви дійсно бажаєте остаточно видалити {{count}} елементів? Це включає в себе теки та їх вміст.", - "confirmDeleteFolder": "Ви дійсно бажаєте остаточно видалити теку \"{{name}}\" і весь зміст?", - "permanentDeleteWarning": "Цю дію скасувати не можна. Будуть видалені з сервера.", - "recent": "Недавні", - "pinned": "Закріплено", - "folderShortcuts": "Скорочення для теки", - "failedToReconnectSSH": "Не вдалося повторно підключити SSH сесію", - "openTerminalHere": "Відкрити термінал тут", - "run": "Ран", - "openTerminalInFolder": "Відкрити термінал в цій теці", - "openTerminalInFileLocation": "Відкривати термінал в місці файлу", - "runningFile": "Працює - {{file}}", - "onlyRunExecutableFiles": "Можна запускати лише виконувані файли", - "directories": "Каталоги", - "removedFromRecentFiles": "Видалено \"{{name}}\" з останніх файлів", - "removeFailed": "Помилка видалення", - "unpinnedSuccessfully": "Відкріплений{{name}}\" успішно", - "unpinFailed": "Відкріпити не вдалося", - "removedShortcut": "Вилучено ярлик\"{{name}}\"", - "removeShortcutFailed": "Не вдалося видалити ярлик", - "clearedAllRecentFiles": "Видалені всі останні файли", - "clearFailed": "Очистити невдалі", - "removeFromRecentFiles": "Видалити з останніх файлів", - "clearAllRecentFiles": "Очистити всі нещодавні файли", - "unpinFile": "Відкріпити файл", - "removeShortcut": "Видалити ярлик", - "pinFile": "Закріпити файл", - "addToShortcuts": "Додати до ярликів", - "pasteFailed": "Вставити не вдалося", - "noUndoableActions": "Непридатні для дії", - "undoCopySuccess": "Операція видалення: Видалено скопійовані файли {{count}}", - "undoCopyFailedDelete": "Не вдалося скасувати. Не вдалося видалити скопійовані файли", - "undoCopyFailedNoInfo": "Не вдалося: Не вдалося знайти інформацію про скопійовані файли", - "undoMoveSuccess": "Скасовано операцію переміщення: Переміщено {{count}} файлів назад у початкове розташування", - "undoMoveFailedMove": "Помилка при скасуванні: Не вдалося перемістити файли назад", - "undoMoveFailedNoInfo": "Помилка скидання: Не вдалося знайти інформацію про переміщений файл", - "undoDeleteNotSupported": "Операція видалення не може бути скасована: файли були остаточно видалені з сервера", - "undoTypeNotSupported": "Непідтримуваний тип операції скасування", - "undoOperationFailed": "Не вдалося виконати операцію відміни", - "unknownError": "Невідома помилка", - "confirm": "Підтвердити", - "find": "Знайти...", - "replace": "Заміняти", - "downloadInstead": "Завантажити натомість", - "keyboardShortcuts": "Комбінації клавіш", - "searchAndReplace": "Пошук і заміна", - "editing": "Редагування", - "search": "Пошук", - "findNext": "Знайти наступне", - "findPrevious": "Знайти попереднє", - "save": "Зберегти", - "selectAll": "Виділити все", - "undo": "Скасувати", - "redo": "Повторити дію", - "moveLineUp": "Перемістити рядок вгору", - "moveLineDown": "Перемістити рядок вниз", - "toggleComment": "Перемкнути коментар", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", "autoComplete": "Auto Complete", - "imageLoadError": "Не вдалося завантажити зображення", - "startTyping": "Початок вводу...", - "unknownSize": "Невідомий розмір", - "fileIsEmpty": "Файл порожній", - "largeFileWarning": "Попередження про великий файл", - "largeFileWarningDesc": "Цей файл {{size}} розміром, що може спричинити проблеми з продуктивністю при відкритті тексту.", - "fileNotFoundAndRemoved": "Файл \"{{name}}\" не знайдено і був видалений з останніх файлів/прикріплених", - "failedToLoadFile": "Не вдалося завантажити файл: {{error}}", - "serverErrorOccurred": "Сталася помилка сервера. Будь ласка, спробуйте ще раз пізніше.", - "autoSaveFailed": "Помилка автоматичного збереження", - "fileAutoSaved": "Автоматичне збереження файлу", - "moveFileFailed": "Не вдалося перемістити {{name}}", - "moveOperationFailed": "Не вдалося здійснити операцію переміщення", - "canOnlyCompareFiles": "Можна порівняти лише два файли", - "comparingFiles": "Порівняння файлів: {{file1}} і {{file2}}", - "dragFailed": "Не вдалося здійснити операцію перетягування", - "filePinnedSuccessfully": "Файл \"{{name}}\" закріплено успішно", - "pinFileFailed": "Не вдалось зв'язати файл", - "fileUnpinnedSuccessfully": "Файл \"{{name}}\" відкріплено успішно", - "unpinFileFailed": "Не вдалося відкріпити файл", - "shortcutAddedSuccessfully": "Ярлик теки \"{{name}}\" успішно додано", - "addShortcutFailed": "Не вдалося додати кнопку", - "operationCompletedSuccessfully": "Елементи {{operation}} {{count}} успішно", - "operationCompleted": "Елементи {{operation}} {{count}}", - "downloadFileSuccess": "Файл {{name}} успішно завантажено", - "downloadFileFailed": "Не вдалося завантажити", - "moveTo": "Перейти до {{name}}", - "diffCompareWith": "Порівняти порівняння з {{name}}", - "dragOutsideToDownload": "Перетягніть поза межами вікна для завантаження ({{count}} файлів)", - "newFolderDefault": "Новачка", - "newFileDefault": "Створити файл", - "successfullyMovedItems": "Записи успішно переміщено {{count}} на {{target}}", - "move": "Пересунути", - "searchInFile": "Пошук у файлі (Ctrl+F)", - "showKeyboardShortcuts": "Показувати комбінації клавіш", - "startWritingMarkdown": "Почніть записувати вміст markdown...", - "loadingFileComparison": "Завантаження порівняння файлів...", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", "reload": "Reload", - "compare": "Порівняти", - "sideBySide": "Пліч-о-пліч", - "inline": "У тексті", - "fileComparison": "Порівняння файлів: {{file1}} проти {{file2}}", - "fileTooLarge": "Файл занадто великий: {{error}}", - "sshConnectionFailed": "Не вдалося встановити SSH підключення. Будь ласка, перевірте підключення до {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Не вдалося завантажити файл: {{error}}", - "connecting": "З’єднання...", - "connectedSuccessfully": "Успішно підключено", - "totpVerificationFailed": "Збій верифікації TOTP", - "warpgateVerificationFailed": "Помилка аутентифікації warpgate", - "authenticationFailed": "Помилка аутентифікації", - "verificationCodePrompt": "Код перевірки:", - "changePermissions": "Змінити дозволи для додатку", - "currentPermissions": "Наявні дозволи", - "owner": "Власник", - "group": "Група", - "others": "Інші", - "read": "Прочитано", - "write": "Записати", - "execute": "Виконати", - "permissionsChangedSuccessfully": "Дозволи успішно змінено", - "failedToChangePermissions": "Не вдалося змінити дозволи", - "name": "Ім'я", - "sortByName": "Ім'я", - "sortByDate": "Дата зміни", - "sortBySize": "Розмір", - "ascending": "За зростанням", - "descending": "За спаданням", - "root": "Корінь", - "new": "Новий", - "sortBy": "Сортувати за", - "items": "Елементи", - "selected": "Вибрано", - "editor": "Редактор", - "octal": "Окталь", - "storage": "Сховище", - "disk": "Диск", - "used": "Використано", - "of": "з", - "toggleSidebar": "Перемикач бічної панелі", - "cannotLoadPdf": "Не вдалося завантажити PDF", - "pdfLoadError": "При завантаженні цього PDF-файлу сталася помилка.", - "loadingPdf": "Завантаження PDF...", - "loadingPage": "Завантаження сторінки..." + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Копіювати на хост…", - "moveToHost": "Перемістити на хост…", - "copyItemsToHost": "Копіювати елементи {{count}} для розміщення…", - "moveItemsToHost": "Перемістити {{count}} елементів на хост…", - "noHostsConnected": "Інших хостів файлових менеджерів немає.", - "noHostsConnectedHint": "Додайте ще один SSH-хост з увімкненим файловим менеджером у Host Manager.", - "selectDestinationHost": "Виберіть хост призначення", - "destinationPath": "Шлях призначення", - "recentDestinations": "Нещодавні напрямки", - "collapseRecentDestinations": "Згорнути останні пункти призначення", - "expandRecentDestinations": "Розгорнути нещодавні пункти призначення", - "browseFolders": "Перегляд папок призначення", - "browseDestination": "Переглянути або ввести шлях", - "confirmCopy": "Копіювати", - "confirmMove": "Перемістити", - "transferring": "Перенесення…", - "compressing": "Стиснення…", - "extracting": "Видобування…", - "transferringItems": "Перенесення {{current}} з {{total}} елементів…", - "transferSuccess": "Перенесення завершено", - "transferError": "Передача не вдалася", - "transferPartial": "Перенесення завершено з {{count}} помилками", - "transferPartialHint": "Не вдалося перенести: {{paths}}", - "itemsSummary": "{{count}} елементів", - "destMustBeDirectory": "Для перенесення кількох елементів місцем призначення має бути каталог", - "selectThisFolder": "Виберіть цю папку", - "browsePathWillBeCreated": "Цієї папки ще не існує. Вона буде створена, коли розпочнеться передача.", - "browsePathError": "Не вдалося відкрити цей шлях на хості призначення.", - "goUp": "Піднімися", - "copyFolderToHost": "Копіювати папку на хост…", - "moveFolderToHost": "Перемістити папку на хост…", - "hostReady": "Готовий", - "hostConnecting": "Підключення…", - "hostDisconnected": "Не підключено", - "hostAuthRequired": "Потрібна автентифікація — спочатку відкрийте Файловий менеджер на цьому хості", - "hostConnectionFailed": "Помилка підключення", - "metricsTitle": "Час пересадки", - "metricsPrepare": "Підготувати пункт призначення: {{duration}}", - "metricsCompress": "Стиснути у вихідному коді: {{duration}}", - "metricsHopSourceRead": "Джерело → сервер: {{throughput}}", - "metricsHopDestSftpWrite": "Сервер → призначення (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Сервер → призначення (локальний): {{throughput}}", - "metricsTransfer": "Від кінця до кінця: {{throughput}} ({{duration}})", - "metricsExtract": "Вилучити в місці призначення: {{duration}}", - "metricsSourceDelete": "Видалити з джерела: {{duration}}", - "metricsTotal": "Всього: {{duration}}", - "progressCompressing": "Стиснення на вихідному хості…", - "progressExtracting": "Видобування в місці призначення…", - "progressTransferring": "Передача даних…", - "progressReconnecting": "Повторне підключення…", - "parallelSegmentsLabel": "Паралельні пересадочні смуги", - "parallelSegmentsOption": "{{count}} смуги", - "parallelSegmentsHint": "Великі файли розділяються на фрагменти по 256 МБ. Кілька ліній використовують окремі з’єднання (наприклад, запуск кількох передач) для підвищення загальної пропускної здатності.", - "progressTotalSpeed": "{{speed}} всього ({{lanes}} смуг)", - "progressTransferringItems": "Передача файлів ({{current}} з {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} файли", - "sourceNotDeletedPartial": "Вихідні файли збережено (часткове перенесення)", - "jumpHostLimitation": "Обидва хости мають бути доступні з сервера Termix. Пряма маршрутизація між хостами не підтримується.", - "cancel": "Скасувати", - "methodLabel": "Спосіб переказу", - "methodAuto": "Авто", - "methodTar": "Архів Tar", - "methodItemSftp": "SFTP для кожного файлу", - "methodAutoHint": "Вибирає tar-файл або SFTP для кожного файлу на основі кількості файлів, розміру та стискальності. Окремі файли завжди використовують потоковий SFTP.", - "methodTarHint": "Стиснення у вихідному коді, перенесення одного архіву, розпакування у кінцевому. Потрібен tar-архів на обох хостах Unix.", - "methodItemSftpHint": "Передавайте кожен файл окремо через SFTP. Працює на всіх хостах, включаючи Windows.", - "methodPreviewLoading": "Розрахунок методу переказу…", - "methodPreviewError": "Не вдалося переглянути метод передачі. Сервер все одно вибере метод під час запуску.", - "methodPreviewWillUseTar": "Буде використано: архів Tar", - "methodPreviewWillUseItemSftp": "Буде використовуватися: SFTP для кожного файлу", - "methodPreviewScanSummary": "{{fileCount}} файлів, {{totalSize}} всього (проскановано на вихідному хості).", - "methodItemSftpLimitation": "Кожен файл використовує той самий потік SFTP, що й копія окремого файлу, послідовно. Прогрес об'єднується для всіх файлів, тому шкала рухається повільно під час роботи з великими файлами.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Ви обрали SFTP для кожного файлу.", - "user_tar": "Ви обрали tar-архів.", - "tar_unavailable": "Архів TAR недоступний на одному або обох хостах — замість нього використовуватиметься SFTP для кожного файлу.", - "windows_host": "Задіяний хост Windows — tar не використовується.", - "auto_multi_large": "Авто: кілька файлів, включаючи великий файл ({{largestSize}}) зі стисливими даними — tar-пакети об'єднуються в одну передачу.", - "auto_single_large_in_archive": "Авто: один великий файл ({{largestSize}}) у цьому наборі — SFTP для кожного файлу.", - "auto_many_incompressible": "Авто: переважно нестисливі дані — SFTP для кожного файлу.", - "auto_many_files": "Авто: багато файлів ({{fileCount}}) — tar зменшує навантаження на кожен файл.", - "auto_default": "Авто: SFTP для кожного файлу для цього набору." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Скасувати", - "progressCancelling": "Скасування…", - "progressStalled": "Зупинився", - "resumedHint": "Повторне підключення до активної передачі, розпочатої в іншому вікні.", - "transferCancelled": "Переказ скасовано", - "transferCancelledCopyHint": "Часткові файли можуть залишатися на пристрої призначення.", - "transferCancelledMoveHint": "Часткові файли можуть залишатися в місці призначення. Вихідні файли не видалено.", - "cleanupDestFiles": "Прибирання місця призначення", - "cleanupDestFilesSuccess": "Видалено часткові файли з місця призначення", - "cleanupDestFilesPartial": "Не вдалося видалити деякі часткові файли", - "cleanupDestFilesNothing": "Нічого прибирати на місці призначення", - "cleanupDestFilesError": "Очищення не вдалося", - "retryTransfer": "Повторити спробу", - "retryTransferError": "Повторна спроба не вдалася", - "transferFailedRetryHint": "На місці призначення збережено частку даних. Повторна спроба відновиться після відновлення з’єднання." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Немає SSH тунелів", - "createFirstTunnelMessage": "Ви ще не створили жодних тунелів SSH. Налаштуйте тунельні з'єднання в хості менеджері.", - "connected": "Підключено", - "disconnected": "Від’єднано", - "connecting": "З’єднання...", - "error": "Помилка", - "canceling": "Скасування...", - "connect": "Підключитися", - "disconnect": "Від'єднатись", - "cancel": "Скасувати", - "port": "Порт", - "localPort": "Локальний порт", - "remotePort": "Віддалений порт", - "currentHostPort": "Поточний порт хоста", - "endpointPort": "Порт кінцевої точки", - "bindIp": "Локальний IP", - "endpointSshConfig": "Налаштування SSH кінцевої точки", - "endpointSshHost": "SSH хост кінцевої точки", - "endpointSshHostPlaceholder": "Оберіть налаштований хост", - "endpointSshHostRequired": "Вибрати SSH кінцевої точки для кожного клієнтського тунелю.", - "attempt": "Спроба {{current}} від {{max}}", - "nextRetryIn": "Наступна спроба через {{seconds}} секунд", - "clientTunnels": "Тунелі клієнта", - "clientTunnel": "Тунель клієнта", - "addClientTunnel": "Додати тунель клієнта", - "noClientTunnels": "Немає клієнтських тунелів, налаштованих на цьому робочому столі.", - "tunnelName": "Назва тунелю", - "remoteHost": "Віддалений хост", - "autoStart": "Авто старт", - "clientAutoStartDesc": "Починається коли цей настільний клієнт відкривається і залишається підключеним.", - "clientManualStartDesc": "Використовуйте старт і зупинку для цього рядка. Термікс не відкриє його автоматично.", - "clientRemoteServerNote": "Віддалене пересилання може вимагати обмеження TcpForwarding і GatewayPorts на кінцевому сервері SSH. Віддалений порт закриється, коли цей комп'ютер відключається.", - "clientTunnelStarted": "Тунель клієнта запущено", - "clientTunnelStopped": "Тунель клієнта зупинено", - "tunnelTestSucceeded": "Тунельний тест пройшов успішно", - "tunnelTestFailed": "Помилка тесту Тунелю", - "localSaved": "Тунелі клієнта збережено", - "localSaveError": "Не вдалося зберегти локальні тунелі клієнта", - "invalidBindIp": "Локальний IP повинен бути дійсною адресою IPv4.", - "invalidLocalTargetIp": "Локальний цільовий IP повинен бути дійсною адресою IPv4.", - "invalidLocalPort": "Локальний порт має бути від 1 до 65535.", - "invalidRemotePort": "Віддалений порт має бути від 1 до 65535.", - "invalidLocalTargetPort": "Локальний цільовий порт має бути від 1 до 65535.", - "invalidEndpointPort": "Порт кінцевої точки має бути від 1 до 65535.", - "duplicateAutoStartBind": "Лише один автостарт тунель може використовувати {{bind}}.", - "manualControlError": "Не вдалося оновити стан тунелю.", - "active": "Активний", - "start": "Старт", - "stop": "Зупинити", - "test": "Тест", - "type": "Тип тунелю", - "typeLocal": "Локальний (-L)", - "typeRemote": "Віддалений (-R)", - "typeDynamic": "Динамічний (-D)", - "typeServerLocalDesc": "Поточний хост до кінцевої точки.", - "typeServerRemoteDesc": "Повернення до поточного хосту.", - "typeClientLocalDesc": "Локальний комп'ютер для кінцевої точки.", - "typeClientRemoteDesc": "Повернення вихідної точки до локального комп'ютера.", - "typeClientDynamicDesc": "SOCKS на локальному комп'ютері.", - "typeDynamicDesc": "Forward SOCKS5 підключити трафік через SSH", - "forwardDescriptionServerLocal": "Поточний хост {{sourcePort}} → кінцеву точку {{endpointPort}}.", - "forwardDescriptionServerRemote": "Кінцева точка {{endpointPort}} → поточний хост {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS на цьому хості {{sourcePort}}.", - "forwardDescriptionClientLocal": "Локальний {{sourcePort}} → віддалене {{endpointPort}}.", - "forwardDescriptionClientRemote": "Віддалене {{sourcePort}} → локальне {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS на локальному порту {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS через {{endpoint}}", - "autoNameClientLocal": "Локальний {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → локальна {{localPort}}", - "autoNameClientDynamic": "SOCKS {{localPort}} через {{endpoint}}", - "route": "Рейс:", - "lastStarted": "Останній початок", - "lastTested": "Останнє тестування", - "lastError": "Остання помилка", - "maxRetries": "Кількість спроб", - "maxRetriesDescription": "Максимальна кількість спроб повторної спроби.", - "retryInterval": "Інтервал повтору (секунди)", - "retryIntervalDescription": "Час очікування між спробами повторити спробу.", - "local": "Місцевий", - "remote": "Пульт", - "destination": "Пункт призначення", - "host": "Хост", - "mode": "Режим", - "noHostSelected": "Не вибрано жодного хосту", - "working": "Працює..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "ЦП", - "memory": "Пам'ять", - "disk": "Диск", - "network": "Мережа", - "uptime": "Час роботи", - "processes": "Процеси", - "available": "Доступно", - "free": "Безкоштовно", - "connecting": "З’єднання...", - "connectionFailed": "Не вдалося підключитися до сервера", - "naCpus": "N/A ЦП", - "cpuCores_one": "{{count}} Ядро", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "Використання ЦП", - "memoryUsage": "Використання пам'яті", - "diskUsage": "Використання диску", - "failedToFetchHostConfig": "Не вдалося отримати налаштування хосту", - "serverOffline": "Сервер не в мережі", - "cannotFetchMetrics": "Не вдається отримати метрики з сервера офлайн", - "totpFailed": "Збій верифікації TOTP", - "noneAuthNotSupported": "Статистика сервера не підтримує тип аутентифікації.", - "load": "Загрузити", - "systemInfo": "Інформація про систему", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", "hostname": "Hostname", - "operatingSystem": "Операційна система", - "kernel": "Ядро", - "seconds": "секунди", - "networkInterfaces": "Мережеві інтерфейси", - "noInterfacesFound": "Не знайдено інтерфейсів мережі", - "noProcessesFound": "Процесів не знайдено", - "loginStats": "Статистика входу SSH", - "noRecentLoginData": "Немає останніх даних для входу", - "executingQuickAction": "Виконання {{name}}...", - "quickActionSuccess": "{{name}} успішно завершено", - "quickActionFailed": "{{name}} не вдалося", - "quickActionError": "Не вдалося виконати {{name}}", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Прослуховування портів", + "title": "Listening Ports", "protocol": "Protocol", - "port": "Порт", - "address": "Адреса", - "process": "Процес", - "noData": "Немає прослуховування портів" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Брандмауер", - "inactive": "Неактивний", - "policy": "Політика", - "rules": "правила", - "noData": "Дані брандмауера відсутні", - "action": "Дія", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "Порт", - "source": "Джерело", - "anywhere": "Будь-де" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Завантажити Avg", - "swap": "Поміняти", - "architecture": "Архітектура", - "refresh": "Оновити", - "retry": "Повторити спробу" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Власне керування SSH та віддаленим робочим столом", - "loginTitle": "Увійти до Termix", - "registerTitle": "Створити обл. запис", - "forgotPassword": "Забули пароль?", - "rememberMe": "Запам'ятати пристрій за 30 днів (включає TOTP)", - "noAccount": "Ще не зареєстровані?", - "hasAccount": "Вже маєте обліковий запис?", - "twoFactorAuth": "Двофакторна автентифікація", - "enterCode": "Введіть код підтвердження", - "backupCode": "Або використайте резервний код", - "verifyCode": "Код перевірки", - "redirectingToApp": "Перенаправлення до програми...", - "sshAuthenticationRequired": "Необхідна SSH-аутентифікація", - "sshNoKeyboardInteractive": "Ключова інтерактивна автентифікація не доступна", - "sshAuthenticationFailed": "Помилка аутентифікації", - "sshAuthenticationTimeout": "Таймаут автентифікації", - "sshNoKeyboardInteractiveDescription": "Сервер не підтримує автентифікацію за допомогою клавіатури. Введіть свій пароль чи SSH ключ.", - "sshAuthFailedDescription": "Надані облікові дані були невірні. Будь ласка, спробуйте ще раз із дійсними обліковими даними.", - "sshTimeoutDescription": "Час входу минув. Будь ласка, спробуйте ще раз.", - "sshProvideCredentialsDescription": "Будь ласка, надайте свої дані SSH для підключення до цього сервера.", - "sshPasswordDescription": "Введіть пароль для цього SSH підключення.", - "sshKeyPasswordDescription": "Якщо ваш SSH ключ зашифровано, введіть кодову фразу тут.", - "passphraseRequired": "Потрібна кодова фраза", - "passphraseRequiredDescription": "Ключ SSH зашифровано. Введіть секретну фразу, щоб розблокувати його.", - "back": "Відмінити", - "firstUser": "Перший користувач", - "firstUserMessage": "Ви перший користувач і буде створений адміністратором. Ви можете переглянути налаштування адміністратора в випадаючому списку для бічної панелі. Якщо ви думаєте, що це помилка, перевірте журнал докерів, або створіть проблему на GitHub.", - "external": "Зовнішній", - "loginWithExternal": "Увійти за допомогою зовнішнього постачальника", - "loginWithExternalDesc": "Увійдіть, використовуючи налаштованого постачальника зовнішньої ідентифікації", - "externalNotSupportedInElectron": "В даний час зовнішня аутентифікація не підтримується цією програмою. Використовуйте веб-версію для входу на OIDC.", - "resetPasswordButton": "Скидання пароля", - "sendResetCode": "Надіслати код скидання", - "resetCodeDesc": "Введіть ваше ім'я користувача, щоб отримати код скидання пароля. Код буде входити в журнали контейнерів докера.", + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", "resetCode": "Reset Code", - "verifyCodeButton": "Код перевірки", - "enterResetCode": "Введіть 6-значний код із логів контейнерів для користувача:", - "newPassword": "Новий пароль", - "confirmNewPassword": "Підтвердити пароль", - "enterNewPassword": "Введіть новий пароль для користувача:", - "signUp": "Зареєструватися", - "desktopApp": "Стільничний додаток", - "loggingInToDesktopApp": "Вхід до настільної програми", - "loadingServer": "Завантаження сервера...", - "dataLossWarning": "Скидання пароля таким чином видалить всі збережені хости SSH, облікові дані та інші зашифровані дані. Цю дію не можна скасувати. Використовуйте цю дію, якщо ви забули свій пароль і не ввійшли.", - "authenticationDisabled": "Автентифікація відключена", - "authenticationDisabledDesc": "Усі методи автентифікації зараз вимкнено. Будь ласка, зверніться до адміністратора.", - "attemptsRemaining": "Залишилося спроб {{count}}" + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Перевірити SSH ключ хоста", - "keyChangedWarning": "SSH ключ хоста змінено", - "firstConnectionTitle": "вперше при підключенні до цього хосту", - "firstConnectionDescription": "Неможливо встановити автентифікацію цього хоста. Перевірте, чи збігається ваш запит.", - "keyChangedDescription": "Ключ хоста для цього сервера було змінено після вашого останнього підключення. Це може означати проблему безпеки.", - "previousKey": "Попередній ключ", - "newFingerprint": "Новий відбиток пальця", - "fingerprint": "Відбиток пальця", - "verifyInstructions": "Щоб продовжити і зберегти цей відбиток для майбутніх з'єднань, натисніть кнопку \"Прийняти\".", - "securityWarning": "Попередження системи безпеки", - "acceptAndContinue": "Прийняти та продовжити", - "acceptNewKey": "Прийняти новий ключ і продовжити" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Не вдалося підключитися до бази даних", - "unknownError": "Невідома помилка", - "loginFailed": "Помилка входу в систему", - "failedPasswordReset": "Не вдалося ініціювати скидання пароля", - "failedVerifyCode": "Не вдалося перевірити код скидання", - "failedCompleteReset": "Не вдалося виконати скидання пароля", + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", "invalidTotpCode": "Invalid TOTP code", - "failedOidcLogin": "Не вдалося запустити OIDC логін", - "silentSigninOidcUnavailable": "Вхід без звуку, було запитано, але логін OIDC недоступний.", - "failedUserInfo": "Не вдалося отримати дані користувача після входу", - "oidcAuthFailed": "Помилка автентифікації OIDC", - "invalidAuthUrl": "Неприпустима URL-адреса авторизації, отримана від backend", - "requiredField": "Це поле обов'язкове для заповнення", - "minLength": "Мінімальна довжина {{min}}", - "passwordMismatch": "Паролі не збігаються", - "passwordLoginDisabled": "Ім'я користувача/пароль в даний час відключені", - "sessionExpired": "Сесія минула - будь ласка, увійдіть знову", - "totpRateLimited": "Обмеження частоти: занадто багато спроб перевірки TOTP. Будь ласка, спробуйте ще раз пізніше.", - "totpRateLimitedWithTime": "Обмеження частоти пошуку. Занадто багато спроб перевірки TOTP. Будь ласка, зачекайте {{time}} секунд перед повторною спробою.", - "resetCodeRateLimited": "Обмеження частоти підтвердження. Будь ласка, спробуйте ще раз пізніше.", - "resetCodeRateLimitedWithTime": "Обмеження частоти: занадто багато спроб підтвердження. Будь ласка, зачекайте {{time}} секунд, перш ніж повторювати спробу.", - "authTokenSaveFailed": "Не вдалося зберегти маркер автентифікації", - "failedToLoadServer": "Не вдалося завантажити сервер" + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Адміністратор вимкнув реєстрацію нового облікового запису. Будь ласка, увійдіть або зверніться до адміністратора.", - "userNotAllowed": "Ваш обліковий запис не дозволено реєструватися. Будь ласка, зверніться до адміністратора.", - "databaseConnectionFailed": "Не вдалося підключитися до сервера бази даних", - "resetCodeSent": "Код скидання надіслано до логів Docker", - "codeVerified": "Код успішно перевірено", - "passwordResetSuccess": "Пароль успішно скинуто", - "loginSuccess": "Вхід вдало виконано", - "registrationSuccess": "Реєстрація пройшла успішно" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Тунелі локального комп'ютера налаштовані SSH хости.", - "c2sTunnelPresets": "Пресети тунелів клієнта", - "c2sTunnelPresetsDesc": "Збережіть локальний список тунелів для цього комп'ютера в якості пресету сервера або завантажте пресет назад до цього клієнта.", - "c2sTunnelPresetsUnavailable": "Шаблони клієнтського тунелю доступні лише в стільничному клієнті.", - "c2sPresetName": "Назва заданого параметра", - "c2sPresetNamePlaceholder": "Ім'я пресету клієнта", - "c2sPresetToLoad": "Пресет для завантаження", - "c2sNoPresetSelected": "Шаблони не вибрані", - "c2sNoPresets": "Немає збережених попередніх налаштувань", - "c2sLoadPreset": "Загрузити", - "c2sCurrentLocalConfig": "{{count}} локальний клієнт тунелі налаштований на цьому робочому столі.", - "c2sPresetSyncNote": "Пресети явні знімки; завантаження один замінює локальний список тунелів для клієнта.", - "c2sPresetSaved": "Налаштування клієнтського тунелю збережено", - "c2sPresetLoaded": "Налаштування клієнтського тунелю завантажені локально", - "c2sPresetRenamed": "Налаштування клієнтського тунелю перейменовано", - "c2sPresetDeleted": "Налаштування клієнтського тунелю видалено", - "c2sPresetLoadError": "Не вдалося завантажити налаштування тунелю для клієнта" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Мова:", - "keyPassword": "пароль ключа", - "pastePrivateKey": "Вставте свій приватний ключ тут...", - "localListenerHost": "127.0.1 (послухати локально)", - "localTargetHost": "127.0.0.1 (ціль на цьому комп'ютері)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "Введіть пароль", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Приладна дошка", - "loading": "Завантаження панелі...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Підтримка", + "support": "Support", "discord": "Discord", - "serverOverview": "Огляд сервера", - "version": "Версія", - "upToDate": "Теперішня версія", - "updateAvailable": "Доступне оновлення", - "beta": "Бета", - "uptime": "Час роботи", - "database": "База даних", - "healthy": "Здоровий", - "error": "Помилка", - "totalHosts": "Всього хостів", - "totalTunnels": "Всього тунелів", - "totalCredentials": "Загальна кількість облікових даних", - "recentActivity": "Остання активність", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", "reset": "Reset", - "loadingRecentActivity": "Завантаження недавньої діяльності...", - "noRecentActivity": "Нема недавньої активності", - "quickActions": "Швидкі дії", - "addHost": "Додати хост", - "addCredential": "Додати", - "adminSettings": "Адміністративні налаштування", - "userProfile": "Профіль користувача", - "serverStats": "Статистика на сервері", - "loadingServerStats": "Завантаження статистики сервера...", - "noServerData": "Немає даних про сервер", - "cpu": "ЦП", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", "ram": "RAM", - "customizeLayout": "Налаштування панелі", - "dashboardSettings": "Налаштування панелі", - "enableDisableCards": "Увімкнути/Вимкнути картки", - "resetLayout": "Відновити за замовчуванням", - "serverOverviewCard": "Огляд сервера", - "recentActivityCard": "Остання активність", - "networkGraphCard": "Графік мережі", - "networkGraph": "Графік мережі", - "quickActionsCard": "Швидкі дії", - "serverStatsCard": "Статистика на сервері", - "panelMain": "Основне", - "panelSide": "Сторона", - "justNow": "прямо зараз" + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ВИМКНУТИ", - "hostsOnline": "Хости онлайн", - "activeTunnels": "Активні тунелі", - "registerNewServer": "Реєстрація нового сервера", - "storeSshKeysOrPasswords": "Зберігати SSH ключі або паролі", - "manageUsersAndRoles": "Управління користувачами і ролями", - "manageYourAccount": "Керувати вашим профілем", - "hostStatus": "Статус хоста", - "noHostsConfigured": "Не налаштовано хостів", - "online": "У режимі онлайн", - "offline": "ЗАЛИШИТИ", - "onlineLower": "Онлайн", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "Додати:", - "commandPalette": "Командна палітра", - "done": "Виконано", - "editModeInstructions": "Перетягніть картки для зміни порядку · Перетягніть розділювач стовпців для зміни розмірів · Перетягніть нижній край картки для зміни розміру в їх висоту · Смітник для видалення", - "empty": "Порожньо", - "clear": "Очистити" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Додати хост", - "addGroup": "Додати групу", - "addLink": "Додати посилання", - "zoomIn": "Збільшити масштаб", - "zoomOut": "Зменшити масштаб", - "resetView": "Скинути вигляд", - "selectHost": "Виберіть хост", - "chooseHost": "Виберіть хост...", - "parentGroup": "Батьківська група", - "noGroup": "Немає групи", - "groupName": "Назва групи", - "color": "Колір", - "source": "Джерело", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "Перемістити до групи", - "selectGroup": "Виберіть групу...", - "addConnection": "Додати контакти", - "hostDetails": "Деталі хоста", - "removeFromGroup": "Видалити з групи", - "addHostHere": "Додати хост сюди", - "editGroup": "Редагувати групу", - "delete": "Видалити", - "add": "Додати", - "create": "Створити", - "move": "Пересунути", - "connect": "Підключитися", - "createGroup": "Нова група", - "selectSourcePlaceholder": "Вибрати джерело...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "Неприпустимий файл", - "hostAlreadyExists": "Хост вже в топології", - "connectionExists": "З'єднання вже існує", - "unknown": "Не вказано", - "name": "Ім'я", - "ip": "Адреса IP", - "status": "Статус", - "failedToAddNode": "Не вдалося додати вузол", - "sourceDifferentFromTarget": "Джерело і ціль повинні бути різними", - "exportJSON": "Експортувати JSON", - "importJSON": "Імпорт JSON", - "terminal": "Термінал", - "fileManager": "Файловий менеджер", - "tunnel": "Тунель", - "docker": "Докер", - "serverStats": "Статистика на сервері", - "noNodes": "Вузли ще немає" + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker вимкнений для цього хосту", - "validating": "Перевірка Docker...", - "connecting": "З’єднання...", - "error": "Помилка", - "version": "Докер {{version}}", - "connectionFailed": "Не вдалося підключитися до Docker", - "containerStarted": "Контейнер {{name}} запущено", - "failedToStartContainer": "Не вдалося почати контейнер {{name}}", - "containerStopped": "Контейнер {{name}} зупинено", - "failedToStopContainer": "Не вдалося зупинити контейнер {{name}}", - "containerRestarted": "Контейнер {{name}} перезапущено", - "failedToRestartContainer": "Не вдалося перезапустити контейнер {{name}}", - "containerPaused": "Контейнер {{name}} призупинено", - "containerUnpaused": "Контейнер {{name}} не призупинено", - "failedToTogglePauseContainer": "Не вдалося перемкнути режим паузи для контейнера {{name}}", - "containerRemoved": "Видалено контейнер {{name}}", - "failedToRemoveContainer": "Не вдалося видалити контейнер {{name}}", - "image": "Зображення", - "ports": "Порти", - "noPorts": "Немає портів", - "start": "Старт", - "confirmRemoveContainer": "Ви впевнені, що хочете видалити контейнер '{{name}}'? Цю дію неможливо скасувати.", - "runningContainerWarning": "Увага: цей контейнер зараз запущений. Видаляння його призведе до зупинки контейнера.", - "loadingContainers": "Завантаження контейнерів...", - "manager": "Менеджер Docker", - "autoRefresh": "Автоматичне оновлення", - "timestamps": "Відмітки часу", - "lines": "Лінії", - "filterLogs": "Фільтрувати журнали...", - "refresh": "Оновити", - "download": "Звантажити", - "clear": "Очистити", - "logsDownloaded": "Журнали успішно завантажено", - "last50": "Останні 50", - "last100": "Останні 100", - "last500": "Останні 500", - "last1000": "Останні 1000", - "allLogs": "Всі логи", - "noLogsMatching": "Немає журналів, що відповідають \"{{query}}\"", - "noLogsAvailable": "Немає записів у журналі", - "noContainersFound": "Контейнери не знайдено", - "noContainersFoundHint": "Немає Docker контейнерів для цього хосту", - "searchPlaceholder": "Пошук контейнерів...", - "allStatuses": "Всі статуси", - "stateRunning": "Біг", - "statePaused": "Призупинено", - "stateExited": "Вихід", - "stateRestarting": "Перезавантаження", - "noContainersMatchFilters": "Не знайдено контейнерів з вашими фільтрами", - "noContainersMatchFiltersHint": "Спробуйте налаштувати умови пошуку або фільтра", - "failedToFetchStats": "Не вдалося отримати статистику контейнерів", - "containerNotRunning": "Контейнер не запущений", - "startContainerToViewStats": "Запустити контейнер для перегляду статистики", - "loadingStats": "Завантаження статистики...", - "errorLoadingStats": "Помилка при завантаженні статистики", - "noStatsAvailable": "Немає статистичних даних", - "cpuUsage": "Використання ЦП", - "current": "Поточна версія", - "memoryUsage": "Використання пам'яті", - "networkIo": "Мережевий ввід/вивід", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", + "version": "Docker {{version}}", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", "input": "Input", - "output": "Вихід", - "blockIo": "Заблокувати вводу-виводу", - "read": "Прочитано", - "write": "Записати", - "pids": "PID", - "containerInformation": "Інформація про контейнер", - "name": "Ім'я", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "Держава", - "containerMustBeRunning": "Контейнер повинен виконуватися для доступу до консолі", - "verificationCodePrompt": "Введіть код підтвердження", - "totpVerificationFailed": "TOTP підтвердження не вдалося. Будь ласка, спробуйте ще раз.", - "warpgateVerificationFailed": "Спроба розпізнавання warpgate не вдалася. Будь ласка, спробуйте ще раз.", - "connectedTo": "Підключено до {{containerName}}", - "disconnected": "Від’єднано", - "consoleError": "Консольна помилка", - "errorMessage": "Помилка: {{message}}", - "failedToConnect": "Не вдалося підключитися до контейнера", - "console": "Консоль", - "selectShell": "Виберіть оболонку", - "bash": "Баш", - "sh": "висока", - "ash": "попіл", - "connect": "Підключитися", - "disconnect": "Від'єднатись", - "notConnected": "Не підключено", - "clickToConnect": "Натисніть \"З'єднатися\" для запуску сеансу оболонки", - "connectingTo": "Підключення до {{containerName}}...", - "containerNotFound": "Контейнер не знайдено", - "backToList": "Назад до списку", - "logs": "Логи", - "stats": "Статистика", - "consoleTab": "Консоль", - "startContainerToAccess": "Запустити контейнер для доступу до консолі" + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", + "bash": "Bash", + "sh": "sh", + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Загальні налаштування", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Спільноти", - "sectionSessions": "Сеанси", - "sectionRoles": "Ролі", - "sectionDatabase": "База даних", - "sectionApiKeys": "Ключі API", - "allowRegistration": "Дозволити реєстрацію нових користувачів", - "allowRegistrationDesc": "Дозволити власноруч реєструватися новим користувачам", - "allowPasswordLogin": "Дозволити введення пароля", - "allowPasswordLoginDesc": "Ім'я користувача/пароль", - "oidcAutoProvision": "Автоматичне забезпечення OIDC", - "oidcAutoProvisionDesc": "Автостворення облікових записів для користувачів OIDC, навіть при відключенні реєстрації", - "allowPasswordReset": "Дозволити скидання пароля", - "allowPasswordResetDesc": "Скинути код через журнали Docker", - "sessionTimeout": "Таймаут сеансу", - "hours": "годин", - "sessionTimeoutRange": "Мін 1 год · Максимум 720Н", - "monitoringDefaults": "Моніторинг за замовчуванням", - "statusCheck": "Перевірка стану", - "metrics": "Метрика", - "sec": "сек", - "logLevel": "Рівень журналювання", - "enableGuacamole": "Увімкнути Guacamole", - "enableGuacamoleDesc": "RDP/VNC віддалений робочий стіл", - "guacdUrl": "посилання на гуакд", - "oidcDescription": "Налаштувати OpenID Connect для SSO. Поля позначені * обов'язкові для SSO.", - "oidcClientId": "ID клієнта", - "oidcClientSecret": "Секретний ключ", - "oidcAuthUrl": "URL авторизації", - "oidcIssuerUrl": "Адреса емітента", - "oidcTokenUrl": "URL токену", - "oidcUserIdentifier": "Ідентифікатор користувача", - "oidcDisplayName": "Відображення шляху до імені", - "oidcScopes": "Області використання", - "oidcUserinfoUrl": "Змінити URL інформації користувача", - "oidcAllowedUsers": "Дозволені користувачі", - "oidcAllowedUsersDesc": "Один лист на рядок. Залиште порожнім, щоб дозволити всіх.", - "removeOidc": "Видалити", - "usersCount": "Користувач {{count}}", - "createUser": "Створити", - "newRole": "Нова роль", - "roleName": "Ім'я", - "roleDisplayName": "Ім'я для відображення", - "roleDescription": "Опис", - "rolesCount": "Ролі {{count}}", - "createRole": "Створити", - "creating": "Створюю...", - "exportDatabase": "Експорт бази даних", - "exportDatabaseDesc": "Завантажити резервну копію всіх вузлів, облікових даних та налаштувань", - "export": "Експорт", - "exporting": "Експорт...", - "importDatabase": "Імпорт бази даних", - "importDatabaseDesc": "Відновити з файлу резервної копії .sqlite", - "importDatabaseSelected": "Вибрано: {{name}}", - "selectFile": "Виберіть файл", - "changeFile": "Змінити", - "import": "Імпорт", - "importing": "Імпорт...", - "apiKeysCount": "Клавіші {{count}}", - "newApiKey": "Новий API ключ", - "apiKeyCreatedWarning": "Ключ створено - скопіюйте його зараз, його не буде показано знову.", - "apiKeyName": "Ім'я", - "apiKeyUser": "Користувач", - "apiKeySelectUser": "Виберіть користувача...", - "apiKeyExpiresAt": "Завершується в", - "createKey": "Виготовити ключ", - "apiKeyNoExpiry": "Немає терміну дії", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "Подвійна Авторизація", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Місцевий", - "adminStatusAdministrator": "Адміністратор", - "adminStatusRegularUser": "Звичайний користувач", - "adminBadge": "АДМІНА", - "systemBadge": "СИС", - "customBadge": "КОРИСТУВАЦЬКА", - "youBadge": "ВИ", - "sessionsActive": "Активний {{count}}", - "sessionActive": "Активний: {{time}}", - "sessionExpires": "Термін дії: {{time}}", - "revokeAll": "Всі", - "revokeAllSessionsSuccess": "Всі сеанси для користувача відкликано", - "revokeAllSessionsFailed": "Не вдалося скасувати сеанси", - "revokeSessionFailed": "Не вдалося видалити сесію", - "addRole": "Додати роль", - "noCustomRoles": "Користувацькі ролі не визначені", - "removeRoleFailed": "Не вдалося видалити роль", - "assignRoleFailed": "Не вдалося призначити роль", - "deleteRoleFailed": "Не вдалося видалити роль", - "userAdminAccess": "Адміністратор", - "userAdminAccessDesc": "Повний доступ до всіх налаштувань адміністратора", - "userRoles": "Ролі", - "revokeAllUserSessions": "Скасувати всі сеанси", - "revokeAllUserSessionsDesc": "Примусовий повторний вхід на всіх пристроях", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", + "sessionExpires": "Exp: {{time}}", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "Видалення цього користувача є незворотнім.", - "deleteUser": "Видалити {{username}}", - "deleting": "Видалення...", - "deleteUserFailed": "Не вдалося вилучити користувача", - "deleteUserSuccess": "Користувача \"{{username}}\" видалено", - "deleteRoleSuccess": "Роль \"{{name}}\" видалена", - "revokeKeySuccess": "Ключ \"{{name}}відкликано", - "revokeKeyFailed": "Не вдалося відкликати ключ", - "copiedToClipboard": "Скопійовано до буферу обміну", - "done": "Виконано", - "createUserTitle": "Створити користувача", - "createUserDesc": "Створити новий локальний обліковий запис.", - "createUserUsername": "Ім'я користувача", - "createUserPassword": "Пароль", - "createUserPasswordHint": "Мінімум 6 символів.", - "createUserEnterUsername": "Введіть ім'я користувача", - "createUserEnterPassword": "Уведіть пароль", - "createUserSubmit": "Створити користувача", - "editUserTitle": "Керування користувачем: {{username}}", - "editUserDesc": "Редагування ролей, статус, сесій та налаштувань облікового запису.", - "editUserUsername": "Ім'я користувача", - "editUserAuthType": "Тип автентифікації", - "editUserAdminStatus": "Статус адміністратора", - "editUserUserId": "ID користувача", - "linkAccountTitle": "Посилання OIDC до облікового запису пароля", - "linkAccountDesc": "Об’єднайте обліковий запис OIDC {{username}} з існуючим локальним обліковим записом.", - "linkAccountWarningTitle": "Це приведе до:", - "linkAccountEffect1": "Видалити обліковий запис тільки в OIDC-", - "linkAccountEffect2": "Додати OIDC логін до цільового облікового запису", - "linkAccountEffect3": "Дозволити логування OIDC та пароль", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "Введіть локальне ім'я користувача облікового запису, щоб прив'язати його до", - "linkAccounts": "Пов’язати облікові записи", - "linkAccountSuccess": "Обліковий запис OIDC, пов’язаний з «{{username}}»", - "linkAccountFailed": "Не вдалося підключити обліковий запис OIDC", - "linkAccountInProgress": "Зв'язування...", - "saving": "Збереження...", - "updateRegistrationFailed": "Не вдалося оновити налаштування реєстрації", - "updatePasswordLoginFailed": "Не вдалося оновити параметри входу в гасло", - "updateOidcAutoProvisionFailed": "Не вдалося оновити параметр автозабезпечення OIDC", - "updatePasswordResetFailed": "Не вдалося оновити налаштування пароля", - "sessionTimeoutRange2": "Таймаут сесії має бути від 1 до 720 годин", - "sessionTimeoutSaved": "Тайм-аут сесії збережено", - "sessionTimeoutSaveFailed": "Не вдалося зберегти тайм-аут сесії", - "monitoringIntervalInvalid": "Невірне значення інтервалу", - "monitoringSaved": "Спостерігати за налаштуваннями збережено", - "monitoringSaveFailed": "Не вдалося зберегти налаштування моніторингу", - "guacamoleSaved": "Налаштування Guacamole збережено", - "guacamoleSaveFailed": "Не вдалося зберегти настройки Guacamole", - "guacamoleUpdateFailed": "Не вдалося оновити налаштування Guacamole", - "logLevelUpdateFailed": "Не вдалося оновити рівень журналу", - "oidcSaved": "Налаштування OIDC збережено", - "oidcSaveFailed": "Не вдалося зберегти конфігурацію OIDC", - "oidcRemoved": "Налаштування OIDC видалено", - "oidcRemoveFailed": "Не вдалося видалити конфігурацію OIDC", - "createUserRequired": "Ім'я користувача і пароль обов'язкові для заповнення", - "createUserPasswordTooShort": "Пароль повинен містити щонайменше 6 символів", - "createUserSuccess": "Користувача \"{{username}}\" створено", - "createUserFailed": "Не вдалося створити користувача", - "updateAdminStatusFailed": "Не вдалося оновити статус адміністратора", - "allSessionsRevoked": "Всі сесії відкликано", - "revokeSessionsFailed": "Не вдалося скасувати сеанси", - "createRoleRequired": "Ім'я та відображуване ім'я обов'язкові", - "createRoleSuccess": "Роль \" створена{{name}}\"", - "createRoleFailed": "Не вдалося створити роль", - "apiKeyNameRequired": "Потрібне ім'я ключа", - "apiKeyUserRequired": "Необхідний ID користувача", - "apiKeyCreatedSuccess": "Створено ключ API \"{{name}}\"", - "apiKeyCreateFailed": "Не вдалося створити ключ API", - "exportSuccess": "Базу даних успішно експортовано", - "exportFailed": "Не вдалося експортувати базу даних", - "importSelectFile": "Будь ласка, спочатку виберіть файл", - "importCompleted": "Імпорт завершено: імпортовано {{total}} елементів, пропущено {{skipped}}", - "importFailed": "Помилка імпорту: {{error}}", - "importError": "Не вдалося імпортувати базу даних" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Хост", - "hostPlaceholder": "192.168.1.1 або приклад.com", - "portLabel": "Порт", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Ім'я користувача", - "usernamePlaceholder": "ім'я користувача", - "authLabel": "Авторизація", - "passwordLabel": "Пароль", - "passwordPlaceholder": "пароль", - "privateKeyLabel": "Приватний ключ", - "privateKeyPlaceholder": "Вставити приватний ключ...", - "credentialLabel": "Облікові дані", - "credentialPlaceholder": "Оберіть збережені облікові дані", - "connectToTerminal": "Підключення до терміналу", - "connectToFiles": "Підключення до файлів" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Термінал не вибраний", - "noTerminalSelectedHint": "Відкрийте SSH термінал вкладки для перегляду історії команд", - "searchPlaceholder": "Пошук в історії...", - "clearAll": "Очистити все", - "noHistoryEntries": "Немає історії", - "trackingDisabled": "Відстеження історії вимкнено", - "trackingDisabledHint": "Увімкніть це в налаштуваннях вашого профілю для запису команд." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Кей запис", - "recordToTerminals": "Запис у термінали", - "selectAll": "Всі", - "selectNone": "Без ефекту", - "noTerminalTabsOpen": "Відсутні відкриті відкриті термінальні вкладки", - "selectTerminalsAbove": "Виберіть термінали вище", - "broadcastInputPlaceholder": "Введіть тут, щоб транслювати клавіші зі значками...", - "stopRecording": "Зупинити запис", - "startRecording": "Розпочати запис", - "settingsTitle": "Налаштування", - "enableRightClickCopyPaste": "Увімкнути копіювання/вставку правою кнопкою миші" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Макет", - "selectLayoutAbove": "Виберіть макет вище", - "selectLayoutHint": "Виберіть кількість панелей для відображення", - "panesTitle": "Панелі", - "openTabsTitle": "Відкрити вкладки", - "dragTabsHint": "Перетягніть вкладки на панелі вище або скористайтеся функцією швидкого призначення", - "dropHere": "Перетягніть сюди", - "emptyPane": "Порожньо", - "dashboard": "Приладна дошка", - "clearSplitScreen": "Очистити розділений екран", - "quickAssign": "Швидке призначення", - "alreadyAssigned": "Панель {{index}}", - "splitTab": "Розділити вкладку", - "addToSplit": "Додати до Спліта", - "removeFromSplit": "Видалити зі Спліта", - "assignToPane": "Призначити панелі" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Створити сніпет", - "createSnippetDescription": "Створити новий сніпет команди для швидкого виконання", - "nameLabel": "Ім'я", - "namePlaceholder": "наприклад, перезавантажити Нгінкс", - "descriptionLabel": "Опис", - "descriptionPlaceholder": "Необов'язковий опис", - "optional": "За бажанням", - "folderLabel": "Тека", - "noFolder": "Без теки (без категорії)", - "commandLabel": "Команда", - "commandPlaceholder": "наприклад, sudo systemctl restart nginx", - "cancel": "Скасувати", - "createSnippetButton": "Створити сніпет", - "createFolderTitle": "Створити теку", - "createFolderDescription": "Організуйте ваші сніпети у папки", - "folderNameLabel": "Ім'я папки", - "folderNamePlaceholder": "наприклад, Системні Команди, Скрипти Docker", - "folderColorLabel": "Колір теки", - "folderIconLabel": "Іконка папки", - "previewLabel": "Прев'ю", - "folderNameFallback": "Ім'я папки", - "createFolderButton": "Створити теку", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "Всі", - "selectNone": "Без ефекту", - "noTerminalTabsOpen": "Відсутні відкриті відкриті термінальні вкладки", - "searchPlaceholder": "Пошук сніпетів...", - "newSnippet": "Новий сніпет", - "newFolder": "Нова папка", - "run": "Ран", - "noSnippetsInFolder": "Немає сніпетів у цій папці", - "uncategorized": "Без категорії", - "editSnippetTitle": "Редагувати сніпет", - "editSnippetDescription": "Оновити цей сніпет команди", - "saveSnippetButton": "Зберегти зміни", - "createSuccess": "Сніпет успішно створений", - "createFailed": "Не вдалося створити сніпет", - "updateSuccess": "Сніпет успішно оновлено", - "updateFailed": "Не вдалося оновити сніпет", - "deleteFailed": "Не вдалося видалити сніпет", - "folderCreateSuccess": "Теку успішно створено", - "folderCreateFailed": "Не вдалося створити теку", - "editFolderTitle": "Змінити теку", - "editFolderDescription": "Перейменувати або змінити вигляд цієї папки", - "saveFolderButton": "Зберегти зміни", - "editFolder": "Редагувати теку", - "deleteFolder": "Видалити папку", - "folderDeleteSuccess": "Теку \"{{name}}\" видалено", - "folderDeleteFailed": "Не вдалося вилучити теку", - "folderEditSuccess": "Папка успішно змінена", - "folderEditFailed": "Не вдалося оновити теку", - "confirmRunMessage": "Виконати \"{{name}}\"?", - "confirmRunButton": "Бігти", - "runSuccess": "Виконано \"{{name}}\" у {{count}} терміналі(ах)", - "copySuccess": "Скопійовано \"{{name}}\" в буфер обміну", - "shareTitle": "Поділитися сніпетом", - "shareUser": "Користувач", - "shareRole": "Роль", - "selectUser": "Виберіть користувача...", - "selectRole": "Виберіть роль...", - "shareSuccess": "Сніпет успішно поділився", - "shareFailed": "Не вдалося поділитись сніпетом", - "revokeSuccess": "Доступ відкликаний", - "revokeFailed": "Не вдалося відкликати доступ", - "currentAccess": "Поточний доступ", - "shareLoadError": "Не вдалося завантажити дані обміну", - "loading": "Завантажується...", - "close": "Закрити", - "reorderFailed": "Не вдалося зберегти порядок фрагментів" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Обліковий запис", - "sectionAppearance": "Зовнішній вигляд", - "sectionSecurity": "Безпека", - "sectionApiKeys": "Ключі API", - "sectionC2sTunnels": "Налаштування C2S", - "usernameLabel": "Ім'я користувача", - "roleLabel": "Роль", - "roleAdministrator": "Адміністратор", - "authMethodLabel": "Метод автентифікації", - "authMethodLocal": "Місцевий", - "twoFaLabel": "двофакторна аутентифікація", - "twoFaOn": "Увімк", - "twoFaOff": "Вимкнено", - "versionLabel": "Версія", - "deleteAccount": "Видалити обліковий запис", - "deleteAccountDescription": "Остаточно видалити ваш обліковий запис", - "deleteButton": "Видалити", - "deleteAccountPermanent": "Ця дія постійна і її неможливо скасувати.", - "deleteAccountWarning": "Усі сеанси, хости, облікові дані та налаштування буде остаточно видалено.", - "confirmPasswordDeletePlaceholder": "Введіть пароль для підтвердження", - "languageLabel": "Мова:", - "themeLabel": "Тема", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "Колір акценту", - "settingsTerminal": "Термінал", - "commandAutocomplete": "Команду Автозаповнення", - "commandAutocompleteDesc": "Показати автодоповнення під час введення тексту", - "historyTracking": "Історія відстеження", - "historyTrackingDesc": "Відстежувати команди терміналу", - "syntaxHighlighting": "Підсвічування синтаксису", - "syntaxHighlightingDesc": "Видобуток терміналу", - "commandPalette": "Командна палітра", - "commandPaletteDesc": "Увімкнути гарячі клавіші", - "reopenTabsOnLogin": "Повторно відкривати вкладки після входу", - "reopenTabsOnLoginDesc": "Відновлюйте відкриті вкладки після входу в систему або оновлення сторінки, навіть з іншого пристрою", - "confirmTabClose": "Підтвердити закриття вкладки", - "confirmTabCloseDesc": "Запитувати перед закриттям вкладок терміналу", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "Показати теги хостів", - "showHostTagsDesc": "Відображення тегів у списку хостів", - "hostTrayOnClick": "Натисніть, щоб розгорнути дії хоста", - "hostTrayOnClickDesc": "Завжди показувати кнопки підключення; натисніть, щоб розгорнути параметри керування, замість того, щоб наводити курсор на них.", - "pinAppRail": "Закріпити рейку додатків", - "pinAppRailDesc": "Залишати ліву бічну панель програм завжди розгорнутою, а не при наведенні курсора", - "settingsSnippets": "Сніпети", - "foldersCollapsed": "Теки вилучені", - "foldersCollapsedDesc": "Згорнути теки за замовчуванням", - "confirmExecution": "Підтвердити виконання", - "confirmExecutionDesc": "Підтвердити перед запуском сніпетів", - "settingsUpdates": "Оновлення", - "disableUpdateChecks": "Вимкнути перевірку оновлень", - "disableUpdateChecksDesc": "Зупинити перевірку оновлень", - "totpAuthenticator": "Генератор TOTP", - "totpEnabled": "2FA увімкнена", - "totpDisabled": "Додати додатковий захист для входу", - "disable": "Вимкнено", - "enable": "Увімкнено", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "Скануйте QR-код або введіть секрет у вашому додатку для автентифікації, а потім введіть 6-значний код", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Перевірити", - "changePassword": "Зміна пароля", - "currentPasswordLabel": "Поточний пароль", - "currentPasswordPlaceholder": "Поточний пароль", - "newPasswordLabel": "Новий пароль", - "newPasswordPlaceholder": "Новий пароль", - "confirmPasswordLabel": "Підтвердіть новий пароль", - "confirmPasswordPlaceholder": "Підвердіть новий пароль", - "updatePassword": "Оновити пароль", - "createApiKeyTitle": "Створити ключ API", - "createApiKeyDescription": "Створити новий ключ API для програмічного доступу.", - "apiKeyNameLabel": "Ім'я", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "необов'язково", - "cancel": "Скасувати", - "createKey": "Виготовити ключ", - "apiKeyCount": "Клавіші {{count}}", - "newKey": "Новий ключ", - "noApiKeys": "Немає API ключів.", - "apiKeyActive": "Активний", - "apiKeyUsageHint": "Додайте ключ до", - "apiKeyUsageHintHeader": "заголовок.", - "apiKeyPermissionsHint": "Ключі успадковують дозволи для створення користувача.", - "roleUser": "Користувач", - "authMethodDual": "Подвійна Авторизація", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Не вдалося запустити налаштування TOTP", - "totpEnter6Digits": "Введіть 6-значний код", - "totpEnabledSuccess": "Двофакторна аутентифікація увімкнена", - "totpInvalidCode": "Неприпустимий код, будь ласка, спробуйте ще раз", - "totpDisableInputRequired": "Введіть свій код TOTP або пароль", - "totpDisabledSuccess": "Двофакторна аутентифікація вимкнена", - "totpDisableFailed": "Не вдалося вимкнути 2FA", - "totpDisableTitle": "Вимкнути 2FA", - "totpDisablePlaceholder": "Введіть код TOTP або пароль", - "totpDisableConfirm": "Вимкнути 2FA", - "totpContinueVerify": "Продовжити перевірку", - "totpVerifyTitle": "Код перевірки", - "totpBackupTitle": "Резервні коди", - "totpDownloadBackup": "Завантажити резервні коди", - "done": "Виконано", - "secretCopied": "Секрет скопійовано до буферу обміну", - "apiKeyNameRequired": "Потрібне ім'я ключа", - "apiKeyCreated": "Створено ключ API \"{{name}}\"", - "apiKeyCreateFailed": "Не вдалося створити ключ API", - "apiKeyUser": "Користувач", - "apiKeyExpires": "Закінчується", - "apiKeyRevoked": "Ключ API \"{{name}}\" відкликано", - "apiKeyRevokeFailed": "Не вдалося відкликати ключ API", - "passwordFieldsRequired": "Необхідні поточні та нові паролі", - "passwordMismatch": "Паролі не збігаються", - "passwordTooShort": "Пароль повинен містити щонайменше 6 символів", - "passwordUpdated": "Пароль успішно оновлено", - "passwordUpdateFailed": "Не вдалося оновити пароль", - "deletePasswordRequired": "Потрібен пароль для видалення вашого облікового запису", - "deleteFailed": "Не вдалося вилучити обліковий запис", - "deleting": "Видалення...", - "colorPickerTooltip": "Вибір кольору", - "themeSystem": "Система", - "themeLight": "Світла", - "themeDark": "Темна", - "themeDracula": "Дракула", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "Соляризований", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "Одна темна", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/vi_VN.json b/src/ui/locales/translated/vi_VN.json index 9b34fb1c..9bc5af32 100644 --- a/src/ui/locales/translated/vi_VN.json +++ b/src/ui/locales/translated/vi_VN.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "Thư mục", - "folder": "Thư mục", - "password": "Mật khẩu", - "key": "Chìa khóa", - "sshPrivateKey": "Khóa riêng SSH", - "upload": "Tải lên", - "keyPassword": "Mật khẩu khóa", - "sshKey": "Khóa SSH", - "uploadPrivateKeyFile": "Tải lên tệp khóa riêng tư", - "searchCredentials": "Tìm kiếm thông tin đăng nhập...", - "addCredential": "Thêm thông tin đăng nhập", - "caCertificate": "Chứng chỉ CA (-cert.pub)", - "caCertificateDescription": "Tùy chọn: Tải lên hoặc dán tệp chứng chỉ được CA ký (ví dụ: id_ed25519-cert.pub). Bắt buộc khi máy chủ SSH của bạn sử dụng xác thực dựa trên chứng chỉ.", - "uploadCertFile": "Tải lên tệp -cert.pub", - "clearCert": "Thông thoáng", - "certLoaded": "Chứng chỉ đã được tải", - "certPublicKeyLabel": "Chứng chỉ CA", - "certTypeLabel": "Loại chứng chỉ", - "pasteOrUploadCert": "Dán hoặc tải lên chứng chỉ -cert.pub...", - "hasCaCert": "Có chứng chỉ CA", - "noCaCert": "Không có chứng chỉ CA" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "Không thể tải cảnh báo", - "failedToDismissAlert": "Không thể tắt cảnh báo" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "Cấu hình máy chủ", - "description": "Cấu hình URL máy chủ Termix để kết nối với các dịch vụ phụ trợ của bạn.", - "serverUrl": "URL máy chủ", - "enterServerUrl": "Vui lòng nhập URL máy chủ", - "saveFailed": "Không thể lưu cấu hình", - "saveError": "Lỗi khi lưu cấu hình", - "saving": "Đang lưu...", - "saveConfig": "Lưu cấu hình", - "helpText": "Nhập URL nơi máy chủ Termix của bạn đang chạy (ví dụ: http://localhost:30001 hoặc https://your-server.com)", - "changeServer": "Thay đổi máy chủ", - "mustIncludeProtocol": "URL máy chủ phải bắt đầu bằng http:// hoặc https://", - "allowInvalidCertificate": "Cho phép chứng chỉ không hợp lệ", - "allowInvalidCertificateDesc": "Chỉ sử dụng cho các máy chủ tự lưu trữ đáng tin cậy có chứng chỉ tự ký hoặc chứng chỉ địa chỉ IP.", - "useEmbedded": "Sử dụng máy chủ cục bộ", - "embeddedDesc": "Chạy Termix với máy chủ cục bộ tích hợp sẵn (không cần máy chủ từ xa).", - "embeddedConnecting": "Đang kết nối đến máy chủ cục bộ...", - "embeddedNotReady": "Máy chủ cục bộ chưa sẵn sàng. Vui lòng đợi một lát và thử lại.", - "localServer": "Máy chủ cục bộ" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "Lỗi kiểm tra phiên bản", - "checkFailed": "Không thể kiểm tra cập nhật", - "upToDate": "Ứng dụng đã được cập nhật.", - "currentVersion": "Bạn đang sử dụng phiên bản {{version}}", - "updateAvailable": "Đã có bản cập nhật", - "newVersionAvailable": "Phiên bản mới đã có sẵn! Bạn đang sử dụng {{current}}, nhưng {{latest}} đã có sẵn.", - "betaVersion": "Phiên bản Beta", - "betaVersionDesc": "Bạn đang sử dụng {{current}}, phiên bản này mới hơn bản phát hành ổn định mới nhất {{latest}}.", - "releasedOn": "Phát hành vào {{date}}", - "downloadUpdate": "Tải xuống bản cập nhật", - "checking": "Đang kiểm tra cập nhật...", - "checkUpdates": "Kiểm tra cập nhật", - "checkingUpdates": "Đang kiểm tra cập nhật...", - "updateRequired": "Cần cập nhật" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "Đóng", - "minimize": "Giảm thiểu", - "online": "Trực tuyến", - "offline": "Ngoại tuyến", - "continue": "Tiếp tục", - "maintenance": "BẢO TRÌ", - "degraded": "bị xuống cấp", - "error": "Lỗi", - "warning": "Cảnh báo", - "unsavedChanges": "Thay đổi chưa được lưu", - "dismiss": "Miễn nhiệm", - "loading": "Đang tải...", - "optional": "Không bắt buộc", - "connect": "Kết nối", - "copied": "Đã sao chép", - "connecting": "Đang kết nối...", - "updateAvailable": "Đã có bản cập nhật", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "Mở trong tab mới", - "noReleases": "Không có bản phát hành nào", - "updatesAndReleases": "Cập nhật & Phát hành", - "newVersionAvailable": "Phiên bản mới ({{version}}) đã có sẵn.", - "failedToFetchUpdateInfo": "Không thể tải thông tin cập nhật.", - "preRelease": "Bản phát hành trước", - "noReleasesFound": "Không tìm thấy bản phát hành nào.", - "cancel": "Hủy bỏ", - "username": "Tên người dùng", - "login": "Đăng nhập", - "register": "Đăng ký", - "password": "Mật khẩu", - "confirmPassword": "Xác nhận mật khẩu", - "back": "Mặt sau", - "save": "Cứu", - "saving": "Đang lưu...", - "delete": "Xóa bỏ", - "rename": "Đổi tên", - "edit": "Biên tập", - "add": "Thêm vào", - "confirm": "Xác nhận", - "no": "KHÔNG", - "or": "HOẶC", - "next": "Kế tiếp", - "previous": "Trước", - "refresh": "Làm cho khỏe lại", - "language": "Ngôn ngữ", - "checking": "Đang kiểm tra...", - "checkingDatabase": "Kiểm tra kết nối cơ sở dữ liệu...", - "checkingAuthentication": "Đang kiểm tra xác thực...", - "backendReconnected": "Kết nối máy chủ đã được khôi phục.", - "connectionDegraded": "Mất kết nối máy chủ, đang khôi phục…", - "reload": "Tải lại", - "remove": "Di dời", - "create": "Tạo nên", - "update": "Cập nhật", - "copy": "Sao chép", - "copyFailed": "Không thể sao chép vào clipboard", - "maximize": "Tối đa hóa", - "restore": "Khôi phục", - "of": "của" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "Trang chủ", - "terminal": "Phần cuối", + "home": "Home", + "terminal": "Terminal", "docker": "Docker", - "tunnels": "Đường hầm", - "fileManager": "Trình quản lý tập tin", - "serverStats": "Thống kê máy chủ", - "admin": "Quản trị viên", - "userProfile": "Hồ sơ người dùng", - "splitScreen": "Chia màn hình", - "confirmClose": "Đóng phiên làm việc hiện tại này?", - "close": "Đóng", - "cancel": "Hủy bỏ", - "sshManager": "Trình quản lý SSH", - "cannotSplitTab": "Không thể chia tab này", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "Sao chép mật khẩu", - "copySudoPassword": "Sao chép mật khẩu Sudo", - "passwordCopied": "Mật khẩu đã được sao chép vào clipboard", - "noPasswordAvailable": "Không có mật khẩu nào khả dụng", - "failedToCopyPassword": "Không thể sao chép mật khẩu", - "refreshTab": "Làm mới kết nối", - "openFileManager": "Mở Trình quản lý tệp", - "dashboard": "Bảng điều khiển", - "networkGraph": "Đồ thị mạng", - "quickConnect": "Kết nối nhanh", - "sshTools": "Công cụ SSH", - "history": "Lịch sử", - "hosts": "Người dẫn chương trình", - "snippets": "Những đoạn trích", - "hostManager": "Quản lý máy chủ", - "credentials": "Thông tin xác thực", - "connections": "Kết nối", - "roleAdministrator": "Quản trị viên", - "roleUser": "Người dùng" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "Người dẫn chương trình", - "noHosts": "Không có máy chủ SSH", - "retry": "Thử lại", - "refresh": "Làm cho khỏe lại", - "optional": "Không bắt buộc", - "downloadSample": "Tải xuống mẫu", - "failedToDeleteHost": "Không thể xóa {{name}}", - "importSkipExisting": "Nhập khẩu (bỏ qua mục hiện có)", - "connectionDetails": "Chi tiết kết nối", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "Máy tính từ xa", - "port": "Cảng", - "username": "Tên người dùng", - "folder": "Thư mục", - "tags": "Thẻ", - "pin": "Ghim", - "addHost": "Thêm máy chủ", - "editHost": "Chỉnh sửa máy chủ", - "cloneHost": "Máy chủ nhân bản", - "enableTerminal": "Bật thiết bị đầu cuối", - "enableTunnel": "Kích hoạt đường hầm", - "enableFileManager": "Kích hoạt Trình quản lý tệp", - "enableDocker": "Bật Docker", - "defaultPath": "Đường dẫn mặc định", - "connection": "Sự liên quan", - "upload": "Tải lên", - "authentication": "Xác thực", - "password": "Mật khẩu", - "key": "Chìa khóa", - "credential": "Chứng chỉ", - "none": "Không có", - "sshPrivateKey": "Khóa riêng SSH", - "keyType": "Loại khóa", - "uploadFile": "Tải lên tệp", - "tabGeneral": "Tổng quan", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "Đường hầm", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "Tệp", - "tabStats": "Thống kê", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "Chia sẻ", - "tabAuthentication": "Xác thực", - "terminal": "Phần cuối", - "tunnel": "Đường hầm", - "fileManager": "Trình quản lý tập tin", - "serverStats": "Thống kê máy chủ", - "status": "Trạng thái", - "folderRenamed": "Thư mục \"{{oldName}}\" đã được đổi tên thành \"{{newName}}\" thành công", - "failedToRenameFolder": "Không thể đổi tên thư mục.", - "movedToFolder": "Đã chuyển đến \"{{folder}}\"", - "editHostTooltip": "Chỉnh sửa máy chủ", - "statusChecks": "Kiểm tra trạng thái", - "metricsCollection": "Thu thập số liệu", - "metricsInterval": "Khoảng thời gian thu thập số liệu", - "metricsIntervalDesc": "Tần suất thu thập số liệu thống kê máy chủ (5 giây - 1 giờ)", - "behavior": "Hành vi", - "themePreview": "Xem trước giao diện", - "theme": "Chủ đề", - "fontFamily": "Họ phông chữ", - "fontSize": "Kích thước phông chữ", - "letterSpacing": "Khoảng cách giữa các chữ", - "lineHeight": "Chiều cao dòng", - "cursorStyle": "Kiểu con trỏ", - "cursorBlink": "Con trỏ nhấp nháy", - "scrollbackBuffer": "Bộ đệm cuộn ngược", - "bellStyle": "Kiểu chuông", - "rightClickSelectsWord": "Nhấp chuột phải chọn Word", - "fastScrollModifier": "Bộ điều chỉnh cuộn nhanh", - "fastScrollSensitivity": "Độ nhạy cuộn nhanh", - "sshAgentForwarding": "Chuyển tiếp tác nhân SSH", - "backspaceMode": "Chế độ xóa lùi", - "startupSnippet": "Đoạn mã khởi nghiệp", - "selectSnippet": "Chọn đoạn trích", - "forceKeyboardInteractive": "Buộc tương tác bàn phím", - "overrideCredentialUsername": "Ghi đè tên người dùng xác thực", - "overrideCredentialUsernameDesc": "Hãy sử dụng tên người dùng được chỉ định ở trên thay vì tên người dùng của thông tin đăng nhập.", - "jumpHostChain": "Chuỗi máy chủ nhảy", - "portKnocking": "Tiếng gõ cửa cảng", - "addKnock": "Thêm cổng", - "addProxyNode": "Thêm nút", - "proxyNode": "Nút ủy quyền", - "proxyType": "Loại Proxy", - "quickActions": "Thao tác nhanh", - "sudoPasswordAutoFill": "Tự động điền mật khẩu Sudo", - "sudoPassword": "Mật khẩu Sudo", - "keepaliveInterval": "Khoảng thời gian duy trì kết nối (ms)", - "moshCommand": "Bộ chỉ huy MOSH", - "environmentVariables": "Biến môi trường", - "addVariable": "Thêm biến", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "Sao chép URL thiết bị đầu cuối", - "copyFileManagerUrl": "Sao chép URL Trình quản lý tập tin", - "copyRemoteDesktopUrl": "Sao chép URL máy tính từ xa", - "failedToConnect": "Không thể kết nối với bảng điều khiển.", - "connect": "Kết nối", - "disconnect": "Ngắt kết nối", - "start": "Bắt đầu", - "enableStatusCheck": "Bật kiểm tra trạng thái", - "enableMetrics": "Kích hoạt số liệu", - "bulkUpdateFailed": "Cập nhật hàng loạt thất bại", - "selectAll": "Chọn tất cả", - "deselectAll": "Bỏ chọn tất cả", - "protocols": "Giao thức", - "secureShell": "Vỏ bảo mật", - "virtualNetwork": "Mạng ảo", - "unencryptedShell": "Vỏ không mã hóa", - "addressIp": "Địa chỉ / IP", - "friendlyName": "Tên thân thiện", - "folderAndAdvanced": "Thư mục & Nâng cao", - "privateNotes": "Ghi chú riêng", - "privateNotesPlaceholder": "Thông tin chi tiết về máy chủ này...", - "pinToTop": "Ghim lên đầu trang", - "pinToTopDesc": "Luôn hiển thị máy chủ này ở đầu danh sách.", - "portKnockingSequence": "Trình tự gõ cửa cảng", - "addKnockBtn": "Thêm Knock", - "noPortKnocking": "Không có cấu hình nào cho việc gõ cổng.", - "knockPort": "Cổng gõ", - "protocol": "Giao thức", - "delayAfterMs": "Độ trễ sau (ms)", - "useSocks5Proxy": "Sử dụng proxy SOCKS5", - "useSocks5ProxyDesc": "Định tuyến kết nối thông qua máy chủ proxy", - "proxyHost": "Máy chủ Proxy", - "proxyPort": "Cổng Proxy", - "proxyUsername": "Tên người dùng ủy quyền", - "proxyPassword": "Mật khẩu Proxy", - "proxySingleMode": "Máy chủ proxy đơn", - "proxyChainMode": "Chuỗi ủy quyền", - "you": "Bạn", - "jumpHostChainLabel": "Chuỗi máy chủ nhảy", - "addJumpBtn": "Thêm Nhảy", - "noJumpHosts": "Chưa cấu hình máy chủ trung gian nào.", - "selectAServer": "Chọn máy chủ...", - "sshPort": "Cổng SSH", - "authMethod": "Phương thức xác thực", - "storedCredential": "Thông tin đăng nhập đã lưu", - "selectACredential": "Chọn thông tin xác thực...", - "keyTypeLabel": "Loại khóa", - "keyTypeAuto": "Tự động phát hiện", - "keyPasteTab": "Dán", - "keyUploadTab": "Tải lên", - "keyFileLoaded": "Tệp khóa đã được tải", - "keyUploadClick": "Nhấp vào đây để tải lên tệp .pem / .key / .ppk", - "clearKey": "Xóa khóa", - "keySaved": "Khóa SSH đã được lưu", - "keyReplaceNotice": "Dán khóa mới bên dưới để thay thế khóa cũ.", - "keyPassphraseSaved": "Mật khẩu đã được lưu, nhập để thay đổi", - "replaceKey": "Thay thế chìa khóa", - "forceKeyboardInteractiveLabel": "Tương tác bàn phím cưỡng chế", - "forceKeyboardInteractiveShortDesc": "Buộc nhập mật khẩu thủ công ngay cả khi đã có chìa khóa.", - "terminalAppearance": "Hình thức đầu cuối", - "colorTheme": "Chủ đề màu sắc", - "fontFamilyLabel": "Họ phông chữ", - "fontSizeLabel": "Kích thước phông chữ", - "cursorStyleLabel": "Kiểu con trỏ", - "letterSpacingPx": "Khoảng cách giữa các chữ cái (px)", - "lineHeightLabel": "Chiều cao dòng", - "bellStyleLabel": "Kiểu chuông", - "backspaceModeLabel": "Chế độ xóa lùi", - "cursorBlinking": "Con trỏ nhấp nháy", - "cursorBlinkingDesc": "Bật hiệu ứng nhấp nháy cho con trỏ trong cửa sổ dòng lệnh.", - "rightClickSelectsWordLabel": "Nhấp chuột phải Chọn Word", - "rightClickSelectsWordShortDesc": "Chọn từ nằm dưới con trỏ chuột bằng cách nhấp chuột phải.", - "behaviorAndAdvanced": "Hành vi & Nâng cao", - "scrollbackBufferLabel": "Bộ đệm cuộn ngược", - "scrollbackMaxLines": "Số dòng tối đa được lưu trữ trong lịch sử", - "sshAgentForwardingLabel": "Chuyển tiếp tác nhân SSH", - "sshAgentForwardingShortDesc": "Hãy chuyển khóa SSH cục bộ của bạn cho máy chủ này.", - "enableAutoMosh": "Bật Auto-Mosh", - "enableAutoMoshDesc": "Nên ưu tiên sử dụng Mosh hơn SSH nếu có sẵn.", - "enableAutoTmux": "Bật Auto-Tmux", - "enableAutoTmuxDesc": "Tự động khởi chạy hoặc kết nối với phiên tmux.", - "sudoPasswordAutoFillLabel": "Tự động điền mật khẩu Sudo", - "sudoPasswordAutoFillShortDesc": "Tự động cung cấp mật khẩu sudo khi được yêu cầu.", - "sudoPasswordLabel": "Mật khẩu Sudo", - "environmentVariablesLabel": "Biến môi trường", - "addVariableBtn": "Thêm biến", - "noEnvVars": "Chưa có biến môi trường nào được cấu hình.", - "fastScrollModifierLabel": "Bộ điều chỉnh cuộn nhanh", - "fastScrollSensitivityLabel": "Độ nhạy cuộn nhanh", - "moshCommandLabel": "Bộ chỉ huy Mosh", - "startupSnippetLabel": "Đoạn mã khởi nghiệp", - "keepaliveIntervalLabel": "Khoảng thời gian duy trì kết nối (giây)", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", "maxKeepaliveMisses": "Max Keepalive Misses", - "tunnelSettings": "Cài đặt đường hầm", - "enableTunneling": "Bật tính năng tạo đường hầm", - "enableTunnelingDesc": "Kích hoạt chức năng đường hầm SSH cho máy chủ này.", - "serverTunnelsSection": "Đường hầm máy chủ", - "addTunnelBtn": "Thêm đường hầm", - "noTunnelsConfigured": "Chưa có đường hầm nào được cấu hình.", - "tunnelLabel": "Đường hầm {{number}}", - "tunnelType": "Loại đường hầm", - "tunnelModeLocalDesc": "Chuyển tiếp cổng cục bộ đến một cổng trên máy chủ từ xa (hoặc một máy chủ có thể truy cập được từ đó).", - "tunnelModeRemoteDesc": "Chuyển tiếp cổng trên máy chủ từ xa về cổng cục bộ trên máy tính của bạn.", - "tunnelModeDynamicDesc": "Tạo một máy chủ proxy SOCKS5 trên một cổng cục bộ để chuyển tiếp cổng động.", - "sameHost": "Máy chủ này (đường hầm trực tiếp)", - "endpointHost": "Máy chủ điểm cuối", - "endpointPort": "Cổng điểm cuối", - "bindHost": "Liên kết máy chủ", - "sourcePort": "Cổng nguồn", - "maxRetries": "Số lần thử lại tối đa", - "retryIntervalS": "Khoảng thời gian thử lại (giây)", - "autoStartLabel": "Tự động khởi động", - "autoStartDesc": "Tự động kết nối đường hầm này khi máy chủ được tải.", - "tunnelConnecting": "Đường hầm kết nối...", - "tunnelDisconnected": "Đường hầm bị ngắt kết nối", - "failedToConnectTunnel": "Không thể kết nối", - "failedToDisconnectTunnel": "Không thể ngắt kết nối", - "dockerIntegration": "Tích hợp Docker", - "enableDockerMonitor": "Bật Docker", - "enableDockerMonitorDesc": "Theo dõi và quản lý các container trên máy chủ này thông qua Docker.", - "enableFileManagerMonitor": "Kích hoạt Trình quản lý tệp", - "enableFileManagerMonitorDesc": "Duyệt và quản lý các tập tin trên máy chủ này qua SFTP.", - "defaultPathLabel": "Đường dẫn mặc định", - "fileManagerPathHint": "Thư mục cần mở khi trình quản lý tập tin khởi chạy cho máy chủ này.", - "statusChecksLabel": "Kiểm tra trạng thái", - "enableStatusChecks": "Bật kiểm tra trạng thái", - "enableStatusChecksDesc": "Thường xuyên kiểm tra trạng thái hoạt động của máy chủ này bằng lệnh ping.", - "useGlobalInterval": "Sử dụng khoảng thời gian toàn cầu", - "useGlobalIntervalDesc": "Ghi đè bằng khoảng thời gian kiểm tra trạng thái toàn máy chủ", - "checkIntervalS": "Khoảng thời gian kiểm tra", - "checkIntervalDesc": "Khoảng thời gian (giây) giữa mỗi lần ping kết nối", - "metricsCollectionLabel": "Thu thập số liệu", - "enableMetricsLabel": "Kích hoạt số liệu", - "enableMetricsDesc": "Thu thập thông tin về mức sử dụng CPU, RAM, ổ đĩa và mạng từ máy chủ này.", - "useGlobalMetrics": "Sử dụng khoảng thời gian toàn cầu", - "useGlobalMetricsDesc": "Ghi đè bằng khoảng thời gian đo lường toàn máy chủ", - "metricsIntervalS": "Khoảng thời gian đo lường (s)", - "metricsIntervalDesc2": "Khoảng thời gian (giây) giữa các lần chụp ảnh chỉ số.", - "visibleWidgets": "Các tiện ích hiển thị", - "cpuUsageLabel": "Mức sử dụng CPU", - "cpuUsageDesc": "Phần trăm CPU, mức tải trung bình, biểu đồ sparkline", - "memoryLabel": "Mức sử dụng bộ nhớ", - "memoryDesc": "Mức sử dụng RAM, bộ nhớ ảo, bộ nhớ đệm", - "storageLabel": "Mức sử dụng ổ đĩa", - "storageDesc": "Mức sử dụng ổ đĩa trên mỗi điểm gắn kết", - "networkLabel": "Giao diện mạng", - "networkDesc": "Danh sách giao diện và băng thông", - "uptimeLabel": "Thời gian hoạt động", - "uptimeDesc": "thời gian hoạt động của hệ thống và thời gian khởi động", - "systemInfoLabel": "Thông tin hệ thống", - "systemInfoDesc": "Hệ điều hành, nhân hệ điều hành, tên máy chủ, kiến trúc", - "recentLoginsLabel": "Đăng nhập gần đây", - "recentLoginsDesc": "Sự kiện đăng nhập thành công và thất bại", - "topProcessesLabel": "Các quy trình hàng đầu", - "topProcessesDesc": "PID, CPU%, MEM%, lệnh", - "listeningPortsLabel": "Cổng nghe", - "listeningPortsDesc": "Mở các cổng với quy trình và trạng thái", - "firewallLabel": "Tường lửa", - "firewallDesc": "Trạng thái tường lửa, AppArmor, SELinux", - "quickActionsLabel": "Thao tác nhanh", - "quickActionsToolbar": "Các thao tác nhanh được hiển thị dưới dạng nút trên thanh công cụ Thống kê máy chủ để thực thi lệnh chỉ bằng một cú nhấp chuột.", - "noQuickActions": "Chưa có hành động nhanh chóng nào.", - "buttonLabel": "Nhãn nút", - "selectSnippetPlaceholder": "Chọn đoạn mã...", - "addActionBtn": "Thêm hành động", - "hostSharedSuccessfully": "Máy chủ đã chia sẻ thành công", - "failedToShareHost": "Không thể chia sẻ máy chủ", - "accessRevoked": "Quyền truy cập đã bị thu hồi", - "failedToRevokeAccess": "Không thể thu hồi quyền truy cập", - "cancelBtn": "Hủy bỏ", - "savingBtn": "Đang lưu...", - "addHostBtn": "Thêm máy chủ", - "hostUpdated": "Máy chủ đã cập nhật", - "hostCreated": "Máy chủ đã được tạo", - "failedToSave": "Không thể lưu máy chủ", - "credentialUpdated": "Thông tin xác thực đã được cập nhật", - "credentialCreated": "Thông tin xác thực đã được tạo", - "failedToSaveCredential": "Không thể lưu thông tin đăng nhập", - "backToHosts": "Trở lại trang Chủ nhà", - "backToCredentials": "Trở lại trang Thông tin đăng nhập", - "pinned": "Đã ghim", - "noHostsFound": "Không tìm thấy máy chủ nào", - "tryDifferentTerm": "Hãy thử một thuật ngữ khác.", - "addFirstHost": "Thêm máy chủ đầu tiên của bạn để bắt đầu", - "noCredentialsFound": "Không tìm thấy thông tin đăng nhập.", - "addCredentialBtn": "Thêm thông tin đăng nhập", - "updateCredentialBtn": "Cập nhật thông tin đăng nhập", - "features": "Đặc trưng", - "noFolder": "(Không có thư mục)", - "deleteSelected": "Xóa bỏ", - "exitSelection": "Lựa chọn thoát", - "importSkip": "Nhập khẩu (bỏ qua mục hiện có)", - "importOverwrite": "Nhập (ghi đè)", - "collapseBtn": "Sụp đổ", - "importExportBtn": "Nhập khẩu / Xuất khẩu", - "hostStatusesRefreshed": "Trạng thái máy chủ đã được cập nhật", - "failedToRefreshHosts": "Không thể làm mới máy chủ", - "movedHostTo": "Đã chuyển {{host}} sang \"{{folder}}\"", - "failedToMoveHost": "Không thể di chuyển máy chủ", - "folderRenamedTo": "Thư mục được đổi tên thành \"{{name}}\"", - "deletedFolder": "Đã xóa thư mục \"{{name}}\"", - "failedToDeleteFolder": "Không thể xóa thư mục", - "deleteAllInFolder": "Xóa tất cả máy chủ trong \"{{name}}\"? Thao tác này không thể hoàn tác.", - "deletedHost": "Đã xóa {{name}}", - "copiedToClipboard": "Đã sao chép vào clipboard", - "terminalUrlCopied": "URL thiết bị đầu cuối đã được sao chép", - "fileManagerUrlCopied": "URL Trình quản lý tệp đã được sao chép", - "tunnelUrlCopied": "URL đường hầm đã được sao chép", - "dockerUrlCopied": "URL Docker đã được sao chép", - "serverStatsUrlCopied": "URL Thống kê máy chủ đã được sao chép", - "rdpUrlCopied": "URL RDP đã được sao chép", - "vncUrlCopied": "URL VNC đã được sao chép", - "telnetUrlCopied": "URL Telnet đã được sao chép", - "remoteDesktopUrlCopied": "URL máy tính từ xa đã được sao chép", - "expandActions": "Mở rộng hành động", - "collapseActions": "Hành động thu gọn", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", "wakeOnLanAction": "Wake on LAN", - "wakeOnLanSuccess": "Gói ma thuật đã được gửi đến {{name}}", - "wakeOnLanError": "Không thể gửi gói tin ma thuật.", - "cloneHostAction": "Máy chủ nhân bản", - "copyAddress": "Sao chép địa chỉ", - "copyLink": "Sao chép liên kết", - "copyTerminalUrlAction": "Sao chép URL thiết bị đầu cuối", - "copyFileManagerUrlAction": "Sao chép URL Trình quản lý tập tin", - "copyTunnelUrlAction": "Sao chép URL đường hầm", - "copyDockerUrlAction": "Sao chép URL Docker", - "copyServerStatsUrlAction": "Sao chép URL Thống kê Máy chủ", - "copyRdpUrlAction": "Sao chép URL RDP", - "copyVncUrlAction": "Sao chép URL VNC", - "copyTelnetUrlAction": "Sao chép URL Telnet", - "copyRemoteDesktopUrlAction": "Sao chép URL máy tính từ xa", - "deleteCredentialConfirm": "Xóa thông tin xác thực \"{{name}}\"?", - "deletedCredential": "Đã xóa {{name}}", - "deploySSHKeyTitle": "Triển khai khóa SSH", - "deployingBtn": "Đang triển khai...", - "deployBtn": "Triển khai", - "failedToDeployKey": "Không thể triển khai khóa.", - "deleteHostsConfirm": "Xóa {{count}} máy chủ{{plural}}? Thao tác này không thể hoàn tác.", - "movedToRoot": "Đã chuyển đến thư mục gốc", - "failedToMoveHosts": "Không thể di chuyển máy chủ", - "enableTerminalFeature": "Bật thiết bị đầu cuối", - "disableTerminalFeature": "Vô hiệu hóa thiết bị đầu cuối", - "enableFilesFeature": "Cho phép tệp", - "disableFilesFeature": "Vô hiệu hóa các tệp", - "enableTunnelsFeature": "Kích hoạt đường hầm", - "disableTunnelsFeature": "Vô hiệu hóa đường hầm", - "enableDockerFeature": "Bật Docker", - "disableDockerFeature": "Vô hiệu hóa Docker", - "addTagsPlaceholder": "Thêm thẻ...", - "authDetails": "Thông tin xác thực", - "credType": "Kiểu", - "generateKeyPairDesc": "Tạo một cặp khóa mới, cả khóa riêng tư và khóa công khai sẽ được điền tự động.", - "generatingKey": "Đang tạo...", - "generateLabel": "Tạo {{label}}", - "uploadFileBtn": "Tải lên tệp", - "keyPassphraseOptional": "Mật khẩu khóa (Tùy chọn)", - "sshPublicKeyOptional": "Khóa công khai SSH (Tùy chọn)", - "publicKeyGenerated": "Khóa công khai được tạo", - "failedToGeneratePublicKey": "Không thể lấy được khóa công khai", - "publicKeyCopied": "Khóa công khai đã được sao chép", - "keyPairGenerated": "{{label}} cặp khóa được tạo", - "failedToGenerateKeyPair": "Không thể tạo cặp khóa", - "searchHostsPlaceholder": "Tìm kiếm máy chủ, địa chỉ, thẻ…", - "searchCredentialsPlaceholder": "Thông tin đăng nhập tìm kiếm…", - "refreshBtn": "Làm cho khỏe lại", - "addTag": "Thêm thẻ...", - "deleteConfirmBtn": "Xóa bỏ", - "tunnelRequirementsText": "Máy chủ SSH phải có các thiết lập GatewayPorts yes, AllowTcpForwarding yes và PermitRootLogin yes trong tệp /etc/ssh/sshd_config.", - "deleteHostConfirm": "Xóa \"{{name}}\"?", - "enableAtLeastOneProtocol": "Hãy bật ít nhất một giao thức ở trên để cấu hình cài đặt xác thực và kết nối.", - "keyPassphrase": "Mật khẩu chính", - "connectBtn": "Kết nối", - "disconnectBtn": "Ngắt kết nối", - "basicInformation": "Thông tin cơ bản", - "authDetailsSection": "Thông tin xác thực", - "credTypeLabel": "Kiểu", - "hostsTab": "Người dẫn chương trình", - "credentialsTab": "Thông tin xác thực", - "selectMultiple": "Chọn nhiều", - "selectHosts": "Chọn máy chủ", - "connectionLabel": "Sự liên quan", - "authenticationLabel": "Xác thực", - "generateKeyPairTitle": "Tạo cặp khóa", - "generateKeyPairDescription": "Tạo một cặp khóa mới, cả khóa riêng tư và khóa công khai sẽ được điền tự động.", - "generateFromPrivateKey": "Tạo từ khóa riêng", - "refreshBtn2": "Làm cho khỏe lại", - "exitSelectionTitle": "Lựa chọn thoát", - "exportAll": "Xuất tất cả", - "addHostBtn2": "Thêm máy chủ", - "addCredentialBtn2": "Thêm thông tin đăng nhập", - "checkingHostStatuses": "Kiểm tra trạng thái máy chủ...", - "pinnedSection": "Đã ghim", - "hostsExported": "Máy chủ đã được xuất khẩu thành công", - "exportFailed": "Không thể xuất máy chủ", - "sampleDownloaded": "Tệp mẫu đã được tải xuống", - "failedToDeleteCredential2": "Không thể xóa thông tin đăng nhập", - "noFolderOption": "(Không có thư mục)", - "nSelected": "{{count}} đã chọn", - "featuresMenu": "Đặc trưng", - "moveMenu": "Di chuyển", - "cancelSelection": "Hủy bỏ", - "deployDialogDesc": "Triển khai {{name}} vào authorized_keys của máy chủ.", - "targetHostLabel": "Máy chủ mục tiêu", - "selectHostOption": "Chọn máy chủ...", - "keyDeployedSuccess": "Khóa đã được triển khai thành công", - "failedToDeployKey2": "Không thể triển khai khóa.", - "deletedCount": "Đã xóa {{count}} máy chủ", - "failedToDeleteCount": "Không thể xóa {{count}} máy chủ", - "duplicatedHost": "Đã sao chép \"{{name}}\"", - "failedToDuplicateHost": "Không thể sao chép máy chủ", - "updatedCount": "Đã cập nhật {{count}} máy chủ", - "friendlyNameLabel": "Tên thân thiện", - "descriptionLabel": "Sự miêu tả", - "loadingHost": "Đang tải máy chủ...", - "loadingHosts": "Đang tải máy chủ...", - "loadingCredentials": "Đang tải thông tin đăng nhập...", - "noHostsYet": "Chưa có máy chủ nào", - "noHostsMatchSearch": "Không có máy chủ nào phù hợp với tìm kiếm của bạn", - "hostNotFound": "Không tìm thấy máy chủ", - "searchHosts": "Tìm kiếm máy chủ...", - "sortHosts": "Sắp xếp máy chủ", - "sortDefault": "Thứ tự mặc định", - "sortNameAsc": "Tên (A → Z)", - "sortNameDesc": "Tên (Z → A)", - "sortIpAsc": "Địa chỉ IP (Tăng dần)", - "sortIpDesc": "Địa chỉ IP (Mô tả)", - "sortOnlineFirst": "Ưu tiên trực tuyến", - "sortOfflineFirst": "Ưu tiên ngoại tuyến", - "sortPinnedFirst": "Được ghim đầu tiên", - "filterHosts": "Lọc máy chủ", - "filterClearAll": "Xóa bộ lọc", - "filterStatusGroup": "Trạng thái", - "filterOnline": "Trực tuyến", - "filterOffline": "Ngoại tuyến", - "filterPinned": "Đã ghim", - "filterAuthGroup": "Loại xác thực", - "filterAuthPassword": "Mật khẩu", - "filterAuthKey": "Khóa SSH", - "filterAuthCredential": "Chứng chỉ", - "filterAuthNone": "Không có", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", + "keyPairGenerated": "{{label}} key pair generated", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "Giao thức", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "Đặc trưng", - "filterFeatureTerminal": "Phần cuối", - "filterFeatureFileManager": "Trình quản lý tập tin", - "filterFeatureTunnel": "Đường hầm", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "Thẻ", - "shareHost": "Chia sẻ máy chủ", - "shareHostTitle": "Chia sẻ: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "Máy chủ này phải sử dụng thông tin xác thực để cho phép chia sẻ. Hãy chỉnh sửa máy chủ và gán thông tin xác thực trước." + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "Sự liên quan", - "authentication": "Xác thực", - "connectionSettings": "Cài đặt kết nối", - "displaySettings": "Cài đặt hiển thị", - "audioSettings": "Cài đặt âm thanh", - "rdpPerformance": "Hiệu suất RDP", - "deviceRedirection": "Chuyển hướng thiết bị", - "session": "Phiên họp", - "gateway": "Cổng", - "remoteApp": "Ứng dụng từ xa", - "clipboard": "Bảng kẹp giấy", - "sessionRecording": "Ghi âm buổi ghi âm", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", "wakeOnLan": "Wake-on-LAN", - "vncSettings": "Cài đặt VNC", - "terminalSettings": "Cài đặt thiết bị đầu cuối", - "rdpPort": "Cảng RDP", - "username": "Tên người dùng", - "password": "Mật khẩu", - "domain": "Lãnh địa", - "securityMode": "Chế độ bảo mật", - "colorDepth": "Độ sâu màu", - "width": "Chiều rộng", - "height": "Chiều cao", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", "dpi": "DPI", - "resizeMethod": "Phương pháp thay đổi kích thước", - "clientName": "Tên khách hàng", - "initialProgram": "Chương trình ban đầu", - "serverLayout": "Bố cục máy chủ", - "timezone": "Múi giờ", - "gatewayHostname": "Tên máy chủ cổng", - "gatewayPort": "Cổng Cổng", - "gatewayUsername": "Tên người dùng cổng", - "gatewayPassword": "Mật khẩu cổng", - "gatewayDomain": "Tên miền cổng", - "remoteAppProgram": "Chương trình RemoteApp", - "workingDirectory": "Thư mục làm việc", - "arguments": "Lập luận", - "normalizeLineEndings": "Chuẩn hóa ký tự xuống dòng", - "recordingPath": "Đường dẫn ghi", - "recordingName": "Tên bản ghi", - "macAddress": "Địa chỉ MAC", - "broadcastAddress": "Địa chỉ phát sóng", - "udpPort": "Cổng UDP", - "waitTimeS": "Thời gian chờ (giây)", - "driveName": "Tên ổ đĩa", - "drivePath": "Đường lái xe", - "ignoreCertificate": "Bỏ qua chứng chỉ", - "ignoreCertificateDesc": "Cho phép kết nối đến các máy chủ có chứng chỉ tự ký.", - "forceLossless": "Lực không mất mát", - "forceLosslessDesc": "Buộc mã hóa hình ảnh không mất dữ liệu (chất lượng cao hơn, băng thông lớn hơn)", - "disableAudio": "Tắt âm thanh", - "disableAudioDesc": "Tắt tiếng toàn bộ âm thanh từ phiên làm việc từ xa.", - "enableAudioInput": "Bật đầu vào âm thanh (Microphone)", - "enableAudioInputDesc": "Chuyển tiếp micro cục bộ đến phiên làm việc từ xa", - "wallpaper": "Hình nền", - "wallpaperDesc": "Hiển thị hình nền máy tính (tắt chức năng này sẽ cải thiện hiệu năng)", - "theming": "Chủ đề", - "themingDesc": "Cho phép sử dụng các chủ đề và kiểu dáng trực quan.", - "fontSmoothing": "Làm mịn phông chữ", - "fontSmoothingDesc": "Bật hiển thị phông chữ ClearType", - "fullWindowDrag": "Kéo toàn màn hình", - "fullWindowDragDesc": "Hiển thị nội dung cửa sổ trong khi kéo", - "desktopComposition": "Bố cục máy tính để bàn", - "desktopCompositionDesc": "Kích hoạt hiệu ứng kính Aero", - "menuAnimations": "Hoạt ảnh menu", - "menuAnimationsDesc": "Bật hiệu ứng chuyển động mờ dần và trượt của menu", - "disableBitmapCaching": "Tắt tính năng lưu trữ bộ nhớ đệm ảnh bitmap", - "disableBitmapCachingDesc": "Tắt bộ nhớ đệm bitmap (có thể giúp khắc phục lỗi)", - "disableOffscreenCaching": "Tắt tính năng lưu bộ nhớ đệm khi tắt màn hình", - "disableOffscreenCachingDesc": "Tắt bộ nhớ đệm ngoại tuyến", - "disableGlyphCaching": "Tắt tính năng lưu trữ ký tự", - "disableGlyphCachingDesc": "Tắt bộ nhớ đệm biểu tượng", - "enableGfx": "Bật GFX", - "enableGfxDesc": "Sử dụng quy trình xử lý đồ họa RemoteFX", - "enablePrinting": "Bật tính năng in", - "enablePrintingDesc": "Chuyển hướng máy in cục bộ sang phiên làm việc từ xa.", - "enableDriveRedirection": "Bật tính năng chuyển hướng ổ đĩa", - "enableDriveRedirectionDesc": "Ánh xạ thư mục cục bộ thành ổ đĩa trong phiên làm việc từ xa.", - "createDrivePath": "Tạo đường dẫn ổ đĩa", - "createDrivePathDesc": "Tự động tạo thư mục nếu thư mục đó chưa tồn tại.", - "disableDownload": "Vô hiệu hóa Tải xuống", - "disableDownloadDesc": "Ngăn chặn việc tải xuống tập tin từ phiên làm việc từ xa", - "disableUpload": "Tắt tính năng tải lên", - "disableUploadDesc": "Ngăn chặn việc tải tệp lên phiên làm việc từ xa", - "enableTouch": "Bật cảm ứng", - "enableTouchDesc": "Bật tính năng chuyển tiếp dữ liệu cảm ứng", - "consoleSession": "Phiên điều khiển", - "consoleSessionDesc": "Kết nối với bảng điều khiển (phiên 0) thay vì tạo một phiên mới.", - "sendWolPacket": "Gửi gói WOL", - "sendWolPacketDesc": "Gửi gói tin đặc biệt để đánh thức máy chủ này trước khi kết nối.", - "disableCopy": "Vô hiệu hóa sao chép", - "disableCopyDesc": "Ngăn chặn việc sao chép văn bản từ phiên làm việc từ xa.", - "disablePaste": "Vô hiệu hóa Paste", - "disablePasteDesc": "Ngăn chặn việc dán văn bản vào phiên làm việc từ xa.", - "createPathIfMissing": "Tạo đường dẫn nếu chưa có", - "createPathIfMissingDesc": "Tự động tạo thư mục ghi âm", - "excludeOutput": "Loại trừ đầu ra", - "excludeOutputDesc": "Không ghi lại nội dung hiển thị trên màn hình (chỉ ghi lại siêu dữ liệu).", - "excludeMouse": "Loại trừ chuột", - "excludeMouseDesc": "Không ghi lại chuyển động chuột", - "includeKeystrokes": "Bao gồm các lần nhấn phím", - "includeKeystrokesDesc": "Ghi lại cả các thao tác gõ phím thô cùng với thông tin hiển thị trên màn hình.", - "vncPort": "Cổng VNC", - "vncPassword": "Mật khẩu VNC", - "vncUsernameOptional": "Tên người dùng (tùy chọn)", - "vncLeaveBlank": "Để trống nếu không cần thiết.", - "cursorMode": "Chế độ con trỏ", - "swapRedBlue": "Đổi màu Đỏ/Xanh", - "swapRedBlueDesc": "Hoán đổi kênh màu đỏ và xanh lam (khắc phục một số vấn đề về màu sắc)", - "readOnly": "Chỉ đọc", - "readOnlyDesc": "Xem màn hình từ xa mà không cần gửi bất kỳ tín hiệu đầu vào nào.", - "telnetPort": "Cổng Telnet", - "terminalType": "Loại thiết bị đầu cuối", - "fontName": "Tên phông chữ", - "fontSize": "Kích thước phông chữ", - "colorScheme": "Bảng màu", - "backspaceKey": "Phím xóa", - "saveHostFirst": "Hãy lưu máy chủ trước.", - "sharingOptionsAfterSave": "Các tùy chọn chia sẻ sẽ khả dụng sau khi máy chủ đã được lưu.", - "sharingLoadError": "Không thể tải dữ liệu chia sẻ. Vui lòng kiểm tra kết nối và thử lại.", - "shareHostSection": "Chia sẻ máy chủ", - "shareWithUser": "Chia sẻ với người dùng", - "shareWithRole": "Chia sẻ với vai trò", - "selectUser": "Chọn người dùng", - "selectRole": "Chọn vai trò", - "selectUserOption": "Chọn người dùng...", - "selectRoleOption": "Chọn một vai trò...", - "permissionLevel": "Cấp độ quyền hạn", - "expiresInHours": "Hết hạn sau (giờ)", - "noExpiryPlaceholder": "Để trống cho đến khi hết hạn sử dụng.", - "shareBtn": "Chia sẻ", - "currentAccess": "Truy cập hiện tại", - "typeHeader": "Kiểu", - "targetHeader": "Mục tiêu", - "permissionHeader": "Sự cho phép", - "grantedByHeader": "Được cấp bởi", - "expiresHeader": "Hết hạn", - "noAccessEntries": "Chưa có thông tin đăng nhập nào.", - "expiredLabel": "Hết hạn", - "neverLabel": "Không bao giờ", - "revokeBtn": "Thu hồi", - "cancelBtn": "Hủy bỏ", - "savingBtn": "Đang lưu...", - "updateHostBtn": "Cập nhật máy chủ", - "addHostBtn": "Thêm máy chủ" + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "Tìm kiếm máy chủ, lệnh hoặc cài đặt...", - "quickActions": "Thao tác nhanh", - "hostManager": "Quản lý máy chủ", - "hostManagerDesc": "Quản lý, thêm hoặc chỉnh sửa máy chủ", - "addNewHost": "Thêm máy chủ mới", - "addNewHostDesc": "Đăng ký máy chủ mới", - "adminSettings": "Cài đặt quản trị", - "adminSettingsDesc": "Cấu hình tùy chọn hệ thống và người dùng", - "userProfile": "Hồ sơ người dùng", - "userProfileDesc": "Quản lý tài khoản và tùy chọn của bạn", - "addCredential": "Thêm thông tin đăng nhập", - "addCredentialDesc": "Lưu trữ khóa SSH hoặc mật khẩu", - "recentActivity": "Hoạt động gần đây", - "serversAndHosts": "Máy chủ & Máy chủ lưu trữ", - "noHostsFound": "Không tìm thấy máy chủ nào phù hợp với \"{{search}}\"", - "links": "Liên kết", - "navigate": "Điều hướng", - "select": "Lựa chọn", - "toggleWith": "Chuyển đổi với" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "Pane {{index}} - trống", - "noTabAssigned": "Không có tab nào được chỉ định", - "focusedPane": "Khung đang hoạt động" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "Không có kết nối", - "noConnectionsDesc": "Mở cửa sổ dòng lệnh, trình quản lý tập tin hoặc máy tính từ xa để xem các kết nối tại đây.", - "connectedFor": "Đã kết nối cho {{duration}}", - "connected": "Đã kết nối", - "disconnected": "Đã ngắt kết nối", - "closeTab": "Đóng tab", - "closeConnection": "Mối liên hệ mật thiết", - "forgetTab": "Quên", - "removeBackground": "Di dời", - "reconnect": "Kết nối lại", - "reopenTab": "Mở cửa trở lại", - "sectionOpen": "Mở", - "sectionBackground": "Lý lịch", - "backgroundDesc": "Phiên làm việc sẽ vẫn duy trì trong 30 phút sau khi ngắt kết nối và có thể được kết nối lại.", - "persisted": "Vẫn tồn tại ở chế độ nền", - "expiresIn": "Hết hạn sau {{duration}}", - "search": "Tìm kiếm các kết nối...", - "noSearchResults": "Không có kết nối nào phù hợp với tìm kiếm của bạn." + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "Đang kết nối đến phiên {{type}}...", - "connectionError": "Lỗi kết nối", - "connectionFailed": "Kết nối thất bại", - "failedToConnect": "Không thể lấy mã thông báo kết nối", - "hostNotFound": "Không tìm thấy máy chủ", - "noHostSelected": "Chưa chọn máy chủ nào", - "reconnect": "Kết nối lại", - "retry": "Thử lại", - "guacdUnavailable": "Dịch vụ máy tính từ xa (guacd) hiện không khả dụng. Vui lòng đảm bảo guacd đang chạy, có thể truy cập và được cấu hình đúng cách trong cài đặt quản trị.", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Tổ hợp phím Win+L (Khóa màn hình)", - "winKey": "Phím Windows", - "ctrl": "Điều khiển", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", + "ctrl": "Ctrl", "alt": "Alt", - "shift": "Sự thay đổi", - "win": "Thắng", - "stickyActive": "{{key}} (đã khóa - nhấp để mở)", - "stickyInactive": "{{key}} (nhấp để khóa)", - "esc": "Bỏ trốn", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "Trang chủ", - "end": "Kết thúc", - "pageUp": "Trang lên", - "pageDown": "Trang xuống", - "arrowUp": "Mũi tên lên", - "arrowDown": "Mũi tên xuống", - "arrowLeft": "Mũi tên sang trái", - "arrowRight": "Mũi tên sang phải", - "fnToggle": "Các phím chức năng", - "reconnect": "Kết nối lại phiên", - "collapse": "Thu gọn thanh công cụ", - "expand": "Mở rộng thanh công cụ", - "dragHandle": "Kéo để định vị lại" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "Kết nối với máy chủ", - "clear": "Thông thoáng", - "paste": "Dán", - "reconnect": "Kết nối lại", - "connectionLost": "Mất kết nối", - "connected": "Đã kết nối", - "clipboardWriteFailed": "Không thể sao chép vào clipboard. Hãy đảm bảo trang được phục vụ qua HTTPS hoặc localhost.", - "clipboardReadFailed": "Không thể đọc dữ liệu từ clipboard. Hãy đảm bảo rằng bạn đã cấp quyền truy cập clipboard.", - "clipboardHttpWarning": "Ứng dụng Paste yêu cầu HTTPS. Hãy sử dụng tổ hợp phím Ctrl+Shift+V hoặc chạy Termix qua HTTPS.", - "unknownError": "Lỗi không xác định đã xảy ra", - "websocketError": "Lỗi kết nối WebSocket", - "connecting": "Đang kết nối...", - "noHostSelected": "Chưa chọn máy chủ nào", - "reconnecting": "Đang kết nối lại... ({{attempt}}/{{max}})", - "reconnected": "Đã kết nối lại thành công", - "tmuxSessionCreated": "Phiên tmux đã được tạo: {{name}}", - "tmuxSessionAttached": "Phiên tmux đã được đính kèm: {{name}}", - "tmuxUnavailable": "tmux không được cài đặt trên máy chủ từ xa, do đó hệ thống sẽ sử dụng shell tiêu chuẩn thay thế.", - "tmuxSessionPickerTitle": "Phiên tmux", - "tmuxSessionPickerDesc": "Đã tìm thấy các phiên tmux hiện có trên máy chủ này. Chọn một phiên để kết nối lại hoặc tạo phiên mới.", + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", "tmuxWindows": "Windows", - "tmuxWindowCount": "{{count}} cửa sổ", - "tmuxAttached": "Khách hàng đính kèm", - "tmuxAttachedCount": "{{count}} đính kèm", - "tmuxLastActivity": "Hoạt động cuối cùng", - "tmuxTimeJustNow": "vừa nãy", - "tmuxTimeMinutes": "{{count}}m trước", - "tmuxTimeHours": "{{count}}giờ trước", - "tmuxTimeDays": "{{count}}ngày trước", - "tmuxCreateNew": "Bắt đầu phiên mới", - "tmuxCopyHint": "Điều chỉnh vùng chọn và nhấn Enter để sao chép vào clipboard", - "tmuxDetach": "Tách khỏi phiên tmux", - "tmuxDetached": "Đã tách khỏi phiên tmux", - "maxReconnectAttemptsReached": "Đã đạt số lần thử kết nối lại tối đa.", - "closeTab": "Đóng", - "connectionTimeout": "Hết thời gian chờ kết nối", - "terminalTitle": "Thiết bị đầu cuối - {{host}}", - "terminalWithPath": "Thiết bị đầu cuối - {{host}}:{{path}}", - "runTitle": "Đang chạy {{command}} - {{host}}", - "totpRequired": "Yêu cầu xác thực hai yếu tố", - "totpCodeLabel": "Mã xác minh", - "totpVerify": "Xác minh", - "warpgateAuthRequired": "Cần xác thực Warpgate", - "warpgateSecurityKey": "Khóa bảo mật", - "warpgateAuthUrl": "URL xác thực", - "warpgateOpenBrowser": "Mở trong trình duyệt", - "warpgateContinue": "Tôi đã hoàn tất xác thực.", - "opksshAuthRequired": "Yêu cầu xác thực OPKSSH", - "opksshAuthDescription": "Hoàn tất xác thực trên trình duyệt của bạn để tiếp tục. Phiên này sẽ có hiệu lực trong 24 giờ.", - "opksshOpenBrowser": "Mở trình duyệt để xác thực.", - "opksshWaitingForAuth": "Đang chờ xác thực trên trình duyệt...", - "opksshAuthenticating": "Đang xử lý xác thực...", - "opksshTimeout": "Quá trình xác thực đã hết hạn. Vui lòng thử lại.", - "opksshAuthFailed": "Xác thực không thành công. Vui lòng kiểm tra lại thông tin đăng nhập của bạn và thử lại.", - "opksshSignInWith": "Đăng nhập bằng {{provider}}", - "sudoPasswordPopupTitle": "Nhập mật khẩu?", - "websocketAbnormalClose": "Kết nối bị ngắt đột ngột. Điều này có thể do sự cố cấu hình proxy ngược hoặc SSL. Vui lòng kiểm tra nhật ký máy chủ.", - "connectionLogTitle": "Nhật ký kết nối", - "connectionLogCopy": "Sao chép nhật ký vào clipboard", - "connectionLogEmpty": "Chưa có nhật ký kết nối nào.", - "connectionLogWaiting": "Đang chờ nhật ký kết nối...", - "connectionLogCopied": "Nhật ký kết nối đã được sao chép vào clipboard", - "connectionLogCopyFailed": "Không thể sao chép nhật ký vào clipboard", - "connectionRejected": "Kết nối bị máy chủ từ chối. Vui lòng kiểm tra cấu hình xác thực và mạng của bạn.", - "hostKeyRejected": "Xác thực khóa máy chủ SSH bị từ chối. Kết nối bị hủy.", - "sessionTakenOver": "Phiên làm việc đã được mở trong một tab khác. Đang kết nối lại..." + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "Chưa chọn máy chủ nào", - "initializingEditor": "Đang khởi tạo trình soạn thảo...", - "file": "Tài liệu", - "folder": "Thư mục", - "uploadFile": "Tải lên tệp", - "downloadFile": "Tải xuống", - "extractArchive": "Trích xuất tệp lưu trữ", - "extractingArchive": "Đang trích xuất {{name}}...", - "archiveExtractedSuccessfully": "{{name}} đã trích xuất thành công", - "extractFailed": "Trích xuất thất bại", - "compressFile": "Nén tệp", - "compressFiles": "Nén tệp", - "compressFilesDesc": "Nén {{count}} mục vào một kho lưu trữ", - "archiveName": "Tên kho lưu trữ", - "enterArchiveName": "Nhập tên kho lưu trữ...", - "compressionFormat": "Định dạng nén", - "selectedFiles": "Các tệp đã chọn", - "andMoreFiles": "và {{count}} nhiều hơn nữa...", - "compress": "Nén", - "compressingFiles": "Nén {{count}} mục thành {{name}}...", - "filesCompressedSuccessfully": "{{name}} đã được tạo thành công", - "compressFailed": "Quá trình nén thất bại", - "edit": "Biên tập", - "preview": "Xem trước", - "previous": "Trước", - "next": "Kế tiếp", - "pageXOfY": "Trang {{current}} trên {{total}}", - "zoomOut": "Thu nhỏ", - "zoomIn": "Phóng to", - "newFile": "Tệp mới", - "newFolder": "Thư mục mới", - "rename": "Đổi tên", - "uploading": "Đang tải lên...", - "uploadingFile": "Đang tải lên {{name}}...", - "fileName": "Tên tệp", - "folderName": "Tên thư mục", - "fileUploadedSuccessfully": "Tệp \"{{name}}\" đã được tải lên thành công", - "failedToUploadFile": "Không thể tải tệp lên.", - "fileDownloadedSuccessfully": "Tệp \"{{name}}\" đã được tải xuống thành công", - "failedToDownloadFile": "Không thể tải xuống tệp.", - "fileCreatedSuccessfully": "Tệp \"{{name}}\" đã được tạo thành công", - "folderCreatedSuccessfully": "Thư mục \"{{name}}\" đã được tạo thành công", - "failedToCreateItem": "Không thể tạo mục", - "operationFailed": "Thao tác {{operation}} đã thất bại đối với {{name}}: {{error}}", - "failedToResolveSymlink": "Không thể giải quyết liên kết tượng trưng", - "itemsDeletedSuccessfully": "{{count}} mục đã được xóa thành công", - "failedToDeleteItems": "Không thể xóa các mục", - "sudoPasswordRequired": "Cần có mật khẩu quản trị viên.", - "enterSudoPassword": "Nhập mật khẩu sudo để tiếp tục thao tác này.", - "sudoPassword": "Mật khẩu Sudo", - "sudoOperationFailed": "Thao tác Sudo thất bại", - "sudoAuthFailed": "Xác thực Sudo thất bại", - "dragFilesToUpload": "Kéo thả tệp vào đây để tải lên", - "emptyFolder": "Thư mục này trống", - "searchFiles": "Tìm kiếm tập tin...", - "upload": "Tải lên", - "selectHostToStart": "Chọn máy chủ để bắt đầu quản lý tập tin", - "sshRequiredForFileManager": "Trình quản lý tập tin yêu cầu SSH. Máy chủ này chưa bật SSH.", - "failedToConnect": "Không thể kết nối SSH.", - "failedToLoadDirectory": "Không thể tải thư mục", - "noSSHConnection": "Không có kết nối SSH nào khả dụng", - "copy": "Sao chép", - "cut": "Cắt", - "paste": "Dán", - "copyPath": "Sao chép đường dẫn", - "copyPaths": "Sao chép đường dẫn", - "delete": "Xóa bỏ", - "properties": "Của cải", - "refresh": "Làm cho khỏe lại", - "downloadFiles": "Tải xuống {{count}} tập tin vào Trình duyệt", - "copyFiles": "Sao chép các mục {{count}}", - "cutFiles": "Cắt các mục {{count}}", - "deleteFiles": "Xóa {{count}} mục", - "filesCopiedToClipboard": "{{count}} mục đã được sao chép vào clipboard", - "filesCutToClipboard": "{{count}} các mục đã được cắt vào clipboard", - "pathCopiedToClipboard": "Đường dẫn đã được sao chép vào clipboard", - "pathsCopiedToClipboard": "{{count}} đường dẫn đã được sao chép vào clipboard", - "failedToCopyPath": "Không thể sao chép đường dẫn vào clipboard", - "movedItems": "Đã di chuyển {{count}} mục", - "failedToDeleteItem": "Không thể xóa mục", - "itemRenamedSuccessfully": "{{type}} đã đổi tên thành công", - "failedToRenameItem": "Không thể đổi tên mục", - "download": "Tải xuống", - "permissions": "Quyền hạn", - "size": "Kích cỡ", - "modified": "Đã sửa đổi", - "path": "Con đường", - "confirmDelete": "Bạn có chắc chắn muốn xóa {{name}} không?", - "permissionDenied": "Quyền truy cập bị từ chối", - "serverError": "Lỗi máy chủ", - "fileSavedSuccessfully": "Tệp đã được lưu thành công", - "failedToSaveFile": "Không thể lưu tệp", - "confirmDeleteSingleItem": "Bạn có chắc chắn muốn xóa vĩnh viễn \"{{name}} \" không?", - "confirmDeleteMultipleItems": "Bạn có chắc chắn muốn xóa vĩnh viễn {{count}} mục không?", - "confirmDeleteMultipleItemsWithFolders": "Bạn có chắc chắn muốn xóa vĩnh viễn {{count}} mục không? Việc này bao gồm cả thư mục và nội dung bên trong chúng.", - "confirmDeleteFolder": "Bạn có chắc chắn muốn xóa vĩnh viễn thư mục \"{{name}}\" và tất cả nội dung bên trong không?", - "permanentDeleteWarning": "Thao tác này không thể hoàn tác. Mục (các mục) sẽ bị xóa vĩnh viễn khỏi máy chủ.", - "recent": "Gần đây", - "pinned": "Đã ghim", - "folderShortcuts": "Phím tắt thư mục", - "failedToReconnectSSH": "Không thể kết nối lại phiên SSH.", - "openTerminalHere": "Mở cửa sổ dòng lệnh tại đây", - "run": "Chạy", - "openTerminalInFolder": "Mở cửa sổ dòng lệnh trong thư mục này", - "openTerminalInFileLocation": "Mở cửa sổ dòng lệnh tại vị trí tệp", - "runningFile": "Đang chạy - {{file}}", - "onlyRunExecutableFiles": "Chỉ có thể chạy các tệp thực thi.", - "directories": "Danh mục", - "removedFromRecentFiles": "Đã xóa \"{{name}}\" khỏi các tệp gần đây", - "removeFailed": "Xóa không thành công", - "unpinnedSuccessfully": "Đã gỡ ghim \"{{name}}\" thành công", - "unpinFailed": "Gỡ ghim không thành công", - "removedShortcut": "Đã xóa lối tắt \"{{name}}\"", - "removeShortcutFailed": "Xóa lối tắt không thành công", - "clearedAllRecentFiles": "Đã xóa tất cả các tệp gần đây", - "clearFailed": "Xóa thất bại", - "removeFromRecentFiles": "Xóa khỏi tệp gần đây", - "clearAllRecentFiles": "Xóa tất cả các tệp gần đây", - "unpinFile": "Gỡ ghim tệp", - "removeShortcut": "Xóa lối tắt", - "pinFile": "Tệp ghim", - "addToShortcuts": "Thêm vào lối tắt", - "pasteFailed": "Dán thất bại", - "noUndoableActions": "Không có hành động nào có thể hoàn tác", - "undoCopySuccess": "Hoàn tác thao tác sao chép: Đã xóa {{count}} tệp đã sao chép", - "undoCopyFailedDelete": "Hoàn tác thất bại: Không thể xóa bất kỳ tệp nào đã sao chép.", - "undoCopyFailedNoInfo": "Hoàn tác thất bại: Không tìm thấy thông tin tệp đã sao chép.", - "undoMoveSuccess": "Hoàn tác thao tác di chuyển: Đã di chuyển {{count}} tập tin trở lại vị trí ban đầu", - "undoMoveFailedMove": "Hoàn tác thất bại: Không thể khôi phục bất kỳ tệp nào.", - "undoMoveFailedNoInfo": "Hoàn tác thất bại: Không tìm thấy thông tin tệp đã di chuyển.", - "undoDeleteNotSupported": "Không thể hoàn tác thao tác xóa: Các tệp đã bị xóa vĩnh viễn khỏi máy chủ.", - "undoTypeNotSupported": "Loại thao tác hoàn tác không được hỗ trợ", - "undoOperationFailed": "Thao tác hoàn tác thất bại", - "unknownError": "Lỗi không xác định", - "confirm": "Xác nhận", - "find": "Tìm thấy...", - "replace": "Thay thế", - "downloadInstead": "Tải xuống thay vì", - "keyboardShortcuts": "Phím tắt", - "searchAndReplace": "Tìm kiếm và thay thế", - "editing": "Chỉnh sửa", - "search": "Tìm kiếm", - "findNext": "Tìm tiếp theo", - "findPrevious": "Tìm kiếm trước đó", - "save": "Cứu", - "selectAll": "Chọn tất cả", - "undo": "Hoàn tác", - "redo": "Làm lại", - "moveLineUp": "Di chuyển hàng", - "moveLineDown": "Di chuyển dòng xuống", - "toggleComment": "Ẩn/Hiện bình luận", - "autoComplete": "Tự động hoàn thành", - "imageLoadError": "Không thể tải hình ảnh", - "startTyping": "Bắt đầu gõ...", - "unknownSize": "Kích thước không xác định", - "fileIsEmpty": "Tệp tin trống", - "largeFileWarning": "Cảnh báo về tệp tin lớn", - "largeFileWarningDesc": "Tệp này có kích thước {{size}} , có thể gây ra sự cố về hiệu năng khi mở dưới dạng văn bản.", - "fileNotFoundAndRemoved": "Tệp \"{{name}}\" không được tìm thấy và đã bị xóa khỏi các tệp gần đây/được ghim.", - "failedToLoadFile": "Không thể tải tệp: {{error}}", - "serverErrorOccurred": "Đã xảy ra lỗi máy chủ. Vui lòng thử lại sau.", - "autoSaveFailed": "Lưu tự động thất bại", - "fileAutoSaved": "Tệp đã được tự động lưu", - "moveFileFailed": "Không thể di chuyển {{name}}", - "moveOperationFailed": "Thao tác di chuyển thất bại", - "canOnlyCompareFiles": "Chỉ có thể so sánh hai tệp", - "comparingFiles": "So sánh các tập tin: {{file1}} và {{file2}}", - "dragFailed": "Thao tác kéo thả thất bại", - "filePinnedSuccessfully": "Tệp \"{{name}}\" đã được ghim thành công", - "pinFileFailed": "Không thể ghim tệp", - "fileUnpinnedSuccessfully": "Tệp \"{{name}}\" đã được gỡ ghim thành công", - "unpinFileFailed": "Không thể bỏ ghim tệp.", - "shortcutAddedSuccessfully": "Thêm lối tắt thư mục \"{{name}}\" thành công", - "addShortcutFailed": "Không thể thêm lối tắt", - "operationCompletedSuccessfully": "{{operation}} {{count}} mục thành công", - "operationCompleted": "{{operation}} {{count}} mục", - "downloadFileSuccess": "Tệp {{name}} đã được tải xuống thành công", - "downloadFileFailed": "Tải xuống thất bại", - "moveTo": "Di chuyển đến {{name}}", - "diffCompareWith": "So sánh khác biệt với {{name}}", - "dragOutsideToDownload": "Kéo chuột ra ngoài cửa sổ để tải xuống ({{count}} tập tin)", - "newFolderDefault": "Thư mục mới", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "Đã chuyển thành công {{count}} mục đến {{target}}", - "move": "Di chuyển", - "searchInFile": "Tìm kiếm trong tệp (Ctrl+F)", - "showKeyboardShortcuts": "Hiển thị các phím tắt", - "startWritingMarkdown": "Hãy bắt đầu viết nội dung bằng định dạng Markdown...", - "loadingFileComparison": "Đang tải so sánh tệp...", - "reload": "Tải lại", - "compare": "So sánh", - "sideBySide": "Cạnh nhau", - "inline": "Nội tuyến", - "fileComparison": "So sánh tập tin: {{file1}} so với {{file2}}", - "fileTooLarge": "Tệp quá lớn: {{error}}", - "sshConnectionFailed": "Kết nối SSH thất bại. Vui lòng kiểm tra kết nối của bạn tới {{name}} ({{ip}}:{{port}})", - "loadFileFailed": "Không thể tải tệp: {{error}}", - "connecting": "Đang kết nối...", - "connectedSuccessfully": "Kết nối thành công", - "totpVerificationFailed": "Xác thực TOTP thất bại", - "warpgateVerificationFailed": "Xác thực Warpgate thất bại", - "authenticationFailed": "Xác thực thất bại", - "verificationCodePrompt": "Mã xác minh:", - "changePermissions": "Thay đổi quyền", - "currentPermissions": "Quyền hiện tại", - "owner": "Người sở hữu", - "group": "Nhóm", - "others": "Người khác", - "read": "Đọc", - "write": "Viết", - "execute": "Thực thi", - "permissionsChangedSuccessfully": "Quyền truy cập đã được thay đổi thành công.", - "failedToChangePermissions": "Không thể thay đổi quyền truy cập", - "name": "Tên", - "sortByName": "Tên", - "sortByDate": "Ngày sửa đổi", - "sortBySize": "Kích cỡ", - "ascending": "Đi lên", - "descending": "Đi xuống", - "root": "Rễ", - "new": "Mới", - "sortBy": "Sắp xếp theo", - "items": "Mặt hàng", - "selected": "Đã chọn", - "editor": "Biên tập viên", - "octal": "bát phân", - "storage": "Kho", - "disk": "Đĩa", - "used": "Đã sử dụng", - "of": "của", - "toggleSidebar": "Ẩn/Hiện thanh bên", - "cannotLoadPdf": "Không thể tải PDF", - "pdfLoadError": "Đã xảy ra lỗi khi tải tệp PDF này.", - "loadingPdf": "Đang tải PDF...", - "loadingPage": "Đang tải trang..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "Sao chép vào máy chủ…", - "moveToHost": "Di chuyển đến máy chủ…", - "copyItemsToHost": "Sao chép {{count}} mục vào máy chủ…", - "moveItemsToHost": "Di chuyển {{count}} vật phẩm đến máy chủ…", - "noHostsConnected": "Không có máy chủ quản lý tập tin nào khác khả dụng.", - "noHostsConnectedHint": "Thêm một máy chủ SSH khác có bật Trình quản lý tệp trong Trình quản lý máy chủ.", - "selectDestinationHost": "Chọn máy chủ đích", - "destinationPath": "Đường dẫn đích", - "recentDestinations": "Các điểm đến gần đây", - "collapseRecentDestinations": "Thu gọn các điểm đến gần đây", - "expandRecentDestinations": "Mở rộng các điểm đến gần đây", - "browseFolders": "Duyệt các thư mục đích", - "browseDestination": "Duyệt hoặc nhập đường dẫn", - "confirmCopy": "Sao chép", - "confirmMove": "Di chuyển", - "transferring": "Đang chuyển…", - "compressing": "Nén…", - "extracting": "Đang trích xuất…", - "transferringItems": "Đang chuyển {{current}} trong số {{total}} mục…", - "transferSuccess": "Giao dịch hoàn tất", - "transferError": "Giao dịch chuyển khoản thất bại", - "transferPartial": "Quá trình chuyển giao hoàn tất với {{count}} lỗi", - "transferPartialHint": "Không thể chuyển khoản: {{paths}}", - "itemsSummary": "{{count}} mục", - "destMustBeDirectory": "Thư mục đích phải là thư mục dùng cho việc chuyển nhiều mục.", - "selectThisFolder": "Chọn thư mục này", - "browsePathWillBeCreated": "Thư mục này chưa tồn tại. Nó sẽ được tạo khi quá trình chuyển dữ liệu bắt đầu.", - "browsePathError": "Không thể mở đường dẫn này trên máy chủ đích.", - "goUp": "Đi lên", - "copyFolderToHost": "Sao chép thư mục vào máy chủ…", - "moveFolderToHost": "Di chuyển thư mục đến máy chủ…", - "hostReady": "Sẵn sàng", - "hostConnecting": "Đang kết nối…", - "hostDisconnected": "Không kết nối", - "hostAuthRequired": "Cần xác thực — trước tiên hãy mở Trình quản lý tập tin trên máy chủ này.", - "hostConnectionFailed": "Kết nối thất bại", - "metricsTitle": "Thời gian chuyển khoản", - "metricsPrepare": "Chuẩn bị điểm đến: {{duration}}", - "metricsCompress": "Nén tại nguồn: {{duration}}", - "metricsHopSourceRead": "Nguồn → máy chủ: {{throughput}}", - "metricsHopDestSftpWrite": "Máy chủ → đích (SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "Máy chủ → đích (cục bộ): {{throughput}}", - "metricsTransfer": "Từ đầu đến cuối: {{throughput}} ({{duration}})", - "metricsExtract": "Trích xuất tại đích đến: {{duration}}", - "metricsSourceDelete": "Xóa khỏi nguồn: {{duration}}", - "metricsTotal": "Tổng cộng: {{duration}}", - "progressCompressing": "Nén trên máy chủ nguồn…", - "progressExtracting": "Đang trích xuất tại đích đến…", - "progressTransferring": "Đang truyền dữ liệu…", - "progressReconnecting": "Đang kết nối lại…", - "parallelSegmentsLabel": "Các làn chuyển tiếp song song", - "parallelSegmentsOption": "{{count}} làn đường", - "parallelSegmentsHint": "Các tệp lớn được chia thành các đoạn 256 MB. Nhiều làn truyền dữ liệu sử dụng các kết nối riêng biệt (giống như bắt đầu nhiều lần truyền) để đạt được thông lượng tổng thể cao hơn.", - "progressTotalSpeed": "{{speed}} tổng ({{lanes}} làn đường)", - "progressTransferringItems": "Đang chuyển các tập tin ({{current}} của {{total}})…", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", + "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} tập tin", - "sourceNotDeletedPartial": "Các tập tin nguồn được giữ lại (chuyển giao một phần)", - "jumpHostLimitation": "Cả hai máy chủ phải có thể truy cập được từ máy chủ Termix. Định tuyến trực tiếp giữa các máy chủ không được hỗ trợ.", - "cancel": "Hủy bỏ", - "methodLabel": "Phương thức chuyển giao", - "methodAuto": "Tự động", - "methodTar": "Tệp lưu trữ Tar", - "methodItemSftp": "SFTP theo từng tệp", - "methodAutoHint": "Hệ thống sẽ chọn phương thức nén tar hoặc SFTP cho từng tập tin dựa trên số lượng, kích thước và khả năng nén của tập tin. Các tập tin đơn lẻ luôn sử dụng SFTP truyền phát trực tuyến.", - "methodTarHint": "Nén dữ liệu trên nguồn, chuyển một tệp lưu trữ duy nhất, giải nén trên đích. Yêu cầu cài đặt tar trên cả hai máy chủ Unix.", - "methodItemSftpHint": "Chuyển từng tập tin riêng lẻ qua SFTP. Hoạt động trên mọi hệ điều hành, bao gồm cả Windows.", - "methodPreviewLoading": "Tính toán phương pháp chuyển đổi…", - "methodPreviewError": "Không thể xem trước phương thức chuyển khoản. Máy chủ vẫn sẽ chọn phương thức khi bạn bắt đầu.", - "methodPreviewWillUseTar": "Sẽ sử dụng: Tệp lưu trữ Tar", - "methodPreviewWillUseItemSftp": "Sẽ sử dụng: SFTP cho từng tệp", - "methodPreviewScanSummary": "{{fileCount}} tập tin, {{totalSize}} tổng số (đã quét trên máy chủ nguồn).", - "methodItemSftpLimitation": "Mỗi tập tin sử dụng cùng một luồng SFTP như khi sao chép từng tập tin riêng lẻ. Tiến trình được tính gộp cho tất cả các tập tin, vì vậy thanh tiến trình di chuyển chậm hơn đối với các tập tin lớn.", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "Bạn đã chọn SFTP cho từng tập tin.", - "user_tar": "Bạn đã chọn định dạng lưu trữ tar.", - "tar_unavailable": "Tar không khả dụng trên một hoặc cả hai máy chủ — thay vào đó, SFTP sẽ được sử dụng cho từng tệp.", - "windows_host": "Quá trình này sử dụng máy chủ Windows — không dùng lệnh tar.", - "auto_multi_large": "Tự động: nhiều tệp bao gồm một tệp lớn ({{largestSize}}) với dữ liệu có thể nén — các gói tar được chuyển thành một lần.", - "auto_single_large_in_archive": "Tự động: một tập tin lớn ({{largestSize}}) trong bộ này — SFTP cho mỗi tập tin.", - "auto_many_incompressible": "Tự động: dữ liệu chủ yếu không thể nén được — SFTP theo từng tệp.", - "auto_many_files": "Tự động: nhiều tệp ({{fileCount}}) — tar giảm chi phí xử lý trên mỗi tệp.", - "auto_default": "Tự động: SFTP cho từng tệp trong bộ này." + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "Hủy bỏ", - "progressCancelling": "Đang hủy…", - "progressStalled": "Bị kẹt", - "resumedHint": "Đã kết nối lại với quá trình truyền tải đang hoạt động được bắt đầu trong cửa sổ khác.", - "transferCancelled": "Giao dịch bị hủy", - "transferCancelledCopyHint": "Các tập tin chưa hoàn chỉnh có thể vẫn còn lưu lại trên thư mục đích.", - "transferCancelledMoveHint": "Các tập tin chưa hoàn chỉnh có thể vẫn còn trên thư mục đích. Các tập tin nguồn không bị xóa.", - "cleanupDestFiles": "Điểm đến dọn dẹp", - "cleanupDestFilesSuccess": "Đã xóa một phần tệp khỏi thư mục đích.", - "cleanupDestFilesPartial": "Một số tập tin chưa hoàn chỉnh không thể được xóa.", - "cleanupDestFilesNothing": "Không cần phải dọn dẹp gì ở điểm đến", - "cleanupDestFilesError": "Quá trình dọn dẹp thất bại", - "retryTransfer": "Thử lại", - "retryTransferError": "Thử lại không thành công", - "transferFailedRetryHint": "Một phần dữ liệu đã được lưu trữ tại đích đến. Quá trình thử lại sẽ tiếp tục khi kết nối được khôi phục." + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "Không có đường hầm SSH", - "createFirstTunnelMessage": "Bạn chưa tạo bất kỳ đường hầm SSH nào. Hãy cấu hình kết nối đường hầm trong Trình quản lý máy chủ để bắt đầu.", - "connected": "Đã kết nối", - "disconnected": "Đã ngắt kết nối", - "connecting": "Đang kết nối...", - "error": "Lỗi", - "canceling": "Đang hủy...", - "connect": "Kết nối", - "disconnect": "Ngắt kết nối", - "cancel": "Hủy bỏ", - "port": "Cảng", - "localPort": "Cảng địa phương", - "remotePort": "Cổng từ xa", - "currentHostPort": "Cổng máy chủ hiện tại", - "endpointPort": "Cổng điểm cuối", - "bindIp": "Địa chỉ IP cục bộ", - "endpointSshConfig": "Cấu hình SSH điểm cuối", - "endpointSshHost": "Máy chủ SSH điểm cuối", - "endpointSshHostPlaceholder": "Chọn một máy chủ đã được cấu hình", - "endpointSshHostRequired": "Chọn một máy chủ SSH điểm cuối cho mỗi đường hầm SSH của máy khách.", - "attempt": "Lần thử {{current}} của {{max}}", - "nextRetryIn": "Lần thử lại tiếp theo sau {{seconds}} giây", - "clientTunnels": "Đường hầm máy khách", - "clientTunnel": "Đường hầm máy khách", - "addClientTunnel": "Thêm đường hầm máy khách", - "noClientTunnels": "Hiện chưa có đường hầm máy khách nào được cấu hình trên máy tính để bàn này.", - "tunnelName": "Tên đường hầm", - "remoteHost": "Máy chủ từ xa", - "autoStart": "Tự động khởi động", - "clientAutoStartDesc": "Bắt đầu khi ứng dụng khách trên máy tính để bàn này được mở và duy trì kết nối.", - "clientManualStartDesc": "Sử dụng nút Bắt đầu và Dừng từ hàng này. Termix sẽ không tự động mở nó.", - "clientRemoteServerNote": "Việc chuyển tiếp từ xa có thể yêu cầu thiết lập AllowTcpForwarding và GatewayPorts trên máy chủ SSH của điểm cuối. Cổng từ xa sẽ đóng khi máy tính để bàn này ngắt kết nối.", - "clientTunnelStarted": "Đường hầm máy khách đã được khởi động", - "clientTunnelStopped": "Đường hầm máy khách đã dừng", - "tunnelTestSucceeded": "Thử nghiệm đường hầm đã thành công.", - "tunnelTestFailed": "Thử nghiệm đường hầm thất bại", - "localSaved": "Đường hầm máy khách đã được lưu", - "localSaveError": "Không thể lưu các đường hầm máy khách cục bộ.", - "invalidBindIp": "Địa chỉ IP cục bộ phải là địa chỉ IPv4 hợp lệ.", - "invalidLocalTargetIp": "Địa chỉ IP đích cục bộ phải là địa chỉ IPv4 hợp lệ.", - "invalidLocalPort": "Mã cổng cục bộ phải nằm trong khoảng từ 1 đến 65535.", - "invalidRemotePort": "Cổng từ xa phải nằm trong khoảng từ 1 đến 65535.", - "invalidLocalTargetPort": "Cổng đích cục bộ phải nằm trong khoảng từ 1 đến 65535.", - "invalidEndpointPort": "Cổng điểm cuối phải nằm trong khoảng từ 1 đến 65535.", - "duplicateAutoStartBind": "Chỉ một đường hầm máy khách tự khởi động có thể sử dụng {{bind}}.", - "manualControlError": "Không thể cập nhật trạng thái đường hầm.", - "active": "Tích cực", - "start": "Bắt đầu", - "stop": "Dừng lại", - "test": "Bài kiểm tra", - "type": "Loại đường hầm", - "typeLocal": "Địa phương (-L)", - "typeRemote": "Điều khiển từ xa (-R)", - "typeDynamic": "Động lực (-D)", - "typeServerLocalDesc": "Máy chủ hiện tại đến điểm cuối.", - "typeServerRemoteDesc": "Điểm cuối quay trở lại máy chủ hiện tại.", - "typeClientLocalDesc": "Từ máy tính cục bộ đến thiết bị đầu cuối.", - "typeClientRemoteDesc": "Điểm cuối quay trở lại máy tính cục bộ.", - "typeClientDynamicDesc": "SOCKS trên máy tính cục bộ.", - "typeDynamicDesc": "Chuyển tiếp lưu lượng SOCKS5 CONNECT thông qua SSH", - "forwardDescriptionServerLocal": "Máy chủ hiện tại {{sourcePort}} → điểm cuối {{endpointPort}}.", - "forwardDescriptionServerRemote": "Điểm cuối {{endpointPort}} → máy chủ hiện tại {{sourcePort}}.", - "forwardDescriptionServerDynamic": "SOCKS trên máy chủ hiện tại {{sourcePort}}.", - "forwardDescriptionClientLocal": "Cục bộ {{sourcePort}} → từ xa {{endpointPort}}.", - "forwardDescriptionClientRemote": "Từ xa {{sourcePort}} → cục bộ {{endpointPort}}.", - "forwardDescriptionClientDynamic": "SOCKS trên cổng cục bộ {{sourcePort}}.", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → TẤT QUA {{endpoint}}", - "autoNameClientLocal": "Cục bộ {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → cục bộ {{localPort}}", - "autoNameClientDynamic": "TẤT {{localPort}} qua {{endpoint}}", - "route": "Tuyến đường:", - "lastStarted": "Lần cuối bắt đầu", - "lastTested": "Lần kiểm tra cuối cùng", - "lastError": "Lỗi cuối cùng", - "maxRetries": "Số lần thử lại tối đa", - "maxRetriesDescription": "Số lần thử lại tối đa.", - "retryInterval": "Khoảng thời gian thử lại (giây)", - "retryIntervalDescription": "Thời gian chờ giữa các lần thử lại.", - "local": "Địa phương", - "remote": "Xa", - "destination": "Điểm đến", - "host": "Chủ nhà", - "mode": "Cách thức", - "noHostSelected": "Chưa chọn máy chủ nào", - "working": "Đang làm việc..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "Ký ức", - "disk": "Đĩa", - "network": "Mạng", - "uptime": "Thời gian hoạt động", - "processes": "Quy trình", - "available": "Có sẵn", - "free": "Miễn phí", - "connecting": "Đang kết nối...", - "connectionFailed": "Không thể kết nối đến máy chủ.", - "naCpus": "Không áp dụng CPU", - "cpuCores_one": "{{count}} Lõi", - "cpuCores_other": "{{count}} Lõi", - "cpuUsage": "Mức sử dụng CPU", - "memoryUsage": "Mức sử dụng bộ nhớ", - "diskUsage": "Mức sử dụng ổ đĩa", - "failedToFetchHostConfig": "Không thể tải cấu hình máy chủ", - "serverOffline": "Máy chủ ngoại tuyến", - "cannotFetchMetrics": "Không thể lấy số liệu từ máy chủ ngoại tuyến.", - "totpFailed": "Xác thực TOTP thất bại", - "noneAuthNotSupported": "Server Stats không hỗ trợ loại xác thực 'non'.", - "load": "Trọng tải", - "systemInfo": "Thông tin hệ thống", - "hostname": "Tên máy chủ", - "operatingSystem": "Hệ điều hành", - "kernel": "Hạt nhân", - "seconds": "giây", - "networkInterfaces": "Giao diện mạng", - "noInterfacesFound": "Không tìm thấy giao diện mạng nào.", - "noProcessesFound": "Không tìm thấy tiến trình nào.", - "loginStats": "Thống kê đăng nhập SSH", - "noRecentLoginData": "Không có dữ liệu đăng nhập gần đây", - "executingQuickAction": "Đang thực thi {{name}}...", - "quickActionSuccess": "{{name}} đã hoàn thành thành công", - "quickActionFailed": "{{name}} thất bại", - "quickActionError": "Không thể thực thi {{name}}", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "Cổng nghe", - "protocol": "Giao thức", - "port": "Cảng", - "address": "Địa chỉ", - "process": "Quá trình", - "noData": "Không có dữ liệu cổng lắng nghe" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "Tường lửa", - "inactive": "Không hoạt động", - "policy": "Chính sách", - "rules": "quy tắc", - "noData": "Không có dữ liệu tường lửa nào.", - "action": "Hoạt động", - "protocol": "Nguyên mẫu", - "port": "Cảng", - "source": "Nguồn", - "anywhere": "Bất cứ nơi nào" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "Tải trung bình", - "swap": "Tráo đổi", - "architecture": "Ngành kiến trúc", - "refresh": "Làm cho khỏe lại", - "retry": "Thử lại" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "Quản lý SSH và máy tính từ xa tự lưu trữ", - "loginTitle": "Đăng nhập vào Termix", - "registerTitle": "Tạo tài khoản", - "forgotPassword": "Quên mật khẩu?", - "rememberMe": "Lưu mã xác thực thiết bị trong 30 ngày (bao gồm cả mã TOTP)", - "noAccount": "Bạn chưa có tài khoản?", - "hasAccount": "Bạn đã có tài khoản chưa?", - "twoFactorAuth": "Xác thực hai yếu tố", - "enterCode": "Nhập mã xác minh", - "backupCode": "Hoặc sử dụng mã dự phòng", - "verifyCode": "Xác minh mã", - "redirectingToApp": "Đang chuyển hướng đến ứng dụng...", - "sshAuthenticationRequired": "Yêu cầu xác thực SSH", - "sshNoKeyboardInteractive": "Xác thực tương tác bàn phím không khả dụng", - "sshAuthenticationFailed": "Xác thực không thành công", - "sshAuthenticationTimeout": "Hết thời gian xác thực", - "sshNoKeyboardInteractiveDescription": "Máy chủ không hỗ trợ xác thực bằng bàn phím. Vui lòng cung cấp mật khẩu hoặc khóa SSH của bạn.", - "sshAuthFailedDescription": "Thông tin đăng nhập bạn cung cấp không chính xác. Vui lòng thử lại với thông tin đăng nhập hợp lệ.", - "sshTimeoutDescription": "Quá trình xác thực đã hết thời gian chờ. Vui lòng thử lại.", - "sshProvideCredentialsDescription": "Vui lòng cung cấp thông tin đăng nhập SSH của bạn để kết nối với máy chủ này.", - "sshPasswordDescription": "Nhập mật khẩu cho kết nối SSH này.", - "sshKeyPasswordDescription": "Nếu khóa SSH của bạn được mã hóa, hãy nhập mật khẩu vào đây.", - "passphraseRequired": "Cần có mật khẩu", - "passphraseRequiredDescription": "Khóa SSH đã được mã hóa. Vui lòng nhập mật khẩu để mở khóa.", - "back": "Mặt sau", - "firstUser": "Người dùng đầu tiên", - "firstUserMessage": "Bạn là người dùng đầu tiên và sẽ được chỉ định làm quản trị viên. Bạn có thể xem cài đặt quản trị viên trong menu thả xuống người dùng ở thanh bên. Nếu bạn cho rằng đây là lỗi, hãy kiểm tra nhật ký Docker hoặc tạo sự cố trên GitHub.", - "external": "Bên ngoài", - "loginWithExternal": "Đăng nhập bằng tài khoản nhà cung cấp bên ngoài", - "loginWithExternalDesc": "Đăng nhập bằng nhà cung cấp danh tính bên ngoài đã được cấu hình của bạn", - "externalNotSupportedInElectron": "Hiện tại, ứng dụng Electron chưa hỗ trợ xác thực bên ngoài. Vui lòng sử dụng phiên bản web để đăng nhập bằng OIDC.", - "resetPasswordButton": "Đặt lại mật khẩu", - "sendResetCode": "Gửi mã đặt lại", - "resetCodeDesc": "Nhập tên người dùng của bạn để nhận mã đặt lại mật khẩu. Mã này sẽ được ghi lại trong nhật ký của container Docker.", - "resetCode": "Mã đặt lại", - "verifyCodeButton": "Xác minh mã", - "enterResetCode": "Nhập mã 6 chữ số từ nhật ký container Docker cho người dùng:", - "newPassword": "Mật khẩu mới", - "confirmNewPassword": "Xác nhận mật khẩu", - "enterNewPassword": "Nhập mật khẩu mới của bạn cho người dùng:", - "signUp": "Đăng ký", - "desktopApp": "Ứng dụng máy tính để bàn", - "loggingInToDesktopApp": "Đăng nhập vào ứng dụng máy tính để bàn", - "loadingServer": "Đang tải máy chủ...", - "dataLossWarning": "Việc đặt lại mật khẩu theo cách này sẽ xóa tất cả các máy chủ SSH đã lưu, thông tin đăng nhập và dữ liệu được mã hóa khác của bạn. Hành động này không thể hoàn tác. Chỉ sử dụng cách này nếu bạn đã quên mật khẩu và chưa đăng nhập.", - "authenticationDisabled": "Xác thực bị vô hiệu hóa", - "authenticationDisabledDesc": "Tất cả các phương thức xác thực hiện đang bị vô hiệu hóa. Vui lòng liên hệ với quản trị viên của bạn.", - "attemptsRemaining": "{{count}} lần thử còn lại" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "Xác minh khóa máy chủ SSH", - "keyChangedWarning": "Khóa máy chủ SSH đã thay đổi", - "firstConnectionTitle": "Lần đầu tiên kết nối với máy chủ này.", - "firstConnectionDescription": "Không thể xác minh tính xác thực của máy chủ này. Hãy kiểm tra xem dấu vân tay có khớp với những gì bạn mong đợi hay không.", - "keyChangedDescription": "Khóa máy chủ (host key) của máy chủ này đã thay đổi kể từ lần kết nối cuối cùng của bạn. Điều này có thể cho thấy một vấn đề về bảo mật.", - "previousKey": "Phím trước đó", - "newFingerprint": "Dấu vân tay mới", - "fingerprint": "Dấu vân tay", - "verifyInstructions": "Nếu bạn tin tưởng máy chủ này, hãy nhấp vào Chấp nhận để tiếp tục và lưu dấu vân tay này cho các kết nối trong tương lai.", - "securityWarning": "Cảnh báo an ninh", - "acceptAndContinue": "Chấp nhận và tiếp tục", - "acceptNewKey": "Chấp nhận khóa mới và tiếp tục" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "Không thể kết nối với cơ sở dữ liệu", - "unknownError": "Lỗi không xác định", - "loginFailed": "Đăng nhập thất bại", - "failedPasswordReset": "Không thể bắt đầu quá trình đặt lại mật khẩu.", - "failedVerifyCode": "Không thể xác minh mã đặt lại.", - "failedCompleteReset": "Không thể hoàn tất quá trình đặt lại mật khẩu.", - "invalidTotpCode": "Mã TOTP không hợp lệ", - "failedOidcLogin": "Không thể bắt đầu đăng nhập OIDC.", - "silentSigninOidcUnavailable": "Đã yêu cầu đăng nhập im lặng, nhưng tính năng đăng nhập OIDC không khả dụng.", - "failedUserInfo": "Không thể lấy thông tin người dùng sau khi đăng nhập.", - "oidcAuthFailed": "Xác thực OIDC thất bại", - "invalidAuthUrl": "URL xác thực không hợp lệ được nhận từ máy chủ phụ trợ.", - "requiredField": "Trường này là bắt buộc", - "minLength": "Độ dài tối thiểu là {{min}}", - "passwordMismatch": "Mật khẩu không khớp", - "passwordLoginDisabled": "Chức năng đăng nhập bằng tên người dùng/mật khẩu hiện đang bị vô hiệu hóa.", - "sessionExpired": "Phiên đăng nhập đã hết hạn - vui lòng đăng nhập lại.", - "totpRateLimited": "Tốc độ xác thực bị giới hạn: Quá nhiều lần thử xác thực TOTP. Vui lòng thử lại sau.", - "totpRateLimitedWithTime": "Tốc độ bị giới hạn: Quá nhiều lần thử xác thực TOTP. Vui lòng đợi {{time}} giây trước khi thử lại.", - "resetCodeRateLimited": "Tốc độ xác thực bị hạn chế: Quá nhiều lần xác thực. Vui lòng thử lại sau.", - "resetCodeRateLimitedWithTime": "Tốc độ xác thực bị giới hạn: Quá nhiều lần xác thực. Vui lòng đợi {{time}} giây trước khi thử lại.", - "authTokenSaveFailed": "Không thể lưu mã xác thực.", - "failedToLoadServer": "Không thể tải máy chủ" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "Hiện tại, việc đăng ký tài khoản mới đã bị quản trị viên vô hiệu hóa. Vui lòng đăng nhập hoặc liên hệ với quản trị viên.", - "userNotAllowed": "Tài khoản của bạn không được phép đăng ký. Vui lòng liên hệ với quản trị viên.", - "databaseConnectionFailed": "Không thể kết nối đến máy chủ cơ sở dữ liệu.", - "resetCodeSent": "Mã đặt lại đã được gửi đến nhật ký Docker.", - "codeVerified": "Mã đã được xác minh thành công.", - "passwordResetSuccess": "Đặt lại mật khẩu thành công", - "loginSuccess": "Đăng nhập thành công", - "registrationSuccess": "Đăng ký thành công" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "Các đường hầm SSH cục bộ trên máy tính để bàn hướng đến các máy chủ SSH đã được cấu hình.", - "c2sTunnelPresets": "Cài đặt sẵn đường hầm máy khách", - "c2sTunnelPresetsDesc": "Lưu danh sách đường hầm cục bộ của ứng dụng máy tính để bàn này dưới dạng thiết lập máy chủ có tên, hoặc tải lại thiết lập đó vào ứng dụng này.", - "c2sTunnelPresetsUnavailable": "Các thiết lập đường hầm dành cho máy khách chỉ khả dụng trong ứng dụng dành cho máy tính để bàn.", - "c2sPresetName": "Tên cài đặt sẵn", - "c2sPresetNamePlaceholder": "Tên cài đặt sẵn của khách hàng", - "c2sPresetToLoad": "Cài đặt sẵn để tải", - "c2sNoPresetSelected": "Chưa có cài đặt sẵn nào được chọn.", - "c2sNoPresets": "Không có thiết lập nào được lưu.", - "c2sLoadPreset": "Trọng tải", - "c2sCurrentLocalConfig": "{{count}} Đường hầm máy khách cục bộ được cấu hình trên máy tính để bàn này.", - "c2sPresetSyncNote": "Các thiết lập sẵn là các ảnh chụp nhanh cụ thể; việc tải một thiết lập sẽ thay thế danh sách đường hầm máy khách cục bộ của ứng dụng máy tính để bàn này.", - "c2sPresetSaved": "Đã lưu cài đặt đường hầm máy khách", - "c2sPresetLoaded": "Cài đặt đường hầm máy khách được tải cục bộ", - "c2sPresetRenamed": "Đặt lại cài đặt sẵn đường hầm máy khách", - "c2sPresetDeleted": "Đã xóa cài đặt sẵn đường hầm máy khách", - "c2sPresetLoadError": "Không thể tải các thiết lập đường hầm máy khách." + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "Ngôn ngữ", - "keyPassword": "mật khẩu khóa", - "pastePrivateKey": "Dán khóa riêng tư của bạn vào đây...", - "localListenerHost": "127.0.0.1 (nghe cục bộ)", - "localTargetHost": "127.0.0.1 (địa chỉ IP mục tiêu trên máy tính này)", - "socksListenerHost": "127.0.0.1 (Trình lắng nghe SOCKS)", - "enterPassword": "Nhập mật khẩu của bạn", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "Bảng điều khiển", - "loading": "Đang tải bảng điều khiển...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "Ủng hộ", + "support": "Support", "discord": "Discord", - "serverOverview": "Tổng quan về máy chủ", - "version": "Phiên bản", - "upToDate": "Đã cập nhật", - "updateAvailable": "Đã có bản cập nhật", - "beta": "Phiên bản Beta", - "uptime": "Thời gian hoạt động", - "database": "Cơ sở dữ liệu", - "healthy": "Khỏe mạnh", - "error": "Lỗi", - "totalHosts": "Tổng số máy chủ", - "totalTunnels": "Tổng số đường hầm", - "totalCredentials": "Tổng số chứng chỉ", - "recentActivity": "Hoạt động gần đây", - "reset": "Cài lại", - "loadingRecentActivity": "Đang tải hoạt động gần đây...", - "noRecentActivity": "Không có hoạt động gần đây", - "quickActions": "Thao tác nhanh", - "addHost": "Thêm máy chủ", - "addCredential": "Thêm thông tin đăng nhập", - "adminSettings": "Cài đặt quản trị", - "userProfile": "Hồ sơ người dùng", - "serverStats": "Thống kê máy chủ", - "loadingServerStats": "Đang tải thống kê máy chủ...", - "noServerData": "Không có dữ liệu máy chủ nào khả dụng", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", - "ram": "ĐẬP", - "customizeLayout": "Tùy chỉnh bảng điều khiển", - "dashboardSettings": "Cài đặt bảng điều khiển", - "enableDisableCards": "Kích hoạt/Vô hiệu hóa thẻ", - "resetLayout": "Khôi phục về mặc định", - "serverOverviewCard": "Tổng quan về máy chủ", - "recentActivityCard": "Hoạt động gần đây", - "networkGraphCard": "Đồ thị mạng", - "networkGraph": "Đồ thị mạng", - "quickActionsCard": "Thao tác nhanh", - "serverStatsCard": "Thống kê máy chủ", - "panelMain": "Chủ yếu", - "panelSide": "Bên", - "justNow": "vừa nãy" + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "ỔN ĐỊNH", + "stable": "STABLE", "hostsOnline": "Hosts Online", - "activeTunnels": "Đường hầm đang hoạt động", - "registerNewServer": "Đăng ký máy chủ mới", - "storeSshKeysOrPasswords": "Lưu trữ khóa SSH hoặc mật khẩu", - "manageUsersAndRoles": "Quản lý người dùng và vai trò", - "manageYourAccount": "Quản lý tài khoản của bạn", - "hostStatus": "Trạng thái máy chủ", - "noHostsConfigured": "Không có máy chủ nào được cấu hình", - "online": "TRỰC TUYẾN", - "offline": "NGOẠI TUYẾN", - "onlineLower": "Trực tuyến", - "nodes": "{{count}} nút", - "add": "Thêm vào:", - "commandPalette": "Bảng lệnh", - "done": "Xong", - "editModeInstructions": "Kéo các thẻ để sắp xếp lại thứ tự · Kéo đường phân cách cột để thay đổi kích thước cột · Kéo cạnh dưới của thẻ để thay đổi chiều cao · Biểu tượng thùng rác để xóa", - "empty": "Trống", - "clear": "Thông thoáng" + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "Thêm máy chủ", - "addGroup": "Thêm nhóm", - "addLink": "Thêm liên kết", - "zoomIn": "Phóng to", - "zoomOut": "Thu nhỏ", - "resetView": "Đặt lại chế độ xem", - "selectHost": "Chọn máy chủ", - "chooseHost": "Chọn một máy chủ...", - "parentGroup": "Nhóm phụ huynh", - "noGroup": "Không có nhóm", - "groupName": "Tên nhóm", - "color": "Màu sắc", - "source": "Nguồn", - "target": "Mục tiêu", - "moveToGroup": "Chuyển sang Nhóm", - "selectGroup": "Chọn nhóm...", - "addConnection": "Thêm kết nối", - "hostDetails": "Thông tin chủ nhà", - "removeFromGroup": "Xóa khỏi nhóm", - "addHostHere": "Thêm máy chủ tại đây", - "editGroup": "Chỉnh sửa nhóm", - "delete": "Xóa bỏ", - "add": "Thêm vào", - "create": "Tạo nên", - "move": "Di chuyển", - "connect": "Kết nối", - "createGroup": "Tạo nhóm", - "selectSourcePlaceholder": "Chọn nguồn...", - "selectTargetPlaceholder": "Chọn mục tiêu...", - "invalidFile": "Tệp không hợp lệ", - "hostAlreadyExists": "Máy chủ đã có trong sơ đồ mạng.", - "connectionExists": "Kết nối đã tồn tại", - "unknown": "Không rõ", - "name": "Tên", - "ip": "Địa chỉ IP", - "status": "Trạng thái", - "failedToAddNode": "Thêm nút không thành công", - "sourceDifferentFromTarget": "Nguồn và đích phải khác nhau", - "exportJSON": "Xuất JSON", - "importJSON": "Nhập JSON", - "terminal": "Phần cuối", - "fileManager": "Trình quản lý tập tin", - "tunnel": "Đường hầm", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", + "ip": "IP", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "Thống kê máy chủ", - "noNodes": "Chưa có nút nào" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "Docker chưa được kích hoạt trên máy chủ này.", - "validating": "Đang xác thực Docker...", - "connecting": "Đang kết nối...", - "error": "Lỗi", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "Không thể kết nối với Docker.", - "containerStarted": "Container {{name}} đã khởi động", - "failedToStartContainer": "Không thể khởi động container {{name}}", - "containerStopped": "Container {{name}} đã dừng", - "failedToStopContainer": "Không thể dừng container {{name}}", - "containerRestarted": "Container {{name}} đã khởi động lại", - "failedToRestartContainer": "Không thể khởi động lại container {{name}}", - "containerPaused": "Container {{name}} tạm dừng", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", "containerUnpaused": "Container {{name}} unpaused", - "failedToTogglePauseContainer": "Không thể chuyển đổi trạng thái tạm dừng cho vùng chứa {{name}}", - "containerRemoved": "Container {{name}} đã bị xóa", - "failedToRemoveContainer": "Không thể xóa container {{name}}", - "image": "Hình ảnh", - "ports": "Cảng", - "noPorts": "Không có cổng", - "start": "Bắt đầu", - "confirmRemoveContainer": "Bạn có chắc chắn muốn xóa vùng chứa '{{name}}' không? Hành động này không thể hoàn tác.", - "runningContainerWarning": "Cảnh báo: Container này hiện đang chạy. Việc xóa container này sẽ dừng container trước khi thực thi.", - "loadingContainers": "Đang xếp hàng vào container...", - "manager": "Trình quản lý Docker", - "autoRefresh": "Tự động làm mới", - "timestamps": "Dấu thời gian", - "lines": "Dòng", - "filterLogs": "Lọc nhật ký...", - "refresh": "Làm cho khỏe lại", - "download": "Tải xuống", - "clear": "Thông thoáng", - "logsDownloaded": "Nhật ký đã được tải xuống thành công", - "last50": "50 cuối cùng", - "last100": "100 cuối cùng", - "last500": "500 cuối cùng", - "last1000": "1000 cuối cùng", - "allLogs": "Tất cả nhật ký", - "noLogsMatching": "Không tìm thấy nhật ký nào khớp với \"{{query}}\"", - "noLogsAvailable": "Không có nhật ký nào.", - "noContainersFound": "Không tìm thấy thùng chứa nào", - "noContainersFoundHint": "Hiện không có container Docker nào khả dụng trên máy chủ này.", - "searchPlaceholder": "Tìm kiếm container...", - "allStatuses": "Tất cả trạng thái", - "stateRunning": "Đang chạy", - "statePaused": "Tạm dừng", - "stateExited": "Đã thoát", - "stateRestarting": "Khởi động lại", - "noContainersMatchFilters": "Không có hộp đựng nào phù hợp với bộ lọc của bạn.", - "noContainersMatchFiltersHint": "Hãy thử điều chỉnh tiêu chí tìm kiếm hoặc lọc của bạn.", - "failedToFetchStats": "Không thể lấy số liệu thống kê vùng chứa", - "containerNotRunning": "Container không chạy", - "startContainerToViewStats": "Khởi động container để xem số liệu thống kê.", - "loadingStats": "Đang tải số liệu thống kê...", - "errorLoadingStats": "Lỗi khi tải số liệu thống kê", - "noStatsAvailable": "Không có số liệu thống kê nào.", - "cpuUsage": "Mức sử dụng CPU", - "current": "Hiện hành", - "memoryUsage": "Mức sử dụng bộ nhớ", - "networkIo": "I/O mạng", - "input": "Đầu vào", - "output": "Đầu ra", - "blockIo": "Nhập/Xuất khối", - "read": "Đọc", - "write": "Viết", - "pids": "PID", - "containerInformation": "Thông tin container", - "name": "Tên", - "id": "NHẬN DẠNG", - "state": "Tình trạng", - "containerMustBeRunning": "Container phải đang chạy để truy cập bảng điều khiển.", - "verificationCodePrompt": "Nhập mã xác minh", - "totpVerificationFailed": "Xác thực TOTP không thành công. Vui lòng thử lại.", - "warpgateVerificationFailed": "Xác thực Warpgate thất bại. Vui lòng thử lại.", - "connectedTo": "Đã kết nối với {{containerName}}", - "disconnected": "Đã ngắt kết nối", - "consoleError": "Lỗi bảng điều khiển", - "errorMessage": "Lỗi: {{message}}", - "failedToConnect": "Không thể kết nối với container", - "console": "Bảng điều khiển", - "selectShell": "Chọn vỏ", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", + "id": "ID", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", - "ash": "tro", - "connect": "Kết nối", - "disconnect": "Ngắt kết nối", - "notConnected": "Không kết nối", - "clickToConnect": "Nhấp vào \"Kết nối\" để bắt đầu phiên shell.", - "connectingTo": "Đang kết nối với {{containerName}}...", - "containerNotFound": "Không tìm thấy container", - "backToList": "Trở lại danh sách", - "logs": "Nhật ký", - "stats": "Thống kê", - "consoleTab": "Bảng điều khiển", - "startContainerToAccess": "Khởi động container để truy cập bảng điều khiển." + "ash": "ash", + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "Tổng quan", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "Người dùng", - "sectionSessions": "Phiên họp", - "sectionRoles": "Vai trò", - "sectionDatabase": "Cơ sở dữ liệu", - "sectionApiKeys": "Khóa API", - "allowRegistration": "Cho phép đăng ký người dùng", - "allowRegistrationDesc": "Cho phép người dùng mới tự đăng ký", - "allowPasswordLogin": "Cho phép đăng nhập bằng mật khẩu", - "allowPasswordLoginDesc": "Đăng nhập bằng tên người dùng/mật khẩu", - "oidcAutoProvision": "Tự động cung cấp OIDC", - "oidcAutoProvisionDesc": "Tự động tạo tài khoản cho người dùng OIDC ngay cả khi tính năng đăng ký bị vô hiệu hóa.", - "allowPasswordReset": "Cho phép đặt lại mật khẩu", - "allowPasswordResetDesc": "Đặt lại mã thông qua nhật ký Docker", - "sessionTimeout": "Hết thời gian phiên", - "hours": "giờ", - "sessionTimeoutRange": "Tối thiểu 1 giờ · Tối đa 720 giờ", - "monitoringDefaults": "Giám sát các thiết lập mặc định", - "statusCheck": "Kiểm tra trạng thái", - "metrics": "Số liệu", - "sec": "giây", - "logLevel": "Mức độ ghi nhật ký", - "enableGuacamole": "Bật Guacamole", - "enableGuacamoleDesc": "Máy tính từ xa RDP/VNC", - "guacdUrl": "URL guacd", - "oidcDescription": "Cấu hình OpenID Connect cho SSO. Các trường được đánh dấu * là bắt buộc.", - "oidcClientId": "Mã khách hàng", - "oidcClientSecret": "Bí mật khách hàng", - "oidcAuthUrl": "URL ủy quyền", - "oidcIssuerUrl": "URL của tổ chức phát hành", - "oidcTokenUrl": "URL mã thông báo", - "oidcUserIdentifier": "Đường dẫn định danh người dùng", - "oidcDisplayName": "Đường dẫn tên hiển thị", - "oidcScopes": "Phạm vi", - "oidcUserinfoUrl": "Ghi đè URL thông tin người dùng", - "oidcAllowedUsers": "Người dùng được phép", - "oidcAllowedUsersDesc": "Một địa chỉ email trên mỗi dòng. Để trống nếu muốn hiển thị tất cả.", - "removeOidc": "Di dời", - "usersCount": "{{count}} người dùng", - "createUser": "Tạo nên", - "newRole": "Vai trò mới", - "roleName": "Tên", - "roleDisplayName": "Tên hiển thị", - "roleDescription": "Sự miêu tả", - "rolesCount": "{{count}} vai trò", - "createRole": "Tạo nên", - "creating": "Đang tạo...", - "exportDatabase": "Xuất cơ sở dữ liệu", - "exportDatabaseDesc": "Tải xuống bản sao lưu tất cả máy chủ, thông tin đăng nhập và cài đặt.", - "export": "Xuất khẩu", - "exporting": "Đang xuất khẩu...", - "importDatabase": "Nhập cơ sở dữ liệu", - "importDatabaseDesc": "Khôi phục từ tệp sao lưu .sqlite", - "importDatabaseSelected": "Đã chọn: {{name}}", - "selectFile": "Chọn tệp", - "changeFile": "Thay đổi", - "import": "Nhập khẩu", - "importing": "Đang nhập khẩu...", - "apiKeysCount": "{{count}} phím", - "newApiKey": "Khóa API mới", - "apiKeyCreatedWarning": "Mã khóa đã được tạo - hãy sao chép ngay, mã này sẽ không hiển thị lại nữa.", - "apiKeyName": "Tên", - "apiKeyUser": "Người dùng", - "apiKeySelectUser": "Chọn người dùng...", - "apiKeyExpiresAt": "Hết hạn vào lúc", - "createKey": "Tạo khóa", - "apiKeyNoExpiry": "Không hết hạn", - "revokedBadge": "ĐÃ THU HỒI", - "authTypeDual": "Xác thực kép", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", + "guacdUrl": "guacd URL", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "Địa phương", - "adminStatusAdministrator": "Quản trị viên", - "adminStatusRegularUser": "Người dùng thông thường", - "adminBadge": "QUẢN TRỊ VIÊN", - "systemBadge": "HỆ THỐNG", - "customBadge": "PHONG TỤC", - "youBadge": "BẠN", - "sessionsActive": "{{count}} đang hoạt động", - "sessionActive": "Đang hoạt động: {{time}}", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "Tất cả", - "revokeAllSessionsSuccess": "Tất cả các phiên đăng nhập của người dùng đã bị thu hồi.", - "revokeAllSessionsFailed": "Không thể hủy bỏ các phiên", - "revokeSessionFailed": "Không thể hủy phiên", - "addRole": "Thêm vai trò", - "noCustomRoles": "Không có vai trò tùy chỉnh nào được định nghĩa.", - "removeRoleFailed": "Không thể xóa vai trò", - "assignRoleFailed": "Không thể gán vai trò", - "deleteRoleFailed": "Không thể xóa vai trò", - "userAdminAccess": "Quản trị viên", - "userAdminAccessDesc": "Toàn quyền truy cập vào tất cả các thiết lập quản trị.", - "userRoles": "Vai trò", - "revokeAllUserSessions": "Hủy bỏ tất cả các phiên", - "revokeAllUserSessionsDesc": "Buộc đăng nhập lại trên tất cả các thiết bị.", - "revoke": "Thu hồi", - "deleteUserWarning": "Việc xóa người dùng này là vĩnh viễn.", - "deleteUser": "Xóa {{username}}", - "deleting": "Đang xóa...", - "deleteUserFailed": "Không thể xóa người dùng.", - "deleteUserSuccess": "Người dùng \"{{username}}\" đã bị xóa", - "deleteRoleSuccess": "Vai trò \"{{name}}\" đã bị xóa", - "revokeKeySuccess": "Khóa \"{{name}}\" đã bị thu hồi", - "revokeKeyFailed": "Không thể thu hồi khóa.", - "copiedToClipboard": "Đã sao chép vào clipboard", - "done": "Xong", - "createUserTitle": "Tạo người dùng", - "createUserDesc": "Tạo tài khoản cục bộ mới.", - "createUserUsername": "Tên người dùng", - "createUserPassword": "Mật khẩu", - "createUserPasswordHint": "Tối thiểu 6 ký tự.", - "createUserEnterUsername": "Nhập tên người dùng", - "createUserEnterPassword": "Nhập mật khẩu", - "createUserSubmit": "Tạo người dùng", - "editUserTitle": "Quản lý người dùng: {{username}}", - "editUserDesc": "Chỉnh sửa vai trò, trạng thái quản trị viên, phiên làm việc và cài đặt tài khoản.", - "editUserUsername": "Tên người dùng", - "editUserAuthType": "Loại xác thực", - "editUserAdminStatus": "Trạng thái quản trị viên", - "editUserUserId": "ID người dùng", - "linkAccountTitle": "Liên kết OIDC với tài khoản mật khẩu", - "linkAccountDesc": "Hợp nhất tài khoản OIDC {{username}} với tài khoản cục bộ hiện có.", - "linkAccountWarningTitle": "Điều này sẽ:", - "linkAccountEffect1": "Xóa tài khoản chỉ dành cho OIDC", - "linkAccountEffect2": "Thêm thông tin đăng nhập OIDC vào tài khoản mục tiêu.", - "linkAccountEffect3": "Cho phép cả đăng nhập bằng OIDC và mật khẩu.", - "linkAccountTargetUsername": "Tên người dùng mục tiêu", - "linkAccountTargetPlaceholder": "Nhập tên người dùng tài khoản cục bộ cần liên kết.", - "linkAccounts": "Liên kết tài khoản", - "linkAccountSuccess": "Tài khoản OIDC được liên kết với \"{{username}}\"", - "linkAccountFailed": "Không thể liên kết tài khoản OIDC.", - "linkAccountInProgress": "Đang liên kết...", - "saving": "Đang lưu...", - "updateRegistrationFailed": "Không thể cập nhật cài đặt đăng ký.", - "updatePasswordLoginFailed": "Không thể cập nhật cài đặt mật khẩu đăng nhập.", - "updateOidcAutoProvisionFailed": "Không thể cập nhật cài đặt tự động cấp phép OIDC.", - "updatePasswordResetFailed": "Không thể cập nhật cài đặt đặt lại mật khẩu.", - "sessionTimeoutRange2": "Thời gian chờ phiên phải nằm trong khoảng từ 1 đến 720 giờ.", - "sessionTimeoutSaved": "Thời gian chờ phiên đã được lưu", - "sessionTimeoutSaveFailed": "Không thể lưu thời gian chờ phiên.", - "monitoringIntervalInvalid": "Giá trị khoảng thời gian không hợp lệ", - "monitoringSaved": "Cài đặt giám sát đã được lưu", - "monitoringSaveFailed": "Không thể lưu cài đặt giám sát.", - "guacamoleSaved": "Cài đặt guacamole đã được lưu", - "guacamoleSaveFailed": "Không thể lưu cài đặt Guacamole.", - "guacamoleUpdateFailed": "Không thể cập nhật cài đặt Guacamole.", - "logLevelUpdateFailed": "Không thể cập nhật mức độ ghi nhật ký", - "oidcSaved": "Cấu hình OIDC đã được lưu.", - "oidcSaveFailed": "Không thể lưu cấu hình OIDC.", - "oidcRemoved": "Cấu hình OIDC đã bị xóa", - "oidcRemoveFailed": "Không thể xóa cấu hình OIDC.", - "createUserRequired": "Tên người dùng và mật khẩu là bắt buộc.", - "createUserPasswordTooShort": "Mật khẩu phải có ít nhất 6 ký tự.", - "createUserSuccess": "Người dùng \"{{username}}\" đã tạo", - "createUserFailed": "Không thể tạo người dùng.", - "updateAdminStatusFailed": "Không thể cập nhật trạng thái quản trị viên.", - "allSessionsRevoked": "Tất cả các phiên đã bị hủy bỏ", - "revokeSessionsFailed": "Không thể hủy bỏ các phiên", - "createRoleRequired": "Tên và tên hiển thị là bắt buộc", - "createRoleSuccess": "Vai trò \"{{name}}\" đã được tạo", - "createRoleFailed": "Không thể tạo vai trò", - "apiKeyNameRequired": "Tên khóa là bắt buộc", - "apiKeyUserRequired": "Cần có ID người dùng.", - "apiKeyCreatedSuccess": "Khóa API \"{{name}}\" đã được tạo", - "apiKeyCreateFailed": "Không thể tạo khóa API", - "exportSuccess": "Cơ sở dữ liệu đã được xuất thành công.", - "exportFailed": "Xuất cơ sở dữ liệu thất bại", - "importSelectFile": "Vui lòng chọn tệp trước", - "importCompleted": "Quá trình nhập khẩu hoàn tất: {{total}} mục đã được nhập, {{skipped}} mục bị bỏ qua", - "importFailed": "Nhập khẩu thất bại: {{error}}", - "importError": "Nhập dữ liệu cơ sở dữ liệu thất bại" + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "Chủ nhà", - "hostPlaceholder": "192.168.1.1 hoặc example.com", - "portLabel": "Cảng", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "Tên người dùng", - "usernamePlaceholder": "tên người dùng", - "authLabel": "Xác thực", - "passwordLabel": "Mật khẩu", - "passwordPlaceholder": "mật khẩu", - "privateKeyLabel": "Khóa riêng tư", - "privateKeyPlaceholder": "Dán khóa riêng tư...", - "credentialLabel": "Chứng chỉ", - "credentialPlaceholder": "Chọn thông tin đăng nhập đã lưu", - "connectToTerminal": "Kết nối với thiết bị đầu cuối", - "connectToFiles": "Kết nối với các tệp" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "Chưa chọn thiết bị đầu cuối nào", - "noTerminalSelectedHint": "Mở một tab cửa sổ dòng lệnh SSH để xem lịch sử lệnh của nó.", - "searchPlaceholder": "Lịch sử tìm kiếm...", - "clearAll": "Xóa tất cả", - "noHistoryEntries": "Không có mục lịch sử nào", - "trackingDisabled": "Tính năng theo dõi lịch sử đã bị vô hiệu hóa.", - "trackingDisabledHint": "Hãy bật tính năng này trong cài đặt hồ sơ của bạn để ghi lại các lệnh." + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "Ghi âm chính", - "recordToTerminals": "Ghi vào thiết bị đầu cuối", - "selectAll": "Tất cả", - "selectNone": "Không có", - "noTerminalTabsOpen": "Không có tab terminal nào đang mở.", - "selectTerminalsAbove": "Chọn các thiết bị đầu cuối ở trên", - "broadcastInputPlaceholder": "Nhập vào đây để phát sóng các thao tác gõ phím...", - "stopRecording": "Dừng ghi âm", - "startRecording": "Bắt đầu ghi âm", - "settingsTitle": "Cài đặt", - "enableRightClickCopyPaste": "Cho phép sao chép/dán bằng chuột phải." + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "Cách trình bày", - "selectLayoutAbove": "Chọn bố cục ở trên", - "selectLayoutHint": "Chọn số lượng khung hiển thị", - "panesTitle": "Cửa kính", - "openTabsTitle": "Các tab đang mở", - "dragTabsHint": "Kéo các tab vào các khung phía trên hoặc sử dụng chức năng Gán nhanh.", - "dropHere": "Thả xuống đây", - "emptyPane": "Trống", - "dashboard": "Bảng điều khiển", - "clearSplitScreen": "Xóa màn hình chia đôi", - "quickAssign": "Phân công nhanh", + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", "alreadyAssigned": "Pane {{index}}", - "splitTab": "Chia tab", - "addToSplit": "Thêm vào phần chia", - "removeFromSplit": "Xóa khỏi phần Chia", - "assignToPane": "Gán vào ngăn" + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "Tạo đoạn mã", - "createSnippetDescription": "Tạo một đoạn mã lệnh mới để thực thi nhanh.", - "nameLabel": "Tên", - "namePlaceholder": "Ví dụ: Khởi động lại Nginx", - "descriptionLabel": "Sự miêu tả", - "descriptionPlaceholder": "Mô tả tùy chọn", - "optional": "Không bắt buộc", - "folderLabel": "Thư mục", - "noFolder": "Không có thư mục (Chưa được phân loại)", - "commandLabel": "Yêu cầu", - "commandPlaceholder": "Ví dụ: sudo systemctl restart nginx", - "cancel": "Hủy bỏ", - "createSnippetButton": "Tạo đoạn mã", - "createFolderTitle": "Tạo thư mục", - "createFolderDescription": "Sắp xếp các đoạn văn bản của bạn vào các thư mục.", - "folderNameLabel": "Tên thư mục", - "folderNamePlaceholder": "Ví dụ: Lệnh hệ thống, tập lệnh Docker", - "folderColorLabel": "Màu thư mục", - "folderIconLabel": "Biểu tượng thư mục", - "previewLabel": "Xem trước", - "folderNameFallback": "Tên thư mục", - "createFolderButton": "Tạo thư mục", - "targetTerminals": "Các thiết bị đầu cuối mục tiêu", - "selectAll": "Tất cả", - "selectNone": "Không có", - "noTerminalTabsOpen": "Không có tab terminal nào đang mở.", - "searchPlaceholder": "Đoạn trích kết quả tìm kiếm...", - "newSnippet": "Đoạn mã mới", - "newFolder": "Thư mục mới", - "run": "Chạy", - "noSnippetsInFolder": "Không có đoạn mã nào trong thư mục này.", - "uncategorized": "Chưa được phân loại", - "editSnippetTitle": "Chỉnh sửa đoạn mã", - "editSnippetDescription": "Cập nhật đoạn mã lệnh này", - "saveSnippetButton": "Lưu thay đổi", - "createSuccess": "Đoạn mã đã được tạo thành công", - "createFailed": "Không thể tạo đoạn mã", - "updateSuccess": "Đoạn mã đã được cập nhật thành công.", - "updateFailed": "Không thể cập nhật đoạn mã", - "deleteFailed": "Không thể xóa đoạn mã", - "folderCreateSuccess": "Thư mục đã được tạo thành công", - "folderCreateFailed": "Không thể tạo thư mục", - "editFolderTitle": "Chỉnh sửa thư mục", - "editFolderDescription": "Đổi tên hoặc thay đổi giao diện của thư mục này.", - "saveFolderButton": "Lưu thay đổi", - "editFolder": "Chỉnh sửa thư mục", - "deleteFolder": "Xóa thư mục", - "folderDeleteSuccess": "Thư mục \"{{name}}\" đã bị xóa", - "folderDeleteFailed": "Không thể xóa thư mục", - "folderEditSuccess": "Thư mục đã được cập nhật thành công.", - "folderEditFailed": "Không thể cập nhật thư mục.", - "confirmRunMessage": "Chạy \"{{name}}\"?", - "confirmRunButton": "Chạy", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", - "copySuccess": "Đã sao chép \"{{name}}\" vào clipboard", - "shareTitle": "Chia sẻ đoạn trích", - "shareUser": "Người dùng", - "shareRole": "Vai trò", - "selectUser": "Chọn người dùng...", - "selectRole": "Chọn một vai trò...", - "shareSuccess": "Đoạn mã đã được chia sẻ thành công", - "shareFailed": "Không thể chia sẻ đoạn trích", - "revokeSuccess": "Quyền truy cập đã bị thu hồi", - "revokeFailed": "Không thể thu hồi quyền truy cập", - "currentAccess": "Truy cập hiện tại", - "shareLoadError": "Không thể tải dữ liệu chia sẻ", - "loading": "Đang tải...", - "close": "Đóng", - "reorderFailed": "Không thể lưu thứ tự đoạn mã." + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "Tài khoản", - "sectionAppearance": "Vẻ bề ngoài", - "sectionSecurity": "Bảo vệ", - "sectionApiKeys": "Khóa API", - "sectionC2sTunnels": "Đường hầm C2S", - "usernameLabel": "Tên người dùng", - "roleLabel": "Vai trò", - "roleAdministrator": "Quản trị viên", - "authMethodLabel": "Phương thức xác thực", - "authMethodLocal": "Địa phương", - "twoFaLabel": "Xác thực hai yếu tố", - "twoFaOn": "TRÊN", - "twoFaOff": "Tắt", - "versionLabel": "Phiên bản", - "deleteAccount": "Xóa tài khoản", - "deleteAccountDescription": "Xóa vĩnh viễn tài khoản của bạn", - "deleteButton": "Xóa bỏ", - "deleteAccountPermanent": "Hành động này là vĩnh viễn và không thể đảo ngược.", - "deleteAccountWarning": "Tất cả các phiên, máy chủ, thông tin đăng nhập và cài đặt sẽ bị xóa vĩnh viễn.", - "confirmPasswordDeletePlaceholder": "Nhập mật khẩu của bạn để xác nhận", - "languageLabel": "Ngôn ngữ", - "themeLabel": "Chủ đề", - "fontSizeLabel": "Kích thước phông chữ", - "accentColorLabel": "Màu nhấn", - "settingsTerminal": "Phần cuối", - "commandAutocomplete": "Tự động hoàn thành lệnh", - "commandAutocompleteDesc": "Hiển thị tính năng tự động hoàn thành khi đang gõ", - "historyTracking": "Theo dõi lịch sử", - "historyTrackingDesc": "Theo dõi lệnh đầu cuối", - "syntaxHighlighting": "Tô sáng cú pháp", - "syntaxHighlightingDesc": "Đánh dấu đầu ra thiết bị đầu cuối", - "commandPalette": "Bảng lệnh", - "commandPaletteDesc": "Bật phím tắt", - "reopenTabsOnLogin": "Mở lại các tab khi đăng nhập", - "reopenTabsOnLoginDesc": "Khôi phục các tab đang mở khi bạn đăng nhập hoặc làm mới trang, ngay cả khi bạn đang sử dụng thiết bị khác.", - "confirmTabClose": "Xác nhận đóng tab", - "confirmTabCloseDesc": "Hãy hỏi trước khi đóng các tab cửa sổ terminal.", - "settingsSidebar": "Thanh bên", - "showHostTags": "Thẻ người dẫn chương trình", - "showHostTagsDesc": "Hiển thị thẻ trong danh sách máy chủ", - "hostTrayOnClick": "Nhấp chuột để mở rộng các hành động của máy chủ", - "hostTrayOnClickDesc": "Luôn hiển thị các nút kết nối; nhấp chuột để mở rộng các tùy chọn quản lý thay vì di chuột.", - "pinAppRail": "Ghim ứng dụng Rail", - "pinAppRailDesc": "Giữ cho thanh bên trái của ứng dụng luôn mở rộng thay vì chỉ mở rộng khi di chuột.", - "settingsSnippets": "Những đoạn trích", - "foldersCollapsed": "Các thư mục đã được thu gọn", - "foldersCollapsedDesc": "Thu gọn thư mục theo mặc định", - "confirmExecution": "Xác nhận thực thi", - "confirmExecutionDesc": "Xác nhận trước khi chạy các đoạn mã.", - "settingsUpdates": "Cập nhật", - "disableUpdateChecks": "Tắt kiểm tra cập nhật", - "disableUpdateChecksDesc": "Hãy ngừng kiểm tra cập nhật.", - "totpAuthenticator": "Bộ xác thực TOTP", - "totpEnabled": "Xác thực hai yếu tố (2FA) đã được kích hoạt.", - "totpDisabled": "Thêm lớp bảo mật đăng nhập", - "disable": "Vô hiệu hóa", - "enable": "Cho phép", - "setupTotp": "Thiết lập TOTP", - "qrCode": "Mã QR", - "totpInstructions": "Quét mã QR hoặc nhập mã bí mật vào ứng dụng xác thực của bạn, sau đó nhập mã 6 chữ số.", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", + "pinAppRail": "Pin App Rail", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "Xác minh", - "changePassword": "Thay đổi mật khẩu", - "currentPasswordLabel": "Mật khẩu hiện tại", - "currentPasswordPlaceholder": "Mật khẩu hiện tại", - "newPasswordLabel": "Mật khẩu mới", - "newPasswordPlaceholder": "Mật khẩu mới", - "confirmPasswordLabel": "Xác nhận mật khẩu mới", - "confirmPasswordPlaceholder": "Xác nhận mật khẩu mới", - "updatePassword": "Cập nhật mật khẩu", - "createApiKeyTitle": "Tạo khóa API", - "createApiKeyDescription": "Tạo khóa API mới để truy cập lập trình.", - "apiKeyNameLabel": "Tên", - "apiKeyNamePlaceholder": "ví dụ CI Pipeline", - "expiryDateLabel": "Ngày hết hạn", - "optional": "không bắt buộc", - "cancel": "Hủy bỏ", - "createKey": "Tạo khóa", - "apiKeyCount": "{{count}} phím", - "newKey": "Khóa mới", - "noApiKeys": "Chưa có khóa API.", - "apiKeyActive": "Tích cực", - "apiKeyUsageHint": "Bao gồm cả chìa khóa của bạn trong", - "apiKeyUsageHintHeader": "tiêu đề.", - "apiKeyPermissionsHint": "Các khóa kế thừa quyền hạn của người dùng tạo ra chúng.", - "roleUser": "Người dùng", - "authMethodDual": "Xác thực kép", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "Không thể bắt đầu thiết lập TOTP.", - "totpEnter6Digits": "Nhập mã 6 chữ số", - "totpEnabledSuccess": "Đã bật xác thực hai yếu tố", - "totpInvalidCode": "Mã không hợp lệ, vui lòng thử lại.", - "totpDisableInputRequired": "Nhập mã TOTP hoặc mật khẩu của bạn", - "totpDisabledSuccess": "Xác thực hai yếu tố đã bị vô hiệu hóa", - "totpDisableFailed": "Không thể tắt xác thực hai yếu tố (2FA).", - "totpDisableTitle": "Vô hiệu hóa xác thực hai yếu tố (2FA).", - "totpDisablePlaceholder": "Nhập mã TOTP hoặc mật khẩu", - "totpDisableConfirm": "Vô hiệu hóa xác thực hai yếu tố (2FA).", - "totpContinueVerify": "Tiếp tục xác minh", - "totpVerifyTitle": "Xác minh mã", - "totpBackupTitle": "Mã dự phòng", - "totpDownloadBackup": "Tải xuống mã sao lưu", - "done": "Xong", - "secretCopied": "Bí mật đã được sao chép vào clipboard", - "apiKeyNameRequired": "Tên khóa là bắt buộc", - "apiKeyCreated": "Khóa API \"{{name}}\" đã được tạo", - "apiKeyCreateFailed": "Không thể tạo khóa API", - "apiKeyUser": "Người dùng", - "apiKeyExpires": "Hết hạn", - "apiKeyRevoked": "Khóa API \"{{name}}\" đã bị thu hồi", - "apiKeyRevokeFailed": "Không thể thu hồi khóa API.", - "passwordFieldsRequired": "Cần có mật khẩu hiện tại và mật khẩu mới.", - "passwordMismatch": "Mật khẩu không khớp", - "passwordTooShort": "Mật khẩu phải có ít nhất 6 ký tự.", - "passwordUpdated": "Mật khẩu đã được cập nhật thành công.", - "passwordUpdateFailed": "Không thể cập nhật mật khẩu.", - "deletePasswordRequired": "Bạn cần nhập mật khẩu để xóa tài khoản của mình.", - "deleteFailed": "Không thể xóa tài khoản", - "deleting": "Đang xóa...", - "colorPickerTooltip": "Mở công cụ chọn màu", - "themeSystem": "Hệ thống", - "themeLight": "Ánh sáng", - "themeDark": "Tối tăm", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", - "themeNord": "Bắc", - "themeSolarized": "được xử lý bằng năng lượng mặt trời", - "themeTokyoNight": "Đêm Tokyo", - "themeOneDark": "Một Bóng Tối", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/zh_CN.json b/src/ui/locales/translated/zh_CN.json index db15fa4f..9bc5af32 100644 --- a/src/ui/locales/translated/zh_CN.json +++ b/src/ui/locales/translated/zh_CN.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "文件夹", - "folder": "文件夹", - "password": "密码", - "key": "密钥", - "sshPrivateKey": "SSH 私钥", - "upload": "上传", - "keyPassword": "密钥密码", - "sshKey": "SSH密钥", - "uploadPrivateKeyFile": "上传私钥文件", - "searchCredentials": "搜索凭证...", - "addCredential": "添加凭证", - "caCertificate": "CA证书 (-cert.pub)", - "caCertificateDescription": "可选:上传或粘贴CA签名的证书文件(例如id_ed25519-cert.pub)。当您的 SSH 服务器使用基于证书的授权时是必需的。", - "uploadCertFile": "上传 -cert.pub 文件", - "clearCert": "清空", - "certLoaded": "证书已加载", - "certPublicKeyLabel": "CA 证书", - "certTypeLabel": "证书类型", - "pasteOrUploadCert": "粘贴或上传一个 -cert.pub 证书...", - "hasCaCert": "有CA证书", - "noCaCert": "无CA证书" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "警报加载失败", - "failedToDismissAlert": "未能关闭警报" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "服务器配置", - "description": "配置 Termix 服务器 URL 以连接到您的后端服务", - "serverUrl": "服务器 URL", - "enterServerUrl": "请输入服务器URL", - "saveFailed": "配置保存失败", - "saveError": "保存配置时出错", - "saving": "保存中...", - "saveConfig": "保存配置", - "helpText": "输入您的 Termix 服务器运行所对应的 URL(例如,http://localhost:30001 或 https://your-server.com)", - "changeServer": "变更服务器", - "mustIncludeProtocol": "服务器 URL 必须以 http:// 或 https:// 开头。", - "allowInvalidCertificate": "允许无效证书", - "allowInvalidCertificateDesc": "仅适用于具有自签名证书或 IP 地址证书的受信任的自托管服务器。", - "useEmbedded": "使用本地服务器", - "embeddedDesc": "使用内置的本地服务器运行 Termixed (不需要远程服务器)", - "embeddedConnecting": "正在连接本地服务器...", - "embeddedNotReady": "本地服务器尚未准备就绪。请稍等,然后重试。", - "localServer": "本地服务器" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "版本检查错误", - "checkFailed": "检查更新失败", - "upToDate": "应用已更新至最新版本", - "currentVersion": "您正在运行版本 {{version}}", - "updateAvailable": "更新可用", - "newVersionAvailable": "新版本已发布!您当前运行的是 {{current}} 版本,但 {{latest}} 版本可用。", - "betaVersion": "测试版", - "betaVersionDesc": "您正在运行 {{current}},它比最新的稳定版本 {{latest}} 更新。", - "releasedOn": "发布日期:{{date}}", - "downloadUpdate": "下载更新", - "checking": "正在检查更新...", - "checkUpdates": "检查更新", - "checkingUpdates": "正在检查更新...", - "updateRequired": "需要更新" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "关闭", - "minimize": "最小化", - "online": "在线", - "offline": "离线", - "continue": "继续", - "maintenance": "维护", - "degraded": "降级", - "error": "错误", - "warning": "警告", - "unsavedChanges": "未保存的更改", - "dismiss": "关闭", - "loading": "加载中...", - "optional": "选修的", - "connect": "连接", - "copied": "已复制", - "connecting": "正在连接...", - "updateAvailable": "更新可用", - "appName": "Termixe", - "openInNewTab": "在新标签中打开", - "noReleases": "无发布", - "updatesAndReleases": "更新与发布", - "newVersionAvailable": "新版本({{version}})可用。", - "failedToFetchUpdateInfo": "获取更新信息失败", - "preRelease": "预发布", - "noReleasesFound": "未找到任何版本。", - "cancel": "取消", - "username": "用户名", - "login": "登录", - "register": "注册", - "password": "密码", - "confirmPassword": "确认密码", - "back": "返回", - "save": "保存", - "saving": "保存中...", - "delete": "删除", - "rename": "重命名:", - "edit": "编辑", - "add": "添加", - "confirm": "确认", - "no": "否", - "or": "或", - "next": "下一个", - "previous": "上一个", - "refresh": "刷新", - "language": "语言", - "checking": "检查...", - "checkingDatabase": "正在检查数据库连接...", - "checkingAuthentication": "正在检查认证...", - "backendReconnected": "服务器连接已恢复", - "connectionDegraded": "服务器连接丢失,正在恢复…", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", + "appName": "Termix", + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", "reload": "Reload", - "remove": "移除", - "create": "创建", - "update": "更新", - "copy": "复制", - "copyFailed": "复制到剪贴板失败", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", "maximize": "Maximize", - "restore": "恢复", - "of": "共" + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "首页", - "terminal": "终端", - "docker": "停靠栏", - "tunnels": "隧道", - "fileManager": "文件管理器", - "serverStats": "服务器统计信息", - "admin": "admin", - "userProfile": "用户配置", - "splitScreen": "分屏", - "confirmClose": "关闭此活动会话?", - "close": "关闭", - "cancel": "取消", - "sshManager": "SSH 管理器", - "cannotSplitTab": "无法拆分此选项卡", + "home": "Home", + "terminal": "Terminal", + "docker": "Docker", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "复制密码", - "copySudoPassword": "复制Sudo密码", - "passwordCopied": "密码已复制到剪贴板", - "noPasswordAvailable": "没有可用密码", - "failedToCopyPassword": "无法复制密码", - "refreshTab": "刷新连接", - "openFileManager": "打开文件管理器", - "dashboard": "仪表板", - "networkGraph": "网络图", - "quickConnect": "快速连接", - "sshTools": "SSH 工具", - "history": "历史记录", - "hosts": "主机", - "snippets": "片段", - "hostManager": "主机管理器", - "credentials": "凭证", - "connections": "连接", - "roleAdministrator": "管理员", - "roleUser": "用户" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "主机", - "noHosts": "无 SSH 主机", - "retry": "重试", - "refresh": "刷新", - "optional": "可选的", - "downloadSample": "下载示例", - "failedToDeleteHost": "删除失败 {{name}}", - "importSkipExisting": "导入(跳过存在)", - "connectionDetails": "连接详情", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "远程桌面", - "port": "端口", - "username": "用户名", - "folder": "文件夹", - "tags": "标签", - "pin": "置顶", - "addHost": "添加主机", - "editHost": "编辑主机", - "cloneHost": "克隆主机", - "enableTerminal": "启用终端", - "enableTunnel": "启用隧道", - "enableFileManager": "启用文件管理器", - "enableDocker": "启用 Docker", - "defaultPath": "默认路径", - "connection": "连接", - "upload": "上传", - "authentication": "验证", - "password": "密码", - "key": "密钥", - "credential": "凭证", - "none": "无", - "sshPrivateKey": "SSH 私钥", - "keyType": "密钥类型", - "uploadFile": "上传文件", - "tabGeneral": "A. 概况", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "隧道设置", - "tabDocker": "停靠栏", - "tabFiles": "文件", - "tabStats": "统计信息", + "tabTunnels": "Tunnels", + "tabDocker": "Docker", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "分享", - "tabAuthentication": "认证", - "terminal": "终端", - "tunnel": "隧道", - "fileManager": "文件管理器", - "serverStats": "服务器统计信息", - "status": "状态", - "folderRenamed": "文件夹“{{oldName}}”已成功重命名为“{{newName}}”。", - "failedToRenameFolder": "重命名文件夹失败", - "movedToFolder": "已移至“{{folder}}”", - "editHostTooltip": "编辑主机", - "statusChecks": "状态检查", - "metricsCollection": "计量收藏", - "metricsInterval": "指标收集间隔", - "metricsIntervalDesc": "服务器统计信息收集频率(5秒-1小时)", - "behavior": "行为", - "themePreview": "主题预览", - "theme": "主题", - "fontFamily": "字体系列", - "fontSize": "字体大小", - "letterSpacing": "字母间距", - "lineHeight": "行高", - "cursorStyle": "光标样式", - "cursorBlink": "光标闪烁", - "scrollbackBuffer": "回滚缓冲区", - "bellStyle": "提示音样式", - "rightClickSelectsWord": "右键单击选中单词", - "fastScrollModifier": "快速滚动修饰键", - "fastScrollSensitivity": "快速滚动灵敏度", - "sshAgentForwarding": "SSH代理转发", - "backspaceMode": "退格键模式", - "startupSnippet": "启动代码片段", - "selectSnippet": "选择代码片段", - "forceKeyboardInteractive": "强制键盘交互", - "overrideCredentialUsername": "覆盖凭证用户名", - "overrideCredentialUsernameDesc": "使用上面指定的用户名而不是凭据的用户名", - "jumpHostChain": "跳转宿主链", - "portKnocking": "端口敲击中", - "addKnock": "添加端口", - "addProxyNode": "添加节点", - "proxyNode": "代理节点", - "proxyType": "代理类型", - "quickActions": "快速操作", - "sudoPasswordAutoFill": "Sudo 密码自动填充", - "sudoPassword": "Sudo 密码", - "keepaliveInterval": "保持活动间隔 (毫秒)", - "moshCommand": "MOSH 命令", - "environmentVariables": "环境变量", - "addVariable": "添加变量", - "docker": "停靠栏", - "copyTerminalUrl": "复制终端 URL", - "copyFileManagerUrl": "复制文件管理器 URL", - "copyRemoteDesktopUrl": "复制远程桌面 URL", - "failedToConnect": "连接控制台失败", - "connect": "连接", - "disconnect": "断开", - "start": "开始", - "enableStatusCheck": "启用状态检查", - "enableMetrics": "启用指标", - "bulkUpdateFailed": "批量更新失败", - "selectAll": "选择所有", - "deselectAll": "取消全选", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", + "docker": "Docker", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", "protocols": "Protocols", - "secureShell": "安全的 Shell", - "virtualNetwork": "虚拟网络", - "unencryptedShell": "未加密的 shell", - "addressIp": "地址 / IP", - "friendlyName": "友好名称", - "folderAndAdvanced": "文件夹和高级版", - "privateNotes": "私人笔记", - "privateNotesPlaceholder": "关于此服务器的详细信息...", - "pinToTop": "固定到顶端", - "pinToTopDesc": "总是在列表顶部显示此主机", - "portKnockingSequence": "端口撞击序列", - "addKnockBtn": "添加垃圾箱", - "noPortKnocking": "未配置端口跟踪。", - "knockPort": "敲击端口", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", "protocol": "Protocol", - "delayAfterMs": "延迟(毫秒)", - "useSocks5Proxy": "使用 SOCKS5 代理", - "useSocks5ProxyDesc": "通过代理服务器路由连接", - "proxyHost": "代理主机", - "proxyPort": "代理端口", - "proxyUsername": "代理用户名", - "proxyPassword": "代理密码", - "proxySingleMode": "单个代理", - "proxyChainMode": "代理链接", - "you": "你", - "jumpHostChainLabel": "跳转主机链接", - "addJumpBtn": "添加跳转", - "noJumpHosts": "未配置跳跃主机.", - "selectAServer": "选择服务器...", - "sshPort": "SSH 端口", - "authMethod": "认证方法", - "storedCredential": "保存的凭据", - "selectACredential": "选择凭据...", - "keyTypeLabel": "密钥类型", - "keyTypeAuto": "自动检测", - "keyPasteTab": "粘贴", - "keyUploadTab": "上传", - "keyFileLoaded": "已加载密钥文件", - "keyUploadClick": "点击上传.pem / .key / .ppk", - "clearKey": "清除密钥", - "keySaved": "SSH 密钥已保存", - "keyReplaceNotice": "粘贴下面的新密钥以替换它", - "keyPassphraseSaved": "密码短语已保存,要更改的类型", - "replaceKey": "替换密钥", - "forceKeyboardInteractiveLabel": "强制键盘互动", - "forceKeyboardInteractiveShortDesc": "强制手动输入密码,即使密钥存在", - "terminalAppearance": "终端外观", - "colorTheme": "颜色主题", - "fontFamilyLabel": "字体类", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", "fontSizeLabel": "Font Size", - "cursorStyleLabel": "光标样式", - "letterSpacingPx": "字母间距 (px)", - "lineHeightLabel": "行高度", - "bellStyleLabel": "铃声样式", - "backspaceModeLabel": "背空模式", - "cursorBlinking": "光标闪烁", - "cursorBlinkingDesc": "启用终端光标闪烁动画", - "rightClickSelectsWordLabel": "右键选择单词", - "rightClickSelectsWordShortDesc": "右键点击光标下的单词", - "behaviorAndAdvanced": "行为和高级操作", - "scrollbackBufferLabel": "回滚缓存", - "scrollbackMaxLines": "在历史记录中保留的最大行数", - "sshAgentForwardingLabel": "SSH 代理转发中", - "sshAgentForwardingShortDesc": "将您的本地 SSH 密钥传递到此主机", - "enableAutoMosh": "启用自动Mosh", - "enableAutoMoshDesc": "如果可用,优先使用 Mosh。", - "enableAutoTmux": "启用自动Tmux", - "enableAutoTmuxDesc": "自动启动或附加到 tmux 会话", - "sudoPasswordAutoFillLabel": "Sudo 密码自动填充", - "sudoPasswordAutoFillShortDesc": "提示时自动提供确认密码", - "sudoPasswordLabel": "Sudo 密码", - "environmentVariablesLabel": "环境变量", - "addVariableBtn": "添加变量", - "noEnvVars": "未配置环境变量。", - "fastScrollModifierLabel": "快速滚动修饰符", - "fastScrollSensitivityLabel": "快速滚动灵敏度", - "moshCommandLabel": "Mosh 命令", - "startupSnippetLabel": "启动代码片段", - "keepaliveIntervalLabel": "保持连接间隔(秒)", - "maxKeepaliveMisses": "最大保留生命失败", - "tunnelSettings": "隧道设置", - "enableTunneling": "启用 Tunneling", - "enableTunnelingDesc": "为此主机启用 SSH 隧道功能", - "serverTunnelsSection": "服务器Tunnels", - "addTunnelBtn": "添加Tunnel", - "noTunnelsConfigured": "未配置隧道。", - "tunnelLabel": "隧道 {{number}}", - "tunnelType": "隧道类型", - "tunnelModeLocalDesc": "将本地端口转发到远程服务器上的端口(或其主机可访问)。", - "tunnelModeRemoteDesc": "将远程服务器上的端口转回您机器上的本地端口。", - "tunnelModeDynamicDesc": "在本地端口上创建一个 SOCKS5 代理, 用于动态端口转发.", - "sameHost": "此主机(直接隧道)", - "endpointHost": "端点主机", - "endpointPort": "端点端口", - "bindHost": "绑定主机", - "sourcePort": "源端口", - "maxRetries": "最大重试次数", - "retryIntervalS": "重试间隔(s)", - "autoStartLabel": "自动启动", - "autoStartDesc": "主机加载时自动连接这个隧道。", - "tunnelConnecting": "正在连接隧道...", - "tunnelDisconnected": "隧道断开连接", - "failedToConnectTunnel": "连接失败", - "failedToDisconnectTunnel": "断开连接失败", - "dockerIntegration": "停靠集成", - "enableDockerMonitor": "启用停靠栏", - "enableDockerMonitorDesc": "通过 Docker 监视和管理此主机上的容器", - "enableFileManagerMonitor": "启用文件管理器", - "enableFileManagerMonitorDesc": "通过 SFTP 浏览和管理主机上的文件", - "defaultPathLabel": "默认路径", - "fileManagerPathHint": "当文件管理器启动此主机时要打开的目录。", - "statusChecksLabel": "状态检查", - "enableStatusChecks": "启用状态检查", - "enableStatusChecksDesc": "定期打扰此主机以验证可用性", - "useGlobalInterval": "使用全局间隔", - "useGlobalIntervalDesc": "重写服务器范围状态检查间隔", - "checkIntervalS": "检查间隔(s)", - "checkIntervalDesc": "每个连接之间的秒数", - "metricsCollectionLabel": "计量收藏", - "enableMetricsLabel": "启用指标", - "enableMetricsDesc": "从此主机收集CPU、RAM、磁盘和网络使用情况", - "useGlobalMetrics": "使用全局间隔", - "useGlobalMetricsDesc": "重写服务器范围内的计量间隔", - "metricsIntervalS": "计量间隔 (秒)", - "metricsIntervalDesc2": "公尺快照之间的秒数", - "visibleWidgets": "可见部件", - "cpuUsageLabel": "CPU 使用", - "cpuUsageDesc": "CPU 百分比,负载平均值和闪光线图", - "memoryLabel": "内存使用", - "memoryDesc": "RAM 用法,交换,缓存", - "storageLabel": "磁盘使用", - "storageDesc": "每个挂载点的磁盘使用", - "networkLabel": "网络接口", - "networkDesc": "接口列表和带宽", - "uptimeLabel": "运行时间", - "uptimeDesc": "系统启动时间和启动时间", - "systemInfoLabel": "系统信息", - "systemInfoDesc": "操作、 内核、 主机名、 架构", - "recentLoginsLabel": "最近登录", - "recentLoginsDesc": "登录活动成功或失败", - "topProcessesLabel": "热门进程", - "topProcessesDesc": "PID, CPU%,MEM%,命令", - "listeningPortsLabel": "监听端口", - "listeningPortsDesc": "打开带流程和状态的端口", - "firewallLabel": "防火墙", - "firewallDesc": "防火墙,AppArmor,SELinux 状态", - "quickActionsLabel": "快速操作", - "quickActionsToolbar": "快速动作在服务器统计工具栏中显示为单击命令执行按钮。", - "noQuickActions": "暂无快速操作。", - "buttonLabel": "按钮标签", - "selectSnippetPlaceholder": "选择代码片断...", - "addActionBtn": "添加操作", - "hostSharedSuccessfully": "主机共享成功", - "failedToShareHost": "共享主机失败", - "accessRevoked": "访问已取消", - "failedToRevokeAccess": "吊销访问失败", - "cancelBtn": "取消", - "savingBtn": "保存中...", - "addHostBtn": "添加主机", - "hostUpdated": "主机已更新", - "hostCreated": "主机已创建", - "failedToSave": "保存主机失败", - "credentialUpdated": "凭据已更新", - "credentialCreated": "凭据已创建", - "failedToSaveCredential": "保存凭据失败", - "backToHosts": "返回主机", - "backToCredentials": "回到凭据", - "pinned": "固定的", - "noHostsFound": "未找到主机", - "tryDifferentTerm": "尝试一个不同的术语", - "addFirstHost": "添加您的第一个主机以开始", - "noCredentialsFound": "未找到凭据", - "addCredentialBtn": "添加凭据", - "updateCredentialBtn": "更新凭据", - "features": "功能", - "noFolder": "(无文件夹)", - "deleteSelected": "删除", - "exitSelection": "退出选择", - "importSkip": "导入(跳过存在)", - "importOverwrite": "导入(覆盖)", - "collapseBtn": "收起", - "importExportBtn": "导入/导出", - "hostStatusesRefreshed": "主机状态已刷新", - "failedToRefreshHosts": "刷新主机失败", - "movedHostTo": "将 {{host}} 移动到 \"{{folder}}\"", - "failedToMoveHost": "移动主机失败", - "folderRenamedTo": "文件夹已重命名为“{{name}}”", - "deletedFolder": "已删除文件夹“{{name}}”", - "failedToDeleteFolder": "删除文件夹失败", - "deleteAllInFolder": "删除“{{name}}”中的所有主机?此操作无法撤销。", - "deletedHost": "已删除 {{name}}", - "copiedToClipboard": "复制到剪贴板", - "terminalUrlCopied": "终端链接已复制", - "fileManagerUrlCopied": "文件管理器 URL 已复制", - "tunnelUrlCopied": "已复制隧道网址", - "dockerUrlCopied": "Docker URL 已复制", - "serverStatsUrlCopied": "服务器统计URL已复制", - "rdpUrlCopied": "已复制 RDP URL", - "vncUrlCopied": "已复制 VNC URL", - "telnetUrlCopied": "已复制Telnet URL", - "remoteDesktopUrlCopied": "已复制远程桌面 URL", - "expandActions": "展开操作", - "collapseActions": "坍塌动作", - "wakeOnLanAction": "局域网唤醒", - "wakeOnLanSuccess": "魔法数据包已发送至 {{name}}", - "wakeOnLanError": "发送魔术包失败", - "cloneHostAction": "复制主机", - "copyAddress": "复制地址", - "copyLink": "复制链接", - "copyTerminalUrlAction": "复制终端 URL", - "copyFileManagerUrlAction": "复制文件管理器 URL", - "copyTunnelUrlAction": "复制隧道网址", - "copyDockerUrlAction": "复制 Docker 网址", - "copyServerStatsUrlAction": "复制服务器统计URL", - "copyRdpUrlAction": "复制 RDP URL", - "copyVncUrlAction": "复制 VNC URL", - "copyTelnetUrlAction": "复制 Telnet URL", - "copyRemoteDesktopUrlAction": "复制远程桌面 URL", - "deleteCredentialConfirm": "删除凭据“{{name}}”?", - "deletedCredential": "已删除 {{name}}", - "deploySSHKeyTitle": "部署 SSH 密钥", - "deployingBtn": "正在部署...", - "deployBtn": "部署", - "failedToDeployKey": "部署密钥失败", - "deleteHostsConfirm": "删除 {{count}} 主机{{plural}}?此操作无法撤销。", - "movedToRoot": "移动到root", - "failedToMoveHosts": "移动主机失败", - "enableTerminalFeature": "启用终端", - "disableTerminalFeature": "禁用终端", - "enableFilesFeature": "启用文件", - "disableFilesFeature": "禁用文件", - "enableTunnelsFeature": "启用Tunnels", - "disableTunnelsFeature": "禁用Tunnels", - "enableDockerFeature": "启用停靠栏", - "disableDockerFeature": "禁用停靠栏", - "addTagsPlaceholder": "添加标签...", - "authDetails": "身份验证详情", - "credType": "类型", - "generateKeyPairDesc": "生成一个新的密钥对,私钥和公钥将自动填充。", - "generatingKey": "正在生成...", - "generateLabel": "生成 {{label}}", - "uploadFileBtn": "上传文件", - "keyPassphraseOptional": "密钥密码(可选)", - "sshPublicKeyOptional": "SSH 公钥(可选)", - "publicKeyGenerated": "公钥已生成", - "failedToGeneratePublicKey": "获取公钥失败", - "publicKeyCopied": "公钥已复制", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", + "moshCommandLabel": "Mosh Command", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", "keyPairGenerated": "{{label}} key pair generated", - "failedToGenerateKeyPair": "生成密钥对失败", - "searchHostsPlaceholder": "搜索主机,地址,标签…", - "searchCredentialsPlaceholder": "搜索凭证…", - "refreshBtn": "刷新", - "addTag": "添加标签...", - "deleteConfirmBtn": "删除", - "tunnelRequirementsText": "SSH 服务器必须有 GatewayPorts 是的,allowTcpForwarding y,PermitRoot登录yes 设置为 /etc/ssh/sshd_config。", - "deleteHostConfirm": "删除“{{name}}”?", - "enableAtLeastOneProtocol": "启用以上至少一个协议来配置身份验证和连接设置。", - "keyPassphrase": "密钥密码", - "connectBtn": "连接", - "disconnectBtn": "断开连接", - "basicInformation": "基本信息", - "authDetailsSection": "身份验证详情", - "credTypeLabel": "类型", - "hostsTab": "主机", - "credentialsTab": "全权证书", - "selectMultiple": "选择多个选项", - "selectHosts": "选择主机", - "connectionLabel": "连接", - "authenticationLabel": "认证", - "generateKeyPairTitle": "生成密钥对", - "generateKeyPairDescription": "生成一个新的密钥对,私钥和公钥将自动填充。", - "generateFromPrivateKey": "从私钥生成", - "refreshBtn2": "刷新", - "exitSelectionTitle": "退出选择", - "exportAll": "全部导出", - "addHostBtn2": "添加主机", - "addCredentialBtn2": "添加凭据", - "checkingHostStatuses": "正在检查主机状态...", - "pinnedSection": "固定的", - "hostsExported": "成功导出主机", - "exportFailed": "导出主机失败", - "sampleDownloaded": "已下载示例文件", - "failedToDeleteCredential2": "删除凭证失败", - "noFolderOption": "(无文件夹)", - "nSelected": "{{count}} 已选择", - "featuresMenu": "功能", - "moveMenu": "移动", - "cancelSelection": "取消", - "deployDialogDesc": "将 {{name}} 部署到主机的 authorized_keys 中。", - "targetHostLabel": "目标主机", - "selectHostOption": "选择主机...", - "keyDeployedSuccess": "密钥部署成功", - "failedToDeployKey2": "部署密钥失败", - "deletedCount": "已删除 {{count}} 主机", - "failedToDeleteCount": "删除 {{count}} 主机失败", - "duplicatedHost": "重复的“{{name}}”", - "failedToDuplicateHost": "复制主机失败", - "updatedCount": "已更新 {{count}} 主机", - "friendlyNameLabel": "友好名称", - "descriptionLabel": "描述", - "loadingHost": "正在加载主机...", - "loadingHosts": "正在加载主机...", - "loadingCredentials": "正在加载凭据...", - "noHostsYet": "尚无主机", - "noHostsMatchSearch": "没有符合您搜索条件的主机", - "hostNotFound": "找不到主机", - "searchHosts": "搜索主机...", - "sortHosts": "排序主机", - "sortDefault": "默认订单", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", - "sortIpAsc": "IP地址(升序)", - "sortIpDesc": "IP地址(描述)", - "sortOnlineFirst": "在线优先", - "sortOfflineFirst": "离线优先", - "sortPinnedFirst": "置顶", - "filterHosts": "过滤主机", - "filterClearAll": "清除筛选条件", - "filterStatusGroup": "地位", - "filterOnline": "在线的", - "filterOffline": "离线", - "filterPinned": "置顶", - "filterAuthGroup": "身份验证类型", - "filterAuthPassword": "密码", - "filterAuthKey": "SSH密钥", - "filterAuthCredential": "凭据", - "filterAuthNone": "没有任何", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "协议", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "远程桌面协议", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特征", - "filterFeatureTerminal": "终端", - "filterFeatureFileManager": "文件管理器", - "filterFeatureTunnel": "隧道", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "标签", - "shareHost": "共享主机", - "shareHostTitle": "分享: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "此主机必须使用凭据来启用共享。首先编辑主机并分配凭证。" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "连接", - "authentication": "认证", - "connectionSettings": "连接设置", - "displaySettings": "显示设置", - "audioSettings": "音频设置", - "rdpPerformance": "RDP 性能", - "deviceRedirection": "设备重定向", - "session": "会议", - "gateway": "网关", - "remoteApp": "移除应用", - "clipboard": "剪切板", - "sessionRecording": "会话录音", - "wakeOnLan": "在局域网上唤醒", - "vncSettings": "VNC 设置", - "terminalSettings": "终端设置", - "rdpPort": "RDP 端口", - "username": "用户名", - "password": "密码", - "domain": "域", - "securityMode": "安全模式", - "colorDepth": "颜色深度", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", + "remoteApp": "RemoteApp", + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", "width": "Width", - "height": "高度", + "height": "Height", "dpi": "DPI", - "resizeMethod": "调整大小方法", - "clientName": "客户端名称", - "initialProgram": "初始程序", - "serverLayout": "服务器布局", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", "timezone": "Timezone", "gatewayHostname": "Gateway Hostname", - "gatewayPort": "网关端口", - "gatewayUsername": "网关用户名", - "gatewayPassword": "网关密码", - "gatewayDomain": "网关域", - "remoteAppProgram": "RemoteApp程序", - "workingDirectory": "工作目录", - "arguments": "参数", - "normalizeLineEndings": "规范化线条结尾", - "recordingPath": "录制路径", - "recordingName": "录制名称", - "macAddress": "MAC地址", - "broadcastAddress": "广播地址", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", "udpPort": "UDP Port", - "waitTimeS": "等待时间 (s)", - "driveName": "驱动器名称", - "drivePath": "驱动器路径", - "ignoreCertificate": "忽略证书", - "ignoreCertificateDesc": "允许连接到有自签名证书的主机", - "forceLossless": "强制无损失", - "forceLosslessDesc": "强制使用无损图像编码 (更高质量、更多带宽)", - "disableAudio": "禁用音频", - "disableAudioDesc": "静音远程会话中的所有音频", - "enableAudioInput": "启用音频输入 (Microphone)", - "enableAudioInputDesc": "转发本地麦克风到远程会话", - "wallpaper": "壁纸", - "wallpaperDesc": "显示桌面壁纸 (禁用改善性能)", - "theming": "主题", - "themingDesc": "启用视觉主题和样式", - "fontSmoothing": "字体平移", - "fontSmoothingDesc": "启用清除类型字体渲染功能", - "fullWindowDrag": "全窗口拖动", - "fullWindowDragDesc": "拖动时显示窗口内容", - "desktopComposition": "桌面组成", - "desktopCompositionDesc": "启用Aero 玻璃效果", - "menuAnimations": "菜单动画", - "menuAnimationsDesc": "启用菜单淡出和幻灯片动画", - "disableBitmapCaching": "禁用位图缓存", - "disableBitmapCachingDesc": "关闭位图缓存 (可能帮助玻璃)", - "disableOffscreenCaching": "禁用屏幕外缓存", - "disableOffscreenCachingDesc": "关闭屏幕缓存", - "disableGlyphCaching": "禁用粒子缓存", - "disableGlyphCachingDesc": "关闭 glyph 缓存", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", "enableGfx": "Enable GFX", - "enableGfxDesc": "使用 RemoteFX 图形管道", - "enablePrinting": "启用打印", - "enablePrintingDesc": "将本地打印机重定向到远程会话", - "enableDriveRedirection": "启用驱动器重定向", - "enableDriveRedirectionDesc": "将本地文件夹映射为远程会话中的驱动器", - "createDrivePath": "创建驱动器路径", - "createDrivePathDesc": "如果不存在自动创建文件夹", - "disableDownload": "禁用下载", - "disableDownloadDesc": "防止从远程会话中下载文件", - "disableUpload": "禁用上传", - "disableUploadDesc": "防止上传文件到远程会话", - "enableTouch": "启用 Touch", - "enableTouchDesc": "启用触摸输入转发功能", - "consoleSession": "控制台会话", - "consoleSessionDesc": "连接到控制台(会话 0) 而不是新会话", - "sendWolPacket": "发送 WOL包", - "sendWolPacketDesc": "在连接之前发送一个魔法包来唤醒这个主机", - "disableCopy": "禁用复制", - "disableCopyDesc": "禁止从远程会话复制文本", - "disablePaste": "禁用粘贴", - "disablePasteDesc": "防止将文本粘贴到远程会话", - "createPathIfMissing": "如果缺少则创建路径", - "createPathIfMissingDesc": "自动创建录制目录", - "excludeOutput": "排除输出", - "excludeOutputDesc": "不录制屏幕输出 (仅限元数据)", - "excludeMouse": "排除鼠标显示", - "excludeMouseDesc": "不记录鼠标移动", - "includeKeystrokes": "包含关键字", - "includeKeystrokesDesc": "除屏幕输出外录制原始按键描边", - "vncPort": "VNC 端口", - "vncPassword": "VNC 密码", - "vncUsernameOptional": "用户名(可选)", - "vncLeaveBlank": "如果不需要则留空", - "cursorMode": "光标模式", - "swapRedBlue": "交换红/蓝图", - "swapRedBlueDesc": "交换红色和蓝色通道(修复一些颜色问题)", - "readOnly": "只读", - "readOnlyDesc": "在不发送任何输入的情况下查看远程屏幕", - "telnetPort": "Telnet 端口", - "terminalType": "终端类型", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", "fontName": "Font Name", "fontSize": "Font Size", - "colorScheme": "配色方案", - "backspaceKey": "背空键", - "saveHostFirst": "先保存主机。", - "sharingOptionsAfterSave": "主机保存后共享选项可用。", - "sharingLoadError": "无法加载共享数据。请检查您的连接,然后重试。", - "shareHostSection": "共享主机", - "shareWithUser": "与用户分享", - "shareWithRole": "与角色分享", - "selectUser": "选择用户", - "selectRole": "选择角色", - "selectUserOption": "选择一个用户...", - "selectRoleOption": "选择角色...", - "permissionLevel": "权限级别", - "expiresInHours": "过期时间(小时)", - "noExpiryPlaceholder": "留空为无过期时间", - "shareBtn": "分享", - "currentAccess": "当前访问", - "typeHeader": "类型", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", "targetHeader": "Target", - "permissionHeader": "权限", - "grantedByHeader": "授予者", - "expiresHeader": "过期时间", - "noAccessEntries": "尚无访问条目。", - "expiredLabel": "已过期", - "neverLabel": "从不使用", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", "revokeBtn": "Revoke", - "cancelBtn": "取消", - "savingBtn": "保存中...", - "updateHostBtn": "更新主机", - "addHostBtn": "添加主机" + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "搜索主机、 命令或设置...", - "quickActions": "快速操作", - "hostManager": "主机管理器", - "hostManagerDesc": "管理、 添加或编辑主机", - "addNewHost": "添加新主机", - "addNewHostDesc": "注册新主机", - "adminSettings": "管理员设置", - "adminSettingsDesc": "配置系统首选项和用户", - "userProfile": "用户资料", - "userProfileDesc": "管理您的帐户和首选项", - "addCredential": "添加凭证", - "addCredentialDesc": "存储 SSH 密钥或密码", - "recentActivity": "近期活动", - "serversAndHosts": "服务器和主机", - "noHostsFound": "未找到与“{{search}}”匹配的主机", - "links": "相关链接", + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", "navigate": "Navigate", - "select": "选择", - "toggleWith": "切换为" + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { "paneEmpty": "Pane {{index}} - empty", - "noTabAssigned": "未分配标签", - "focusedPane": "活动窗格" + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "没有连接", - "noConnectionsDesc": "打开终端、文件管理器或远程桌面即可在此处查看连接。", - "connectedFor": "已连接 {{duration}}", - "connected": "已连接", - "disconnected": "断开连接", - "closeTab": "关闭标签页", - "closeConnection": "紧密联系", - "forgetTab": "忘记", - "removeBackground": "消除", - "reconnect": "重新连接", - "reopenTab": "重新开放", - "sectionOpen": "打开", - "sectionBackground": "背景", - "backgroundDesc": "断开连接后,会话仍会保持 30 分钟,并且可以重新连接。", - "persisted": "在后台持续运行", - "expiresIn": "有效期至 {{duration}}", - "search": "搜索关联...", - "noSearchResults": "没有找到与您的搜索匹配的结果。" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "正在连接到 {{type}} 会话...", - "connectionError": "连接错误", - "connectionFailed": "连接失败", - "failedToConnect": "获取连接令牌失败", - "hostNotFound": "找不到主机", - "noHostSelected": "没有选择主机", - "reconnect": "重新连接", - "retry": "重试", - "guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确认 guacd 正在运行并且可以在管理设置中进行正确配置。", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "胜利+L (锁屏)", - "winKey": "Windows 密钥", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", "alt": "Alt", - "shift": "偏移", - "win": "胜利", - "stickyActive": "{{key}} (已锁定 - 单击释放)", - "stickyInactive": "{{key}} (点击锁定)", - "esc": "逃逸的", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", "tab": "Tab", - "home": "首页", - "end": "结束", - "pageUp": "向上页面", - "pageDown": "向下页面", - "arrowUp": "向上箭头", - "arrowDown": "向下箭头", - "arrowLeft": "左箭头", - "arrowRight": "右箭头", - "fnToggle": "函数键", - "reconnect": "重新连接会话", - "collapse": "折叠工具栏", - "expand": "展开工具栏", - "dragHandle": "拖动以重新定位" + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "连接到主机", - "clear": "清空", - "paste": "粘贴", - "reconnect": "重新连接", - "connectionLost": "连接丢失", - "connected": "已连接", - "clipboardWriteFailed": "复制到剪贴板失败。请确认页面是通过 HTTPS 或本地主机服务的。", - "clipboardReadFailed": "从剪贴板读取失败。请确保已授予剪贴板权限。", - "clipboardHttpWarning": "粘贴需要 HTTPS。使用 Ctrl+Shift+V 或通过 HTTPS 提供Termixed 服务。", - "unknownError": "发生未知错误", - "websocketError": "WebSocket 连接错误", - "connecting": "正在连接...", - "noHostSelected": "没有选择主机", - "reconnecting": "正在重新连接... ({{attempt}}/{{max}})", - "reconnected": "已成功重新连接", - "tmuxSessionCreated": "tmux 会话已创建: {{name}}", - "tmuxSessionAttached": "tmux 会话已连接: {{name}}", - "tmuxUnavailable": "tmux 未安装在远程主机上,返回到标准的 shell", - "tmuxSessionPickerTitle": "tmux 会话", - "tmuxSessionPickerDesc": "在此主机上找到现有的 tmux 会话。选择一个来重新附加或创建一个新会话。", - "tmuxWindows": "窗口", - "tmuxWindowCount": "{{count}} 窗口", - "tmuxAttached": "附加客户端", - "tmuxAttachedCount": "{{count}} 已连接", - "tmuxLastActivity": "上次活动", - "tmuxTimeJustNow": "就在这里", - "tmuxTimeMinutes": "{{count}}几分钟前", - "tmuxTimeHours": "{{count}}小时前", - "tmuxTimeDays": "{{count}}天前", - "tmuxCreateNew": "开始新会话", - "tmuxCopyHint": "调整选区并按回车键复制到剪贴板", - "tmuxDetach": "从 tmux 会话移除", - "tmuxDetached": "从 tmux 会话断开", - "maxReconnectAttemptsReached": "已达到最大重连尝试次数", - "closeTab": "关闭", - "connectionTimeout": "连接超时", - "terminalTitle": "终端 - {{host}}", - "terminalWithPath": "终端 - {{host}}:{{path}}", - "runTitle": "运行 {{command}} - {{host}}", - "totpRequired": "需要双因素身份验证", - "totpCodeLabel": "验证码", - "totpVerify": "验证", - "warpgateAuthRequired": "需要Warpgate 身份验证", - "warpgateSecurityKey": "安全密钥", - "warpgateAuthUrl": "身份验证URL", - "warpgateOpenBrowser": "在浏览器中打开", - "warpgateContinue": "我已完成身份验证", - "opksshAuthRequired": "需要OPKSSH 身份验证", - "opksshAuthDescription": "在您的浏览器中完成身份验证以继续。此会话将持续24小时有效。", - "opksshOpenBrowser": "打开浏览器进行身份验证", - "opksshWaitingForAuth": "正在等待浏览器的身份验证...", - "opksshAuthenticating": "正在处理身份验证...", - "opksshTimeout": "认证超时。请重试。", - "opksshAuthFailed": "认证失败。请检查您的凭据,然后重试。", - "opksshSignInWith": "使用 {{provider}} 登录", - "sudoPasswordPopupTitle": "输入密码?", - "websocketAbnormalClose": "连接意外关闭。这可能是由于反向代理或SSL配置出现问题。请检查服务器日志。", - "connectionLogTitle": "连接日志", - "connectionLogCopy": "复制日志到剪贴板", - "connectionLogEmpty": "尚无连接日志", - "connectionLogWaiting": "正在等待连接日志...", - "connectionLogCopied": "连接日志已复制到剪贴板", - "connectionLogCopyFailed": "复制日志到剪贴板失败", - "connectionRejected": "连接被服务器拒绝。请检查您的身份验证和网络配置。", - "hostKeyRejected": "SSH 主机验证被拒绝。连接已取消。", - "sessionTakenOver": "会话已在另一个标签页打开。正在重新连接..." + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "未选择主机", - "initializingEditor": "正在初始化编辑器...", - "file": "文件", - "folder": "文件夹", - "uploadFile": "上传文件", - "downloadFile": "下载", - "extractArchive": "解压归档文件", - "extractingArchive": "正在提取 {{name}}...", - "archiveExtractedSuccessfully": "{{name}} 提取成功", - "extractFailed": "提取失败", - "compressFile": "压缩文件", - "compressFiles": "压缩多个文件", - "compressFilesDesc": "将 {{count}} 个项目压缩为归档文件", - "archiveName": "归档文件名", - "enterArchiveName": "请输入归档文件名...", - "compressionFormat": "压缩格式", - "selectedFiles": "选定文件", - "andMoreFiles": "及 {{count}} 个其他文件...", - "compress": "压缩", - "compressingFiles": "正在将 {{count}} 个项目压缩为 {{name}}...", - "filesCompressedSuccessfully": "{{name}} 创建成功", - "compressFailed": "压缩失败", - "edit": "编辑", - "preview": "预览", - "previous": "上一页", - "next": "下一页", - "pageXOfY": "第 {{current}} 页 / 共 {{total}} 页", - "zoomOut": "缩小", - "zoomIn": "放大", - "newFile": "新建文件", - "newFolder": "新建文件夹", - "rename": "重命名", - "uploading": "正在上传...", - "uploadingFile": "正在上传 {{name}}...", - "fileName": "文件名", - "folderName": "文件夹名称", - "fileUploadedSuccessfully": "文件“{{name}}”已成功上传", - "failedToUploadFile": "文件上传失败", - "fileDownloadedSuccessfully": "文件“{{name}}”已成功下载", - "failedToDownloadFile": "文件下载失败", - "fileCreatedSuccessfully": "文件“{{name}}”创建成功", - "folderCreatedSuccessfully": "文件夹“{{name}}”已成功创建", - "failedToCreateItem": "创建项目失败", - "operationFailed": "{{operation}} {{name}} 操作失败,: {{error}}", - "failedToResolveSymlink": "解析符号链接失败", - "itemsDeletedSuccessfully": "已成功删除 {{count}} 个项目", - "failedToDeleteItems": "删除项目失败", - "sudoPasswordRequired": "需要管理员密码", - "enterSudoPassword": "输入保证密码以继续此操作", - "sudoPassword": "Sudo 密码", - "sudoOperationFailed": "Sudo 操作失败", - "sudoAuthFailed": "Sudo 身份验证失败", - "dragFilesToUpload": "文件拖放到这里即可上传", - "emptyFolder": "此文件夹为空。", - "searchFiles": "搜索文件...", - "upload": "上传", - "selectHostToStart": "选择主机以启动文件管理", - "sshRequiredForFileManager": "文件管理器需要 SSH。此主机未启用 SSH 。", - "failedToConnect": "SSH连接失败。", - "failedToLoadDirectory": "加载目录失败", - "noSSHConnection": "没有可用的 SSH 连接", - "copy": "复制", - "cut": "剪切", - "paste": "粘贴", - "copyPath": "复制路径", - "copyPaths": "复制路径", - "delete": "删除", - "properties": "属性", - "refresh": "刷新", - "downloadFiles": "将 {{count}} 个文件下载至浏览器", - "copyFiles": "复制 {{count}} 个项目", - "cutFiles": "剪切 {{count}} 个项目", - "deleteFiles": "删除 {{count}} 个项目", - "filesCopiedToClipboard": "已将 {{count}} 个项目复制到剪贴板", - "filesCutToClipboard": "{{count}} 个项目已剪切至剪贴板", - "pathCopiedToClipboard": "路径已复制到剪贴板", - "pathsCopiedToClipboard": "已将 {{count}} 个路径复制到剪贴板", - "failedToCopyPath": "无法将路径复制到剪贴板", - "movedItems": "已移动 {{count}} 个项目", - "failedToDeleteItem": "删除项目失败", - "itemRenamedSuccessfully": "{{type}} 已成功重命名", - "failedToRenameItem": "重命名项目失败", - "download": "下载", - "permissions": "权限", - "size": "大小", - "modified": "修改时间", - "path": "路径", - "confirmDelete": "你确定要删除 {{name}} 吗?", - "permissionDenied": "没有权限", - "serverError": "服务器错误", - "fileSavedSuccessfully": "文件已成功保存", - "failedToSaveFile": "文件保存失败", - "confirmDeleteSingleItem": "您确定要永久删除“{{name}}”吗?", - "confirmDeleteMultipleItems": "您确定要永久删除 {{count}} 个项目吗?", - "confirmDeleteMultipleItemsWithFolders": "您确定要永久删除 {{count}} 个项目吗?这包括文件夹及其内容。", - "confirmDeleteFolder": "您确定要永久删除文件夹“{{name}}”及其所有内容吗?", - "permanentDeleteWarning": "此操作不可撤销,相关项目将从服务器中被永久删除。", - "recent": "最近", - "pinned": "置顶", - "folderShortcuts": "文件夹快捷方式", - "failedToReconnectSSH": "SSH 会话重新连接失败", - "openTerminalHere": "在此处打开终端", - "run": "运行", - "openTerminalInFolder": "在此文件夹中打开终端", - "openTerminalInFileLocation": "打开终端,指向文件位置", - "runningFile": "运行 - {{file}}", - "onlyRunExecutableFiles": "只能运行可执行文件", - "directories": "目录", - "removedFromRecentFiles": "已将“{{name}}”从最近文件中移除", - "removeFailed": "移除失败", - "unpinnedSuccessfully": "已成功取消置顶“{{name}}”", - "unpinFailed": "取消置顶失败", - "removedShortcut": "已移除快捷方式“{{name}}”", - "removeShortcutFailed": "移除快捷方式失败", - "clearedAllRecentFiles": "已清除所有最近文件", - "clearFailed": "清除失败", - "removeFromRecentFiles": "从最近文件中删除", - "clearAllRecentFiles": "清除所有最近文件", - "unpinFile": "取消置顶文件", - "removeShortcut": "移除快捷方式", - "pinFile": "置顶文件", - "addToShortcuts": "添加到快捷方式", - "pasteFailed": "粘贴失败", - "noUndoableActions": "没有可撤销的操作", - "undoCopySuccess": "撤销复制操作:已删除 {{count}} 个已复制的文件", - "undoCopyFailedDelete": "撤销失败:无法删除任何已复制的文件", - "undoCopyFailedNoInfo": "撤销失败:找不到已复制的文件信息", - "undoMoveSuccess": "撤销移动操作:已将 {{count}} 个文件移回原始位置", - "undoMoveFailedMove": "撤销失败:无法将任何文件移回。", - "undoMoveFailedNoInfo": "撤销失败:找不到已移动的文件信息", - "undoDeleteNotSupported": "删除操作无法撤销:文件已从服务器永久删除。", - "undoTypeNotSupported": "不支持的撤销操作类型", - "undoOperationFailed": "撤销操作失败", - "unknownError": "未知错误", - "confirm": "确认", - "find": "查找...", - "replace": "替换", - "downloadInstead": "下载", - "keyboardShortcuts": "快捷键", - "searchAndReplace": "查找与替换", - "editing": "编辑", - "search": "搜索", - "findNext": "查找下一个", - "findPrevious": "查找上一个", - "save": "保存", - "selectAll": "全选", - "undo": "撤销", - "redo": "重做", - "moveLineUp": "上移行", - "moveLineDown": "下移行", - "toggleComment": "切换注释", - "autoComplete": "自动补全", - "imageLoadError": "图片加载失败", - "startTyping": "开始输入……", - "unknownSize": "尺寸未知", - "fileIsEmpty": "文件为空", - "largeFileWarning": "大文件警告", - "largeFileWarningDesc": "此文件大小为 {{size}},以文本格式打开时可能会导致性能问题。", - "fileNotFoundAndRemoved": "文件“{{name}}”未找到,已从最近/置顶文件中移除。", - "failedToLoadFile": "文件加载失败:{{error}}", - "serverErrorOccurred": "服务器出错,请稍后再试。", - "autoSaveFailed": "自动保存失败", - "fileAutoSaved": "文件自动保存", - "moveFileFailed": "移动 {{name}} 失败", - "moveOperationFailed": "移动操作失败", - "canOnlyCompareFiles": "只能比较两个文件", - "comparingFiles": "正在比较文件:{{file1}} 和 {{file2}}", - "dragFailed": "拖拽操作失败", - "filePinnedSuccessfully": "文件“{{name}}”已成功固定", - "pinFileFailed": "文件固定失败", - "fileUnpinnedSuccessfully": "文件“{{name}}”已成功取消固定", - "unpinFileFailed": "取消固定文件失败", - "shortcutAddedSuccessfully": "文件夹快捷方式“{{name}}”已成功添加", - "addShortcutFailed": "添加快捷方式失败", - "operationCompletedSuccessfully": "{{operation}} {{count}} 个项目成功", - "operationCompleted": "{{operation}} {{count}} 个项目", - "downloadFileSuccess": "文件 {{name}} 下载成功", - "downloadFileFailed": "下载失败", - "moveTo": "移至 {{name}}", - "diffCompareWith": "比较与 {{name}} 的差异", - "dragOutsideToDownload": "向外拖动窗口即可下载({{count}} 个文件)", - "newFolderDefault": "新建文件夹", - "newFileDefault": "新建文件 txt", - "successfullyMovedItems": "已成功将 {{count}} 个项目移动到 {{target}}", - "move": "移动", - "searchInFile": "在文件中搜索(Ctrl+F)", - "showKeyboardShortcuts": "显示快捷键", - "startWritingMarkdown": "开始编写你的 Markdown 内容……", - "loadingFileComparison": "正在加载文件比较...", - "reload": "重新加载", - "compare": "对比", - "sideBySide": "左右分栏", - "inline": "行内对比", - "fileComparison": "文件对比:{{file1}} 与 {{file2}}", - "fileTooLarge": "文件过大:{{error}}", - "sshConnectionFailed": "SSH 连接失败,请检查你与 {{name}}({{ip}}:{{port}})的连接状态", - "loadFileFailed": "文件加载失败:{{error}}", - "connecting": "正在连接...", - "connectedSuccessfully": "连接成功", - "totpVerificationFailed": "TOTP验证失败", - "warpgateVerificationFailed": "Warpgate 身份验证失败", - "authenticationFailed": "身份验证失败", - "verificationCodePrompt": "验证码:", - "changePermissions": "更改权限", - "currentPermissions": "当前权限", - "owner": "所有者", - "group": "用户组", - "others": "其他的", - "read": "读", - "write": "写", - "execute": "执行", - "permissionsChangedSuccessfully": "权限已成功更改", - "failedToChangePermissions": "更改权限失败", - "name": "名称", - "sortByName": "名称", - "sortByDate": "修改日期", - "sortBySize": "大小", - "ascending": "升序", - "descending": "降序", - "root": "根目录", - "new": "新的", - "sortBy": "排序方式", - "items": "项目", - "selected": "已选择", - "editor": "编辑器", - "octal": "八月", - "storage": "存储", - "disk": "磁盘", - "used": "已使用", - "of": "共", - "toggleSidebar": "切换侧边栏", - "cannotLoadPdf": "无法加载 PDF", - "pdfLoadError": "加载此 PDF 文件时出错。", - "loadingPdf": "正在加载 PDF...", - "loadingPage": "加载页面..." + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "复制到主机…", - "moveToHost": "移至主机…", - "copyItemsToHost": "将 {{count}} 项复制到主机…", - "moveItemsToHost": "将 {{count}} 个物品移至主机…", - "noHostsConnected": "没有其他文件管理器主机可用。", - "noHostsConnectedHint": "在主机管理器中添加另一台启用了文件管理器的 SSH 主机。", - "selectDestinationHost": "选择目标主机", - "destinationPath": "目标路径", - "recentDestinations": "近期目的地", - "collapseRecentDestinations": "收起最近的目的地", - "expandRecentDestinations": "展开最近目的地", - "browseFolders": "浏览目标文件夹", - "browseDestination": "浏览或输入路径", - "confirmCopy": "复制", - "confirmMove": "移动", - "transferring": "转移…", - "compressing": "正在压缩…", - "extracting": "提取…", - "transferringItems": "转移 {{current}} 个物品中的 {{total}} 个物品…", - "transferSuccess": "转账完成", - "transferError": "转账失败", - "transferPartial": "转账完成,但出现 {{count}} 个错误", - "transferPartialHint": "转账失败: {{paths}}", - "itemsSummary": "{{count}} 项目", - "destMustBeDirectory": "对于多项传输,目标位置必须是目录。", - "selectThisFolder": "选择此文件夹", - "browsePathWillBeCreated": "此文件夹尚不存在。传输开始时将创建此文件夹。", - "browsePathError": "无法在目标主机上打开此路径。", - "goUp": "上", - "copyFolderToHost": "将文件夹复制到主机…", - "moveFolderToHost": "将文件夹移动到主机…", - "hostReady": "准备好", - "hostConnecting": "正在连接…", - "hostDisconnected": "未连接", - "hostAuthRequired": "需要身份验证——请先在此主机上打开文件管理器。", - "hostConnectionFailed": "连接失败", - "metricsTitle": "转账时间", - "metricsPrepare": "准备目的地: {{duration}}", - "metricsCompress": "压缩源文件: {{duration}}", - "metricsHopSourceRead": "来源 → 服务器: {{throughput}}", - "metricsHopDestSftpWrite": "服务器 → 目标(SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "服务器 → 目标(本地): {{throughput}}", - "metricsTransfer": "端到端: {{throughput}} ({{duration}})", - "metricsExtract": "提取到目标位置: {{duration}}", - "metricsSourceDelete": "从源中移除: {{duration}}", - "metricsTotal": "总计: {{duration}}", - "progressCompressing": "在源主机上进行压缩…", - "progressExtracting": "正在从目标位置… 提取", - "progressTransferring": "正在传输数据…", - "progressReconnecting": "正在重新连接…", - "parallelSegmentsLabel": "平行换乘车道", - "parallelSegmentsOption": "{{count}} 车道", - "parallelSegmentsHint": "大文件会被分割成 256 MB 的数据块。多通道使用独立的连接(类似于同时启动多个传输)以提高总吞吐量。", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", - "progressTransferringItems": "正在传输文件({{current}} 中的 {{total}})…", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} 文件", - "sourceNotDeletedPartial": "保留源文件(部分传输)", - "jumpHostLimitation": "两台主机都必须能从Termix服务器访问。不支持主机之间的直接路由。", - "cancel": "取消", - "methodLabel": "转移方法", - "methodAuto": "汽车", - "methodTar": "Tar 归档", - "methodItemSftp": "按文件 SFTP", - "methodAutoHint": "根据文件数量、大小和压缩率选择 tar 协议或按文件 SFTP 协议。单个文件始终使用流式 SFTP 协议。", - "methodTarHint": "在源端压缩,传输单个压缩包,在目标端解压。两个 Unix 主机都需要 tar 命令。", - "methodItemSftpHint": "通过 SFTP 逐个传输文件。支持所有主机,包括 Windows 系统。", - "methodPreviewLoading": "计算转移方法…", - "methodPreviewError": "无法预览传输方式。服务器将在您启动时选择一种传输方式。", - "methodPreviewWillUseTar": "将使用:Tar 归档", - "methodPreviewWillUseItemSftp": "将使用:按文件 SFTP", - "methodPreviewScanSummary": "{{fileCount}} 个文件, {{totalSize}} 总计(在源主机上扫描)。", - "methodItemSftpLimitation": "每个文件都使用同一个 SFTP 流,以单文件复制的方式依次进行。所有文件的进度是合并计算的,因此在处理大文件时进度条移动较慢。", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "您选择了按文件SFTP传输。", - "user_tar": "您选择了tar归档。", - "tar_unavailable": "Tar 在一台或两台主机上不可用——将改用按文件 SFTP。", - "windows_host": "涉及 Windows 主机——未使用 tar。", - "auto_multi_large": "自动:多个文件,包括一个大文件({{largestSize}}),其中包含可压缩数据——tar 打包成一个传输。", - "auto_single_large_in_archive": "自动:此集合中有一个大文件({{largestSize}})—按文件 SFTP。", - "auto_many_incompressible": "自动:主要为不可压缩数据——按文件 SFTP。", - "auto_many_files": "自动:多个文件({{fileCount}})— tar 减少每个文件的开销。", - "auto_default": "自动:此数据集的每个文件都使用 SFTP 进行管理。" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "取消", - "progressCancelling": "取消…", - "progressStalled": "停滞", - "resumedHint": "已重新连接到在另一个窗口中启动的活动传输。", - "transferCancelled": "转账已取消", - "transferCancelledCopyHint": "目标位置可能仍会保留部分文件。", - "transferCancelledMoveHint": "目标位置可能仍保留部分文件。源文件未被删除。", - "cleanupDestFiles": "清理目的地", - "cleanupDestFilesSuccess": "已从目标位置删除部分文件", - "cleanupDestFilesPartial": "部分文件无法删除。", - "cleanupDestFilesNothing": "目的地无需清理", - "cleanupDestFilesError": "清理失败", - "retryTransfer": "重试", - "retryTransferError": "重试失败", - "transferFailedRetryHint": "目标端保留了部分数据。连接恢复后,系统将恢复重试。" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "没有 SSH 隧道", - "createFirstTunnelMessage": "您尚未创建任何 SSH 隧道。请在主机管理器中配置隧道连接以开始操作。", - "connected": "已连接", - "disconnected": "断开连接", - "connecting": "正在连接...", - "error": "错误", - "canceling": "正在取消……", - "connect": "连接", - "disconnect": "断开", - "cancel": "取消", - "port": "港口", - "localPort": "本地端口", - "remotePort": "远程端口", - "currentHostPort": "当前主机端口", - "endpointPort": "端点端口", - "bindIp": "本地IP", - "endpointSshConfig": "端点SSH 配置", - "endpointSshHost": "端点SSH 主机", - "endpointSshHostPlaceholder": "选择一个配置的主机", - "endpointSshHostRequired": "为每个客户端隧道选择一个端点 SSH 主机。", - "attempt": "尝试 {{current}} 次,共 {{max}} 次", - "nextRetryIn": "下次重试时间为 {{seconds}} 秒", - "clientTunnels": "客户端Tunnels", - "clientTunnel": "客户端隧道", - "addClientTunnel": "添加客户端Tunnel", - "noClientTunnels": "在此桌面上没有配置客户端隧道。", - "tunnelName": "隧道名称", - "remoteHost": "远程主机", - "autoStart": "自动启动", - "clientAutoStartDesc": "当此桌面客户端打开并保持连接时开始。", - "clientManualStartDesc": "使用启动和停止此行。混合函数不会自动打开。", - "clientRemoteServerNote": "远程转发可能需要 allowTcpForwarding 和 GatewayPorts 在端点 SSH 服务器上。当此桌面断开连接时,远程端口将关闭。", - "clientTunnelStarted": "客户端隧道已启动", - "clientTunnelStopped": "客户端隧道已停止", - "tunnelTestSucceeded": "隧道测试成功", - "tunnelTestFailed": "隧道测试失败", - "localSaved": "客户端隧道已保存", - "localSaveError": "保存本地客户端隧道失败", - "invalidBindIp": "本地IP必须是有效的IPv4地址。", - "invalidLocalTargetIp": "本地目标IP必须是有效的IPv4地址。", - "invalidLocalPort": "本地端口必须介于 1 到 65535 之间。", - "invalidRemotePort": "远程端口必须介于 1 到 65535 之间。", - "invalidLocalTargetPort": "本地目标端口必须介于 1 到 65535 之间。", - "invalidEndpointPort": "端点端口必须介于 1 到 65535 之间。", - "duplicateAutoStartBind": "只能有一个自动启动客户端隧道使用 {{bind}}。", - "manualControlError": "更新隧道状态失败。", - "active": "已激活", - "start": "开始", - "stop": "停止", - "test": "测试", - "type": "隧道类型", - "typeLocal": "本地(-L)", - "typeRemote": "远程 (-R)", - "typeDynamic": "动态 (-D)", - "typeServerLocalDesc": "当前端点的主机。", - "typeServerRemoteDesc": "返回到当前主机的终点。", - "typeClientLocalDesc": "本地计算机到端点。", - "typeClientRemoteDesc": "返回本地计算机的端点。", - "typeClientDynamicDesc": "本地计算机上的SOCKS。", - "typeDynamicDesc": "通过 SSH 转发SOCKS5 连接", - "forwardDescriptionServerLocal": "当前主机 {{sourcePort}} → 端点 {{endpointPort}}。", - "forwardDescriptionServerRemote": "端点 {{endpointPort}} → 当前主机 {{sourcePort}}。", - "forwardDescriptionServerDynamic": "当前主机上的 SOCKS {{sourcePort}}。", - "forwardDescriptionClientLocal": "本地 {{sourcePort}} → 远程 {{endpointPort}}。", - "forwardDescriptionClientRemote": "远程 {{sourcePort}} → 本地 {{endpointPort}}。", - "forwardDescriptionClientDynamic": "本地端口上的 SOCKS {{sourcePort}}。", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS 通过 {{endpoint}}", - "autoNameClientLocal": "本地 {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → 本地 {{localPort}}", - "autoNameClientDynamic": "袜子 {{localPort}} 通过 {{endpoint}}", - "route": "路由:", - "lastStarted": "上次启动", - "lastTested": "上次测试", - "lastError": "最后一个错误", - "maxRetries": "最大重试次数", - "maxRetriesDescription": "最大重试次数.", - "retryInterval": "重试间隔 (秒)", - "retryIntervalDescription": "重试之间等待的时间。", - "local": "本地", - "remote": "远程", - "destination": "目标", - "host": "主机", - "mode": "模式", - "noHostSelected": "没有选择主机", - "working": "工作中..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { "cpu": "CPU", - "memory": "内存", - "disk": "磁盘", - "network": "网络", - "uptime": "运行时长", - "processes": "进程数", - "available": "可用", - "free": "空闲", - "connecting": "正在连接...", - "connectionFailed": "无法连接到服务器", - "naCpus": "CPU 信息未获取", - "cpuCores_one": "{{count}} 核心", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", "cpuCores_other": "{{count}} Cores", - "cpuUsage": "CPU 使用率", - "memoryUsage": "内存使用率", - "diskUsage": "磁盘使用率", - "failedToFetchHostConfig": "获取主机配置失败", - "serverOffline": "服务器离线", - "cannotFetchMetrics": "无法从离线服务器获取指标", - "totpFailed": "TOTP验证失败", - "noneAuthNotSupported": "服务器统计信息不支持“无”身份验证类型。", - "load": "加载", - "systemInfo": "系统信息", - "hostname": "主机名", - "operatingSystem": "操作系统", - "kernel": "核心", - "seconds": "秒", - "networkInterfaces": "网络接口", - "noInterfacesFound": "未找到网络接口", - "noProcessesFound": "未找到任何进程", - "loginStats": "SSH 登录统计信息", - "noRecentLoginData": "暂无最近登录数据", - "executingQuickAction": "正在执行 {{name}}...", - "quickActionSuccess": "{{name}} 已成功完成", - "quickActionFailed": "{{name}} 失败", - "quickActionError": "执行 {{name}} 失败", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "监听端口", + "title": "Listening Ports", "protocol": "Protocol", - "port": "端口", - "address": "地址", - "process": "进程", - "noData": "没有监听端口数据" + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "防火墙", - "inactive": "未激活", - "policy": "政策", - "rules": "规则", - "noData": "无可用防火墙数据", - "action": "行 动", + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", "protocol": "Proto", - "port": "端口", - "source": "来源", - "anywhere": "任意地方" + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "装入平均值", - "swap": "切换", - "architecture": "结构", - "refresh": "刷新", - "retry": "重试" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "自托管 SSH 和远程桌面管理", - "loginTitle": "登录 Termix", - "registerTitle": "创建账户", - "forgotPassword": "忘记密码?", - "rememberMe": "记住设备30天 (包括TOTP)", - "noAccount": "还没有账号?", - "hasAccount": "已有账号?", - "twoFactorAuth": "双因素身份验证", - "enterCode": "请输入验证码", - "backupCode": "或者使用备用代码", - "verifyCode": "验证码", - "redirectingToApp": "正在重定向到应用程序...", - "sshAuthenticationRequired": "需要 SSH 身份验证", - "sshNoKeyboardInteractive": "键盘交互认证不可用", - "sshAuthenticationFailed": "身份验证失败", - "sshAuthenticationTimeout": "身份验证超时", - "sshNoKeyboardInteractiveDescription": "服务器不支持键盘交互认证。请提供你的密码或 SSH 密钥。", - "sshAuthFailedDescription": "提供的凭证无效。请使用有效的凭证重试。", - "sshTimeoutDescription": "身份验证尝试超时,请重试。", - "sshProvideCredentialsDescription": "请提供您的 SSH 凭证以连接到此服务器。", - "sshPasswordDescription": "请输入此SSH连接的密码。", - "sshKeyPasswordDescription": "如果您的 SSH 密钥已加密,请在此处输入密码。", - "passphraseRequired": "需要密码", - "passphraseRequiredDescription": "SSH 密钥已加密。请输入密码以解锁它。", - "back": "返回", - "firstUser": "首位用户", - "firstUserMessage": "您是第一个用户,将被授予管理员权限。您可以在侧边栏用户下拉菜单中查看管理员设置。如果您认为这是一个错误,请查看 Docker 日志或在 GitHub 上创建一个 issue。", - "external": "第三方", - "loginWithExternal": "通过第三方账号登录", - "loginWithExternalDesc": "使用你配置的第三方身份提供商登录", - "externalNotSupportedInElectron": "Electron 应用暂不支持第三方认证。请使用网页版进行 OIDC 登录。", - "resetPasswordButton": "重置密码", - "sendResetCode": "发送重置码", - "resetCodeDesc": "输入你的用户名以接收密码重置码。该码将记录在 docker 容器日志中。", - "resetCode": "重置码", - "verifyCodeButton": "验证码", - "enterResetCode": "请输入 Docker 容器日志中该用户的 6 位代码:", - "newPassword": "新密码", - "confirmNewPassword": "确认密码", - "enterNewPassword": "请输入用户的新密码:", - "signUp": "注册", - "desktopApp": "桌面应用程序", - "loggingInToDesktopApp": "正在登录桌面应用", - "loadingServer": "正在加载服务器...", - "dataLossWarning": "使用此方法重置密码将删除您保存的所有 SSH 主机、凭证和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此方法。", - "authenticationDisabled": "身份验证已禁用", - "authenticationDisabledDesc": "所有身份验证方式目前均已禁用。请联系您的管理员。", - "attemptsRemaining": "{{count}} 剩余尝试次数" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "验证 SSH 主机密钥", - "keyChangedWarning": "SSH 主机密钥已更改", - "firstConnectionTitle": "第一次连接到此主机", - "firstConnectionDescription": "此主机的真实性无法确认。请验证指纹符合您的期望。", - "keyChangedDescription": "此服务器的主机密钥自您上次连接以来已经改变。这可能会显示一个安全问题。", - "previousKey": "上一键", - "newFingerprint": "新建指纹", - "fingerprint": "指纹", - "verifyInstructions": "如果您信任此主机,请单击接受以继续保存此指纹以备将来的连接。", - "securityWarning": "安全警告", - "acceptAndContinue": "接受并继续", - "acceptNewKey": "接受新密钥并继续" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "无法连接到数据库", - "unknownError": "未知错误", - "loginFailed": "登录失败", - "failedPasswordReset": "密码重置失败", - "failedVerifyCode": "重置代码验证失败", - "failedCompleteReset": "密码重置失败", - "invalidTotpCode": "无效的 TOTP 代码", - "failedOidcLogin": "OIDC 登录启动失败", - "silentSigninOidcUnavailable": "请求静音登录,但OIDC 登录不可用。", - "failedUserInfo": "登录后无法获取用户信息", - "oidcAuthFailed": "OIDC 身份验证失败", - "invalidAuthUrl": "从后端收到的授权 URL 无效", - "requiredField": "此字段是必需的", - "minLength": "最短长度为 {{min}}", - "passwordMismatch": "密码不匹配", - "passwordLoginDisabled": "目前已禁用用户名/密码登录。", - "sessionExpired": "会话已过期 - 请重新登录", - "totpRateLimited": "率限制:太多TOTP 验证尝试。请稍后再试。", - "totpRateLimitedWithTime": "速率限制:TOTP 验证尝试次数过多。请稍等 {{time}} 秒后再试。", - "resetCodeRateLimited": "率限制:验证尝试次数太多。请稍后再试。", - "resetCodeRateLimitedWithTime": "验证次数过多,请稍等 {{time}} 秒后再试。", - "authTokenSaveFailed": "保存身份验证令牌失败", - "failedToLoadServer": "加载服务器失败" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "管理员已禁用新账号注册功能。请登录或联系管理员。", - "userNotAllowed": "您的帐户未被授权注册。请与管理员联系。", - "databaseConnectionFailed": "连接数据库服务器失败", - "resetCodeSent": "重置码已发送到 Docker 日志", - "codeVerified": "验证码验证成功", - "passwordResetSuccess": "密码重置成功", - "loginSuccess": "登录成功", - "registrationSuccess": "注册成功" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "针对已配置的 SSH 主机的本地桌面隧道。", - "c2sTunnelPresets": "客户端隧道预设", - "c2sTunnelPresetsDesc": "将此桌面客户端的本地隧道列表保存为服务器预设,或将预设加载到此客户端。", - "c2sTunnelPresetsUnavailable": "客户端隧道预设仅在桌面客户端可用。", - "c2sPresetName": "预设名称", - "c2sPresetNamePlaceholder": "客户端预设名称", - "c2sPresetToLoad": "预设负载", - "c2sNoPresetSelected": "未选择预设", - "c2sNoPresets": "未保存预设", - "c2sLoadPreset": "负载", - "c2sCurrentLocalConfig": "{{count}} 此桌面上配置了本地客户端隧道。", - "c2sPresetSyncNote": "预设是显式的快照;正在加载一个以替换此桌面客户端本地隧道列表。", - "c2sPresetSaved": "客户端隧道预设保存", - "c2sPresetLoaded": "客户端隧道预设本地加载", - "c2sPresetRenamed": "客户端隧道预设重命名", - "c2sPresetDeleted": "客户端隧道预设已删除", - "c2sPresetLoadError": "加载客户端隧道预设失败" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "语言", - "keyPassword": "密钥密码", - "pastePrivateKey": "把你的私钥粘贴到这里……", - "localListenerHost": "127.0.0.1 (本地监听)", - "localTargetHost": "127.0.0.1 (此计算机上的目标)", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", "socksListenerHost": "127.0.0.1 (SOCKS listener)", - "enterPassword": "请输入您的密码", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "仪表盘", - "loading": "正在加载仪表板...", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "支持", + "support": "Support", "discord": "Discord", - "serverOverview": "服务器概览", - "version": "版本", - "upToDate": "最新", - "updateAvailable": "更新可用", - "beta": "测试版", - "uptime": "运行时长", - "database": "数据库", - "healthy": "健康", - "error": "错误", - "totalHosts": "主机总数", - "totalTunnels": "隧道总数", - "totalCredentials": "凭证总数", - "recentActivity": "最近活动", - "reset": "重置", - "loadingRecentActivity": "加载最近活动...", - "noRecentActivity": "暂无最近活动", - "quickActions": "快捷操作", - "addHost": "添加主机", - "addCredential": "添加凭证", - "adminSettings": "管理员设置", - "userProfile": "用户资料", - "serverStats": "服务器统计信息", - "loadingServerStats": "加载服务器统计信息...", - "noServerData": "暂无服务器数据", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", + "beta": "Beta", + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", "cpu": "CPU", - "ram": "内存", - "customizeLayout": "自定义仪表板", - "dashboardSettings": "仪表盘设置", - "enableDisableCards": "启用/禁用卡片", - "resetLayout": "重置为默认值", - "serverOverviewCard": "服务器概述", - "recentActivityCard": "最近活动", - "networkGraphCard": "网络图", - "networkGraph": "网络图", - "quickActionsCard": "快速操作", - "serverStatsCard": "服务器统计", - "panelMain": "主要的", - "panelSide": "侧面", - "justNow": "就在这里" + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "已开始", - "hostsOnline": "在线主机", - "activeTunnels": "活动Tunnels", - "registerNewServer": "注册一个新服务器", - "storeSshKeysOrPasswords": "存储 SSH 密钥或密码", - "manageUsersAndRoles": "管理用户和角色", - "manageYourAccount": "管理您的帐户", - "hostStatus": "主机状态", - "noHostsConfigured": "未配置主机", - "online": "在线用户", - "offline": "关闭", - "onlineLower": "在线", + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", "nodes": "{{count}} nodes", - "add": "添加:", - "commandPalette": "命令调色板", - "done": "完成", - "editModeInstructions": "拖动卡片以重新排序 · 拖动列分隔符以调整列的大小 · 拖动卡的底部边缘以调整卡的高度 · 回收站以移除。", - "empty": "空的", - "clear": "清空" + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "添加主机", - "addGroup": "添加群组", - "addLink": "添加链接", - "zoomIn": "放大区域", - "zoomOut": "缩放", - "resetView": "重置视图", - "selectHost": "选择主机", - "chooseHost": "选择主机...", - "parentGroup": "上级分组", - "noGroup": "没有群组", - "groupName": "群组名称", - "color": "颜色", - "source": "来源", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", "target": "Target", - "moveToGroup": "移动到组", - "selectGroup": "选择组...", - "addConnection": "添加连接", - "hostDetails": "主机详细信息", - "removeFromGroup": "从群组中删除", - "addHostHere": "在此处添加主机", - "editGroup": "编辑分组", - "delete": "删除", - "add": "添加", - "create": "创建", - "move": "移动", - "connect": "连接", - "createGroup": "创建分组", - "selectSourcePlaceholder": "选择源...", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", "selectTargetPlaceholder": "Select Target...", - "invalidFile": "无效的文件", - "hostAlreadyExists": "主机已经在地形中", - "connectionExists": "连接已存在", - "unknown": "未知的", - "name": "名称", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "状态", - "failedToAddNode": "添加节点失败", - "sourceDifferentFromTarget": "来源和目标必须是不同的", - "exportJSON": "导出 JSON", - "importJSON": "导入 JSON", - "terminal": "终端", - "fileManager": "文件管理器", - "tunnel": "隧道:", - "docker": "停靠栏", - "serverStats": "服务器统计", - "noNodes": "尚无节点" + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", + "docker": "Docker", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "此主机未启用 Docker", - "validating": "正在验证 Docker......", - "connecting": "正在连接......", - "error": "错误", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "无法连接到 Docker", - "containerStarted": "容器 {{name}} 已启动", - "failedToStartContainer": "启动容器 {{name}} 失败", - "containerStopped": "容器 {{name}} 已停止", - "failedToStopContainer": "停止容器 {{name}} 失败", - "containerRestarted": "容器 {{name}} 已重启", - "failedToRestartContainer": "重启容器 {{name}} 失败", - "containerPaused": "容器 {{name}} 已暂停", - "containerUnpaused": "容器 {{name}} 已取消暂停", - "failedToTogglePauseContainer": "切换容器 {{name}} 暂停状态失败", - "containerRemoved": "容器 {{name}} 已移除", - "failedToRemoveContainer": "移除容器 {{name}} 失败", - "image": "镜像", - "ports": "端口", - "noPorts": "无端口", - "start": "启动", - "confirmRemoveContainer": "您确定要移除容器“{{name}}”吗?此操作无法撤销。", - "runningContainerWarning": "警告:此容器当前正在运行,请先停止后再移除。", - "loadingContainers": "加载容器列表......", - "manager": "停靠管理器", - "autoRefresh": "自动刷新", - "timestamps": "时间戳", - "lines": "直线", - "filterLogs": "过滤日志...", - "refresh": "刷新", - "download": "下载", - "clear": "清空", - "logsDownloaded": "日志下载成功", - "last50": "最近50", - "last100": "最近100", - "last500": "最后500", - "last1000": "最近 1000 个", - "allLogs": "所有日志", - "noLogsMatching": "没有与“{{query}}”匹配的日志", - "noLogsAvailable": "无可用日志", - "noContainersFound": "未找到容器", - "noContainersFoundHint": "此主机上没有可用的 Docker 容器", - "searchPlaceholder": "搜索容器……", - "allStatuses": "所有状态", - "stateRunning": "正在运行", - "statePaused": "已暂停", - "stateExited": "退出", - "stateRestarting": "重启中", - "noContainersMatchFilters": "没有容器符合您的筛选条件", - "noContainersMatchFiltersHint": "尝试调整搜索关键词或筛选条件", - "failedToFetchStats": "获取容器统计信息失败", - "containerNotRunning": "容器未运行", - "startContainerToViewStats": "启动容器以查看统计信息", - "loadingStats": "正在加载统计信息……", - "errorLoadingStats": "加载统计信息时出错", - "noStatsAvailable": "暂无统计数据", - "cpuUsage": "CPU 使用率", - "current": "当前值", - "memoryUsage": "内存使用率", - "networkIo": "网络 I/O", - "input": "流入", - "output": "流出", - "blockIo": "块 I/O", - "read": "读取", - "write": "写入", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", "pids": "PIDs", - "containerInformation": "容器信息", - "name": "名称", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "状态", - "containerMustBeRunning": "容器必须正在运行才能访问控制台", - "verificationCodePrompt": "请输入验证码", - "totpVerificationFailed": "TOTP验证失败,请重试。", - "warpgateVerificationFailed": "Warpgate 身份验证失败。请重试。", - "connectedTo": "已连接到 {{containerName}}", - "disconnected": "已断开", - "consoleError": "控制台错误", - "errorMessage": "错误:{{message}}", - "failedToConnect": "连接容器失败", - "console": "控制台", - "selectShell": "选择 Shell", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", "ash": "ash", - "connect": "连接", - "disconnect": "断开连接", - "notConnected": "未连接", - "clickToConnect": "单击“连接”以启动 shell 会话", - "connectingTo": "正在连接到 {{containerName}}......", - "containerNotFound": "未找到容器", - "backToList": "返回列表", - "logs": "日志", - "stats": "统计信息", - "consoleTab": "控制台", - "startContainerToAccess": "启动容器以访问控制台" + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "A. 概况", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "用户", - "sectionSessions": "二. 会议", - "sectionRoles": "角色", - "sectionDatabase": "数据库", - "sectionApiKeys": "API 密钥", - "allowRegistration": "允许用户注册", - "allowRegistrationDesc": "允许新用户自注册", - "allowPasswordLogin": "允许密码登录", - "allowPasswordLoginDesc": "用户名/密码登录", - "oidcAutoProvision": "OIDC 自动提供", - "oidcAutoProvisionDesc": "即使注册被禁用,也自动为OIDC用户创建帐户", - "allowPasswordReset": "允许重置密码", - "allowPasswordResetDesc": "通过 Docker 日志重置代码", - "sessionTimeout": "会话超时", - "hours": "小时", - "sessionTimeoutRange": "最小1小时 · 最大720小时", - "monitoringDefaults": "监控默认值", - "statusCheck": "状态检查", - "metrics": "指标", - "sec": "秒", - "logLevel": "日志级别", - "enableGuacamole": "启用 Guacamole", - "enableGuacamoleDesc": "RDP/VNC 远程桌面", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "配置 OpenID Connect for SSO。需要填写字段*", - "oidcClientId": "客户端ID", - "oidcClientSecret": "客户端密钥", - "oidcAuthUrl": "授权 URL", - "oidcIssuerUrl": "发行者网址", - "oidcTokenUrl": "令牌网址", - "oidcUserIdentifier": "用户标识路径", - "oidcDisplayName": "显示名称路径", - "oidcScopes": "范围", - "oidcUserinfoUrl": "覆盖用户信息URL", - "oidcAllowedUsers": "允许的用户", - "oidcAllowedUsersDesc": "每行一封电子邮件。留空以允许所有电子邮件。", - "removeOidc": "删除", - "usersCount": "{{count}} 用户", - "createUser": "创建", - "newRole": "新建角色", - "roleName": "名称", - "roleDisplayName": "显示名称", - "roleDescription": "描述", - "rolesCount": "{{count}} 角色", - "createRole": "创建", - "creating": "创建中...", - "exportDatabase": "导出数据库", - "exportDatabaseDesc": "下载所有主机、 凭据和设置的备份", - "export": "导出", - "exporting": "导出中...", - "importDatabase": "导入数据库", - "importDatabaseDesc": "从.sqlite备份文件恢复", - "importDatabaseSelected": "已选择: {{name}}", - "selectFile": "选择文件", - "changeFile": "更改", - "import": "导入", - "importing": "输入...", - "apiKeysCount": "{{count}} 键", - "newApiKey": "新 API 密钥", - "apiKeyCreatedWarning": "密钥已创建 - 现在复制,将不会再显示。", - "apiKeyName": "名称", - "apiKeyUser": "用户", - "apiKeySelectUser": "选择一个用户...", - "apiKeyExpiresAt": "到期于", - "createKey": "创建密钥", - "apiKeyNoExpiry": "无过期", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", "revokedBadge": "REVOKED", - "authTypeDual": "双重认证", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "本地的", - "adminStatusAdministrator": "管理员", - "adminStatusRegularUser": "普通用户", - "adminBadge": "管理员信息", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", "systemBadge": "SYS", - "customBadge": "抄送", - "youBadge": "您的", - "sessionsActive": "{{count}} 活跃", - "sessionActive": "当前状态: {{time}}", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "所有的", - "revokeAllSessionsSuccess": "所有用户会话已被吊销了", - "revokeAllSessionsFailed": "取消会话失败", - "revokeSessionFailed": "取消会话失败", - "addRole": "添加角色", - "noCustomRoles": "未定义自定义角色", - "removeRoleFailed": "删除角色失败", - "assignRoleFailed": "分配角色失败", - "deleteRoleFailed": "删除角色失败", - "userAdminAccess": "管理员", - "userAdminAccessDesc": "完全访问所有管理员设置", - "userRoles": "角色", - "revokeAllUserSessions": "撤销所有会话", - "revokeAllUserSessionsDesc": "强制重新登录所有设备", + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", "revoke": "Revoke", - "deleteUserWarning": "永久删除此用户。", - "deleteUser": "删除 {{username}}", - "deleting": "正在删除……", - "deleteUserFailed": "删除用户失败", - "deleteUserSuccess": "用户“{{username}}”已删除", - "deleteRoleSuccess": "角色“{{name}}”已删除", - "revokeKeySuccess": "密钥“{{name}}”已撤销", - "revokeKeyFailed": "吊销密钥失败", - "copiedToClipboard": "复制到剪贴板", - "done": "完成", - "createUserTitle": "创建用户", - "createUserDesc": "创建一个新的本地帐户。", - "createUserUsername": "用户名", - "createUserPassword": "密码", - "createUserPasswordHint": "至少 6 个字符。", - "createUserEnterUsername": "输入用户名", - "createUserEnterPassword": "输入密码", - "createUserSubmit": "创建用户", - "editUserTitle": "管理用户: {{username}}", - "editUserDesc": "编辑角色、管理状态、会话和账户设置。", - "editUserUsername": "用户名", - "editUserAuthType": "认证类型", - "editUserAdminStatus": "管理员状态", - "editUserUserId": "用户 ID", - "linkAccountTitle": "将 OIDC 链接到密码帐户", - "linkAccountDesc": "将 OIDC 帐户 {{username}} 与现有本地帐户合并。", - "linkAccountWarningTitle": "这将:", - "linkAccountEffect1": "删除仅限OIDC账户", - "linkAccountEffect2": "添加OIDC登录到目标账户", - "linkAccountEffect3": "允许同时登录 OIDC 和密码", - "linkAccountTargetUsername": "Target Username", - "linkAccountTargetPlaceholder": "输入本地帐户用户名链接到", - "linkAccounts": "链接账户", - "linkAccountSuccess": "OIDC 账户关联到“{{username}}”", - "linkAccountFailed": "OIDC账户关联失败", - "linkAccountInProgress": "正在连接……", - "saving": "保存中...", - "updateRegistrationFailed": "更新注册设置失败", - "updatePasswordLoginFailed": "更新密码登录设置失败", - "updateOidcAutoProvisionFailed": "更新OIDC自动提供设置失败", - "updatePasswordResetFailed": "更新密码重置设置失败", - "sessionTimeoutRange2": "会话超时必须介于 1 到 720 小时", - "sessionTimeoutSaved": "会话超时保存", - "sessionTimeoutSaveFailed": "保存会话超时", - "monitoringIntervalInvalid": "无效的间隔值", - "monitoringSaved": "监测设置已保存", - "monitoringSaveFailed": "保存监测设置失败", - "guacamoleSaved": "已保存关岛设置", - "guacamoleSaveFailed": "无法保存 Guacamole 设置", - "guacamoleUpdateFailed": "更新关卡莫尔设置失败", - "logLevelUpdateFailed": "更新日志级别失败", - "oidcSaved": "OIDC 配置已保存", - "oidcSaveFailed": "保存 OIDC 配置失败", - "oidcRemoved": "OIDC 配置已删除", - "oidcRemoveFailed": "删除 OIDC 配置失败", - "createUserRequired": "用户名和密码是必需的", - "createUserPasswordTooShort": "密码必须至少 6 个字符", - "createUserSuccess": "用户“{{username}}”创建", - "createUserFailed": "创建用户失败", - "updateAdminStatusFailed": "更新管理状态失败", - "allSessionsRevoked": "所有会话已取消", - "revokeSessionsFailed": "取消会话失败", - "createRoleRequired": "名称和显示名称是必填项", - "createRoleSuccess": "角色“{{name}}”已创建", - "createRoleFailed": "创建角色失败", - "apiKeyNameRequired": "密钥名称是必需的", - "apiKeyUserRequired": "必须填写用户 ID", - "apiKeyCreatedSuccess": "API 密钥“{{name}}”已创建", - "apiKeyCreateFailed": "创建 API 密钥失败", - "exportSuccess": "数据库导出成功", - "exportFailed": "数据库导出失败", - "importSelectFile": "请先选择一个文件", - "importCompleted": "导入完成:已导入 {{total}} 项,跳过 {{skipped}} 项", - "importFailed": "导入失败: {{error}}", - "importError": "数据库导入失败" + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "主机", - "hostPlaceholder": "192.168.1.1 或 example.com", - "portLabel": "端口", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "用户名", - "usernamePlaceholder": "用户名", - "authLabel": "认证", - "passwordLabel": "密码", - "passwordPlaceholder": "密碼", - "privateKeyLabel": "私钥", - "privateKeyPlaceholder": "粘贴私钥...", - "credentialLabel": "凭据", - "credentialPlaceholder": "选择保存的凭据", - "connectToTerminal": "连接到终端", - "connectToFiles": "连接到文件" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "未选择终端", - "noTerminalSelectedHint": "打开一个 SSH 终端标签来查看其命令历史", - "searchPlaceholder": "搜索历史...", - "clearAll": "全部清除", - "noHistoryEntries": "没有历史记录", - "trackingDisabled": "历史跟踪已禁用", - "trackingDisabledHint": "在您的个人资料设置中启用它来录制命令。" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "按键录制", - "recordToTerminals": "录制到终端", - "selectAll": "所有的", - "selectNone": "无", - "noTerminalTabsOpen": "未打开终端标签", - "selectTerminalsAbove": "选择上面的终端", - "broadcastInputPlaceholder": "在这里输入广播关键字...", - "stopRecording": "停止录制", - "startRecording": "开始录制", - "settingsTitle": "设置", - "enableRightClickCopyPaste": "启用右击复制/粘贴" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "布局", - "selectLayoutAbove": "选择上面的布局", - "selectLayoutHint": "选择要显示多少窗格", - "panesTitle": "面包", - "openTabsTitle": "打开标签", - "dragTabsHint": "将标签拖入上方窗格,或使用快速分配功能。", - "dropHere": "拖放到这里", - "emptyPane": "空的", - "dashboard": "仪表板", - "clearSplitScreen": "清除分屏", - "quickAssign": "快速分配", - "alreadyAssigned": "窗格 {{index}}", - "splitTab": "分页标签", - "addToSplit": "添加到拆分", - "removeFromSplit": "从拆分中移除", - "assignToPane": "分配到窗格" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "创建代码片段", - "createSnippetDescription": "创建一个新的命令代码片段以便快速执行", - "nameLabel": "名称", - "namePlaceholder": "例如,重启 Nginx", - "descriptionLabel": "描述", - "descriptionPlaceholder": "可选描述", - "optional": "可选的", - "folderLabel": "文件夹", - "noFolder": "没有文件夹(未分类)", - "commandLabel": "命令", - "commandPlaceholder": "例如: sudo systemctl 重启 nginx", - "cancel": "取消", - "createSnippetButton": "创建代码片段", - "createFolderTitle": "创建文件夹", - "createFolderDescription": "将你的代码片段整理到文件夹", - "folderNameLabel": "文件夹名称", - "folderNamePlaceholder": "例如: 系统命令, Docker 脚本", - "folderColorLabel": "文件夹颜色", - "folderIconLabel": "文件夹图标", - "previewLabel": "预览", - "folderNameFallback": "文件夹名称", - "createFolderButton": "创建文件夹", + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", "targetTerminals": "Target Terminals", - "selectAll": "所有的", - "selectNone": "无", - "noTerminalTabsOpen": "未打开终端标签", - "searchPlaceholder": "搜索代码片段...", - "newSnippet": "新片段", - "newFolder": "新建文件夹", - "run": "运行", - "noSnippetsInFolder": "此文件夹中没有代码片段", - "uncategorized": "未分类", - "editSnippetTitle": "编辑代码片段", - "editSnippetDescription": "更新此命令片段", - "saveSnippetButton": "保存更改", - "createSuccess": "代码片段创建成功", - "createFailed": "创建代码片段失败", - "updateSuccess": "代码片段更新成功", - "updateFailed": "更新代码片段失败", - "deleteFailed": "删除代码片段失败", - "folderCreateSuccess": "文件夹创建成功", - "folderCreateFailed": "创建文件夹失败", - "editFolderTitle": "编辑文件夹", - "editFolderDescription": "重命名或更改此文件夹的外观", - "saveFolderButton": "保存更改", - "editFolder": "编辑文件夹", - "deleteFolder": "删除文件夹", - "folderDeleteSuccess": "文件夹“{{name}}”已删除", - "folderDeleteFailed": "删除文件夹失败", - "folderEditSuccess": "文件夹更新成功", - "folderEditFailed": "更新文件夹失败", - "confirmRunMessage": "运行“{{name}}”?", - "confirmRunButton": "跑步", - "runSuccess": "在 {{count}} 终端中运行“{{name}}”", - "copySuccess": "已将“{{name}}”复制到剪贴板", - "shareTitle": "共享代码片段", - "shareUser": "用户", - "shareRole": "作用", - "selectUser": "选择一个用户...", - "selectRole": "选择角色...", - "shareSuccess": "代码片段共享成功", - "shareFailed": "共享代码片段失败", - "revokeSuccess": "访问已取消", - "revokeFailed": "吊销访问失败", - "currentAccess": "当前访问", - "shareLoadError": "加载共享数据失败", - "loading": "加载中...", - "close": "关闭", - "reorderFailed": "保存代码片段顺序失败" + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "账户", - "sectionAppearance": "外观", - "sectionSecurity": "安全", - "sectionApiKeys": "API 密钥", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", "sectionC2sTunnels": "C2S Tunnels", - "usernameLabel": "用户名", - "roleLabel": "作用", - "roleAdministrator": "管理员", - "authMethodLabel": "认证方法", - "authMethodLocal": "本地的", - "twoFaLabel": "两步验证", - "twoFaOn": "开启", - "twoFaOff": "关闭", - "versionLabel": "版本", - "deleteAccount": "删除帐户", - "deleteAccountDescription": "永久删除您的帐户", - "deleteButton": "删除", - "deleteAccountPermanent": "此操作是永久性的,无法撤销。", - "deleteAccountWarning": "所有会话、主机、 凭据和设置将被永久删除。", - "confirmPasswordDeletePlaceholder": "输入您的密码以确认", - "languageLabel": "语言", - "themeLabel": "主题", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", "fontSizeLabel": "Font Size", - "accentColorLabel": "亮度颜色", - "settingsTerminal": "终端", - "commandAutocomplete": "命令自动完成", - "commandAutocompleteDesc": "输入时显示自动完成", - "historyTracking": "历史跟踪", - "historyTrackingDesc": "跟踪终端命令", - "syntaxHighlighting": "语法加亮", - "syntaxHighlightingDesc": "高亮终端输出", - "commandPalette": "命令调色板", - "commandPaletteDesc": "启用键盘快捷键", - "reopenTabsOnLogin": "登录后重新打开标签页", - "reopenTabsOnLoginDesc": "即使从其他设备登录或刷新页面,也能恢复您打开的标签页。", - "confirmTabClose": "确认关闭标签", - "confirmTabCloseDesc": "关闭终端标签前询问", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", "settingsSidebar": "Sidebar", - "showHostTags": "显示主机标签", - "showHostTagsDesc": "在主机列表中显示标签", - "hostTrayOnClick": "点击展开主机操作", - "hostTrayOnClickDesc": "始终显示连接按钮;点击展开管理选项,而不是悬停。", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "保持左侧边栏应用导轨始终展开,而不是鼠标悬停时才展开。", - "settingsSnippets": "片段", - "foldersCollapsed": "折叠文件夹", - "foldersCollapsedDesc": "默认折叠文件夹", - "confirmExecution": "确认执行", - "confirmExecutionDesc": "在运行代码片段前确认", - "settingsUpdates": "更新", - "disableUpdateChecks": "禁用更新检查", - "disableUpdateChecksDesc": "停止检查更新", - "totpAuthenticator": "TOTP 身份验证器", - "totpEnabled": "2FA 已启用", - "totpDisabled": "添加额外的登录安全", - "disable": "禁用", - "enable": "启用", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", "setupTotp": "Setup TOTP", "qrCode": "QR Code", - "totpInstructions": "扫描二维码或在您的身份验证程序中输入秘密,然后输入6位数字", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "验证", - "changePassword": "更改密码", - "currentPasswordLabel": "当前密码", - "currentPasswordPlaceholder": "当前密码", - "newPasswordLabel": "新密码", - "newPasswordPlaceholder": "新密码", - "confirmPasswordLabel": "确认新密码", - "confirmPasswordPlaceholder": "确认新密码", - "updatePassword": "更新密码", - "createApiKeyTitle": "创建 API 密钥", - "createApiKeyDescription": "生成一个新的 API 密钥用于程序访问。", - "apiKeyNameLabel": "名称", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", "apiKeyNamePlaceholder": "e.g. CI Pipeline", "expiryDateLabel": "Expiry Date", - "optional": "可选的", - "cancel": "取消", - "createKey": "创建密钥", - "apiKeyCount": "{{count}} 键", - "newKey": "新建密钥", - "noApiKeys": "暂无API密钥。", - "apiKeyActive": "已启用", - "apiKeyUsageHint": "包括您的密钥在", - "apiKeyUsageHintHeader": "标题。", - "apiKeyPermissionsHint": "密钥继承创建用户的权限。", - "roleUser": "用户", - "authMethodDual": "双重认证", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "启动TOTP 设置失败", - "totpEnter6Digits": "输入一个6位数字代码", - "totpEnabledSuccess": "两步验证已启用", - "totpInvalidCode": "无效的代码,请重试", - "totpDisableInputRequired": "输入您的 TOTP 代码或密码", - "totpDisabledSuccess": "两步验证已禁用", - "totpDisableFailed": "禁用两步验证失败", - "totpDisableTitle": "禁用两步验证", - "totpDisablePlaceholder": "输入TOTP 代码或密码", - "totpDisableConfirm": "禁用两步验证", - "totpContinueVerify": "继续验证", - "totpVerifyTitle": "验证代码", - "totpBackupTitle": "备份代码", - "totpDownloadBackup": "下载备份代码", - "done": "完成", - "secretCopied": "机密已复制到剪贴板", - "apiKeyNameRequired": "密钥名称是必需的", - "apiKeyCreated": "API 密钥“{{name}}”已创建", - "apiKeyCreateFailed": "创建 API 密钥失败", - "apiKeyUser": "用户", - "apiKeyExpires": "过期时间", - "apiKeyRevoked": "API密钥“{{name}}”已撤销", - "apiKeyRevokeFailed": "吊销API密钥失败", - "passwordFieldsRequired": "需要当前密码和新密码", - "passwordMismatch": "密码不匹配", - "passwordTooShort": "密码必须至少 6 个字符", - "passwordUpdated": "密码更新成功", - "passwordUpdateFailed": "更新密码失败", - "deletePasswordRequired": "需要密码才能删除您的帐户", - "deleteFailed": "删除帐户失败", - "deleting": "正在删除...", - "colorPickerTooltip": "打开颜色选择器", - "themeSystem": "系统", - "themeLight": "亮色的", - "themeDark": "深色", - "themeDracula": "德拉库拉", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", "themeCatppuccin": "Catppuccin", "themeNord": "Nord", - "themeSolarized": "已解决的", + "themeSolarized": "Solarized", "themeTokyoNight": "Tokyo Night", - "themeOneDark": "一个深色", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/locales/translated/zh_TW.json b/src/ui/locales/translated/zh_TW.json index d269a26d..9bc5af32 100644 --- a/src/ui/locales/translated/zh_TW.json +++ b/src/ui/locales/translated/zh_TW.json @@ -1,2184 +1,2583 @@ { "credentials": { - "folders": "資料夾", - "folder": "資料夾", - "password": "密碼", - "key": "金鑰", - "sshPrivateKey": "SSH 私鑰", - "upload": "上傳", - "keyPassword": "密鑰密碼", - "sshKey": "SSH 金鑰", - "uploadPrivateKeyFile": "上傳私鑰檔案", - "searchCredentials": "搜尋憑證...", - "addCredential": "新增憑證", - "caCertificate": "CA 憑證(-cert.pub)", - "caCertificateDescription": "可選:上傳或貼上 CA 簽署的憑證檔案(例如 id_ed25519-cert.pub)。如果您的 SSH 伺服器使用基於憑證的授權,則此步驟為必要。", - "uploadCertFile": "上傳 -cert.pub 文件", - "clearCert": "清除", - "certLoaded": "證書已載入", - "certPublicKeyLabel": "CA憑證", - "certTypeLabel": "證書類型", - "pasteOrUploadCert": "貼上或上傳 -cert.pub 憑證...", - "hasCaCert": "擁有CA證書", - "noCaCert": "無加州證書" + "folders": "Folders", + "folder": "Folder", + "password": "Password", + "key": "Key", + "sshPrivateKey": "SSH Private Key", + "upload": "Upload", + "keyPassword": "Key Password", + "sshKey": "SSH Key", + "uploadPrivateKeyFile": "Upload Private Key File", + "searchCredentials": "Search credentials...", + "addCredential": "Add Credential", + "caCertificate": "CA Certificate (-cert.pub)", + "caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.", + "uploadCertFile": "Upload -cert.pub File", + "clearCert": "Clear", + "certLoaded": "Certificate loaded", + "certPublicKeyLabel": "CA Certificate", + "certTypeLabel": "Certificate type", + "pasteOrUploadCert": "Paste or upload a -cert.pub certificate...", + "hasCaCert": "Has CA Certificate", + "noCaCert": "No CA Certificate", + "noPublicKeyAvailable": "No public key available. Open the credential editor first.", + "deployCommandCopied": "Deploy command copied", + "sortCredentials": "Sort Credentials", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortUsernameAsc": "Username (A → Z)", + "sortUsernameDesc": "Username (Z → A)", + "filterCredentials": "Filter Credentials", + "filterClearAll": "Clear Filters", + "filterTypeGroup": "Type", + "filterTypePassword": "Password", + "filterTypeKey": "SSH Key", + "filterTagsGroup": "Tags" }, "homepage": { - "failedToLoadAlerts": "警示載入失敗", - "failedToDismissAlert": "未能關閉警示" + "failedToLoadAlerts": "Failed to load alerts", + "failedToDismissAlert": "Failed to dismiss alert" }, "serverConfig": { - "title": "伺服器配置", - "description": "設定 Termix 伺服器 URL 以連接到您的後端服務", - "serverUrl": "伺服器 URL", - "enterServerUrl": "請輸入伺服器網址", - "saveFailed": "配置儲存失敗", - "saveError": "儲存配置時發生錯誤", - "saving": "儲存...", - "saveConfig": "儲存配置", - "helpText": "輸入您的 Termix 伺服器運作所在的 URL(例如,http://localhost:30001 或 https://your-server.com)", - "changeServer": "變更伺服器", - "mustIncludeProtocol": "伺服器 URL 必須以 http:// 或 https:// 開頭。", - "allowInvalidCertificate": "允許無效證書", - "allowInvalidCertificateDesc": "僅適用於具有自簽名憑證或 IP 位址憑證的受信任的自託管伺服器。", - "useEmbedded": "使用本地伺服器", - "embeddedDesc": "使用內建的本機伺服器運行Termix(無需遠端伺服器)", - "embeddedConnecting": "正在連接本地伺服器...", - "embeddedNotReady": "本地伺服器尚未準備就緒,請稍等片刻後再試。", - "localServer": "本地伺服器" + "title": "Server Configuration", + "description": "Configure the Termix server URL to connect to your backend services", + "serverUrl": "Server URL", + "enterServerUrl": "Please enter a server URL", + "saveFailed": "Failed to save configuration", + "saveError": "Error saving configuration", + "saving": "Saving...", + "saveConfig": "Save Configuration", + "helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)", + "changeServer": "Change Server", + "mustIncludeProtocol": "Server URL must start with http:// or https://", + "allowInvalidCertificate": "Allow invalid certificate", + "allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.", + "useEmbedded": "Use Local Server", + "embeddedDesc": "Run Termix with the built-in local server (no remote server needed)", + "embeddedConnecting": "Connecting to local server...", + "embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.", + "localServer": "Local Server" }, "versionCheck": { - "error": "版本檢查錯誤", - "checkFailed": "檢查更新失敗", - "upToDate": "應用程式已更新至最新版本", - "currentVersion": "您正在執行版本 {{version}}", - "updateAvailable": "更新可用", - "newVersionAvailable": "新版本可用!您正在執行 {{current}},但 {{latest}} 可用。", - "betaVersion": "測試版", - "betaVersionDesc": "您正在運行 {{current}},它比最新的穩定版本 {{latest}} 更新。", - "releasedOn": "發佈日期: {{date}}", - "downloadUpdate": "下載更新", - "checking": "正在檢查更新...", - "checkUpdates": "檢查更新", - "checkingUpdates": "正在檢查更新...", - "updateRequired": "需要更新" + "error": "Version Check Error", + "checkFailed": "Failed to check for updates", + "upToDate": "App is Up to Date", + "currentVersion": "You are running version {{version}}", + "updateAvailable": "Update Available", + "newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.", + "betaVersion": "Beta Version", + "betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.", + "releasedOn": "Released on {{date}}", + "downloadUpdate": "Download Update", + "checking": "Checking for updates...", + "checkUpdates": "Check for Updates", + "checkingUpdates": "Checking for updates...", + "updateRequired": "Update Required" }, "common": { - "close": "關閉", - "minimize": "最小化", - "online": "在線的", - "offline": "離線", - "continue": "繼續", - "maintenance": "維護", - "degraded": "降級", - "error": "錯誤", - "warning": "警告", - "unsavedChanges": "未儲存的更改", - "dismiss": "解僱", - "loading": "載入中...", - "optional": "選擇性", - "connect": "連接", - "copied": "已複製", - "connecting": "正在連接...", - "updateAvailable": "更新可用", + "close": "Close", + "minimize": "Minimize", + "online": "Online", + "offline": "Offline", + "continue": "Continue", + "maintenance": "Maintenance", + "degraded": "Degraded", + "error": "Error", + "warning": "Warning", + "unsavedChanges": "Unsaved changes", + "dismiss": "Dismiss", + "loading": "Loading...", + "optional": "Optional", + "connect": "Connect", + "copied": "Copied", + "connecting": "Connecting...", + "updateAvailable": "Update Available", "appName": "Termix", - "openInNewTab": "在新分頁中開啟", - "noReleases": "無發布", - "updatesAndReleases": "更新與發布", - "newVersionAvailable": "新版本({{version}})可用。", - "failedToFetchUpdateInfo": "取得更新資訊失敗", - "preRelease": "預發布", - "noReleasesFound": "未找到任何版本。", - "cancel": "取消", - "username": "使用者名稱", - "login": "登入", - "register": "登記", - "password": "密碼", - "confirmPassword": "確認密碼", - "back": "後退", - "save": "儲存", - "saving": "儲存...", - "delete": "刪除", - "rename": "重新命名", - "edit": "編輯", - "add": "添加", - "confirm": "確認", - "no": "不", - "or": "或者", - "next": "下一個", - "previous": "以前的", - "refresh": "重新整理", - "language": "語言", - "checking": "檢查...", - "checkingDatabase": "正在檢查資料庫連線...", - "checkingAuthentication": "正在檢查身份驗證...", - "backendReconnected": "伺服器連線已恢復", - "connectionDegraded": "伺服器連線遺失,正在恢復…", - "reload": "重新載入", - "remove": "消除", - "create": "建立", - "update": "更新", - "copy": "複製", - "copyFailed": "複製到剪貼簿失敗", - "maximize": "最大化", - "restore": "恢復", - "of": "的" + "openInNewTab": "Open in New Tab", + "noReleases": "No Releases", + "updatesAndReleases": "Updates & Releases", + "newVersionAvailable": "A new version ({{version}}) is available.", + "failedToFetchUpdateInfo": "Failed to fetch update information", + "preRelease": "Pre-release", + "noReleasesFound": "No releases found.", + "cancel": "Cancel", + "username": "Username", + "login": "Login", + "register": "Register", + "password": "Password", + "confirmPassword": "Confirm Password", + "back": "Back", + "save": "Save", + "saving": "Saving...", + "delete": "Delete", + "rename": "Rename", + "edit": "Edit", + "add": "Add", + "confirm": "Confirm", + "no": "No", + "or": "OR", + "next": "Next", + "previous": "Previous", + "refresh": "Refresh", + "language": "Language", + "checking": "Checking...", + "checkingDatabase": "Checking database connection...", + "checkingAuthentication": "Checking authentication...", + "backendReconnected": "Server connection restored", + "connectionDegraded": "Server connection lost, recovering…", + "reload": "Reload", + "remove": "Remove", + "create": "Create", + "update": "Update", + "copy": "Copy", + "copyFailed": "Failed to copy to clipboard", + "maximize": "Maximize", + "restore": "Restore", + "of": "of", + "saved": "Saved", + "deleted": "Deleted", + "deleteFailed": "Failed to delete", + "saveFailed": "Failed to save", + "required": "Required" }, "nav": { - "home": "家", - "terminal": "終端機", + "home": "Home", + "terminal": "Terminal", "docker": "Docker", - "tunnels": "隧道", - "fileManager": "檔案管理器", - "serverStats": "伺服器統計訊息", - "admin": "管理員", - "userProfile": "用戶個人資料", - "splitScreen": "分割畫面", - "confirmClose": "關閉此會話?", - "close": "關閉", - "cancel": "取消", - "sshManager": "SSH 管理器", - "cannotSplitTab": "無法拆分此標籤頁", + "tunnels": "Tunnels", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "admin": "Admin", + "userProfile": "User Profile", + "splitScreen": "Split Screen", + "confirmClose": "Close this active session?", + "close": "Close", + "cancel": "Cancel", + "sshManager": "SSH Manager", + "cannotSplitTab": "Cannot split this tab", "hostTabTitle": "{{username}}@{{ip}}:{{port}}", - "copyPassword": "複製密碼", - "copySudoPassword": "複製 sudo 密碼", - "passwordCopied": "密碼已複製到剪貼簿", - "noPasswordAvailable": "沒有可用密碼", - "failedToCopyPassword": "複製密碼失敗", - "refreshTab": "刷新連接", - "openFileManager": "開啟文件管理器", - "dashboard": "儀表板", - "networkGraph": "網路圖", - "quickConnect": "快速連接", - "sshTools": "SSH 工具", - "history": "歷史", - "hosts": "主持人", - "snippets": "片段", - "hostManager": "主機管理器", - "credentials": "證書", - "connections": "連接", - "roleAdministrator": "行政人員", - "roleUser": "使用者" + "copyPassword": "Copy Password", + "copySudoPassword": "Copy Sudo Password", + "passwordCopied": "Password copied to clipboard", + "noPasswordAvailable": "No password available", + "failedToCopyPassword": "Failed to copy password", + "refreshTab": "Refresh connection", + "openFileManager": "Open File Manager", + "dashboard": "Dashboard", + "networkGraph": "Network Graph", + "tmuxMonitor": "Tmux Monitor", + "quickConnect": "Quick Connect", + "sshTools": "SSH Tools", + "history": "History", + "sessionLogs": "Session Logs", + "hosts": "Hosts", + "snippets": "Snippets", + "hostManager": "Host Manager", + "credentials": "Credentials", + "connections": "Connections", + "roleAdministrator": "Administrator", + "roleUser": "User" }, "hosts": { - "hosts": "主持人", - "noHosts": "無 SSH 主機", - "retry": "重試", - "refresh": "重新整理", - "optional": "選擇性", - "downloadSample": "下載範例", - "failedToDeleteHost": "刪除失敗 {{name}}", - "importSkipExisting": "導入(跳過現有)", - "connectionDetails": "連線詳情", + "hosts": "Hosts", + "noHosts": "No SSH Hosts", + "retry": "Retry", + "refresh": "Refresh", + "optional": "Optional", + "downloadSample": "Download Sample", + "failedToDeleteHost": "Failed to delete {{name}}", + "importSkipExisting": "Import (skip existing)", + "connectionDetails": "Connection Details", "ssh": "SSH", "telnet": "Telnet", - "remoteDesktop": "遠端桌面", - "port": "連接埠", - "username": "使用者名稱", - "folder": "資料夾", - "tags": "標籤", - "pin": "置頂", - "addHost": "新增主機", - "editHost": "編輯主機", - "cloneHost": "複製主機", - "enableTerminal": "啟用終端機", - "enableTunnel": "啟用隧道", - "enableFileManager": "啟用檔案管理器", - "enableDocker": "啟用 Docker", - "defaultPath": "預設路徑", - "connection": "聯繫", - "upload": "上傳", - "authentication": "驗證", - "password": "密碼", - "key": "金鑰", - "credential": "憑證", - "none": "沒有", - "sshPrivateKey": "SSH 私鑰", - "keyType": "金鑰類型", - "uploadFile": "上傳檔案", - "tabGeneral": "一般的", + "remoteDesktop": "Remote Desktop", + "port": "Port", + "username": "Username", + "folder": "Folder", + "tags": "Tags", + "pin": "Pin", + "addHost": "Add Host", + "editHost": "Edit Host", + "cloneHost": "Clone Host", + "enableTerminal": "Enable Terminal", + "enableTunnel": "Enable Tunnel", + "enableFileManager": "Enable File Manager", + "enableDocker": "Enable Docker", + "defaultPath": "Default Path", + "connection": "Connection", + "upload": "Upload", + "authentication": "Authentication", + "password": "Password", + "key": "Key", + "credential": "Credential", + "none": "None", + "sshPrivateKey": "SSH Private Key", + "keyType": "Key Type", + "uploadFile": "Upload File", + "tabGeneral": "General", "tabSsh": "SSH", + "tabTerminal": "Terminal", "tabRdp": "RDP", "tabVnc": "VNC", - "tabTunnels": "隧道", + "tabTunnels": "Tunnels", "tabDocker": "Docker", - "tabFiles": "文件", - "tabStats": "統計數據", + "tabFiles": "Files", + "tabStats": "Host Metrics", + "tabHostMetrics": "Host Metrics", "tabTelnet": "Telnet", - "tabSharing": "分享", - "tabAuthentication": "驗證", - "terminal": "終端機", - "tunnel": "隧道", - "fileManager": "檔案管理器", - "serverStats": "伺服器統計訊息", - "status": "地位", - "folderRenamed": "資料夾「{{oldName}}」已成功重新命名為「{{newName}}」。", - "failedToRenameFolder": "重新命名資料夾失敗", - "movedToFolder": "已移至“{{folder}}”", - "editHostTooltip": "編輯主機", - "statusChecks": "狀態檢查", - "metricsCollection": "指標收集", - "metricsInterval": "指標收集間隔", - "metricsIntervalDesc": "伺服器統計資訊收集頻率(5秒-1小時)", - "behavior": "行為", - "themePreview": "主題預覽", - "theme": "主題", - "fontFamily": "字體系列", - "fontSize": "字體大小", - "letterSpacing": "字母間距", - "lineHeight": "行高", - "cursorStyle": "游標樣式", - "cursorBlink": "游標閃爍", - "scrollbackBuffer": "回滾緩衝區", - "bellStyle": "響聲樣式", - "rightClickSelectsWord": "右鍵選擇 Word", - "fastScrollModifier": "快速滾動特殊鍵", - "fastScrollSensitivity": "快速滾動靈敏度", - "sshAgentForwarding": "SSH 代理轉發", - "backspaceMode": "後退鍵模式", - "startupSnippet": "啟動片段", - "selectSnippet": "選擇片段", - "forceKeyboardInteractive": "強制鍵盤交互", - "overrideCredentialUsername": "覆蓋憑證使用者名稱", - "overrideCredentialUsernameDesc": "請使用上面指定的用戶名,而不是憑證中的用戶名。", - "jumpHostChain": "跳轉宿主鏈", - "portKnocking": "敲擊", - "addKnock": "新增連接埠", - "addProxyNode": "新增節點", - "proxyNode": "代理節點", - "proxyType": "代理類型", - "quickActions": "快速操作", - "sudoPasswordAutoFill": "Sudo 密碼自動填充", - "sudoPassword": "Sudo 密碼", - "keepaliveInterval": "保持連線間隔(毫秒)", - "moshCommand": "MOSH 指令", - "environmentVariables": "環境變數", - "addVariable": "新增變數", + "tabSharing": "Sharing", + "tabAuthentication": "Authentication", + "terminal": "Terminal", + "tunnel": "Tunnel", + "fileManager": "File Manager", + "serverStats": "Host Metrics", + "status": "Status", + "folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully", + "failedToRenameFolder": "Failed to rename folder", + "movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"", + "editHostTooltip": "Edit host", + "statusChecks": "Status Checks", + "metricsCollection": "Metrics Collection", + "metricsInterval": "Metrics Collection Interval", + "metricsIntervalDesc": "How often to collect server statistics (5s - 1h)", + "behavior": "Behavior", + "themePreview": "Theme Preview", + "theme": "Theme", + "fontFamily": "Font Family", + "fontSize": "Font Size", + "letterSpacing": "Letter Spacing", + "lineHeight": "Line Height", + "cursorStyle": "Cursor Style", + "cursorBlink": "Cursor Blink", + "scrollbackBuffer": "Scrollback Buffer", + "bellStyle": "Bell Style", + "rightClickSelectsWord": "Right Click Selects Word", + "fastScrollModifier": "Fast Scroll Modifier", + "fastScrollSensitivity": "Fast Scroll Sensitivity", + "sshAgentForwarding": "SSH Agent Forwarding", + "backspaceMode": "Backspace Mode", + "startupSnippet": "Startup Snippet", + "selectSnippet": "Select snippet", + "forceKeyboardInteractive": "Force Keyboard-Interactive", + "overrideCredentialUsername": "Override Credential Username", + "overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username", + "jumpHostChain": "Jump Host Chain", + "portKnocking": "Port Knocking", + "addKnock": "Add Port", + "addProxyNode": "Add Node", + "proxyNode": "Proxy Node", + "proxyType": "Proxy Type", + "quickActions": "Quick Actions", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPassword": "Sudo Password", + "keepaliveInterval": "Keepalive Interval (ms)", + "moshCommand": "MOSH Command", + "environmentVariables": "Environment Variables", + "addVariable": "Add Variable", "docker": "Docker", - "copyTerminalUrl": "複製終端機 URL", - "copyFileManagerUrl": "複製檔案管理器 URL", - "copyRemoteDesktopUrl": "複製遠端桌面 URL", - "failedToConnect": "連線控制台失敗", - "connect": "連接", - "disconnect": "斷開", - "start": "開始", - "enableStatusCheck": "啟用狀態檢查", - "enableMetrics": "啟用指標", - "bulkUpdateFailed": "批量更新失敗", - "selectAll": "全選", - "deselectAll": "取消選擇所有", - "protocols": "協定", - "secureShell": "安全外殼", - "virtualNetwork": "虛擬網路", - "unencryptedShell": "未加密外殼", - "addressIp": "位址/IP", - "friendlyName": "友善名稱", - "folderAndAdvanced": "資料夾和進階功能", - "privateNotes": "私人筆記", - "privateNotesPlaceholder": "關於此伺服器的詳細資訊…", - "pinToTop": "置頂", - "pinToTopDesc": "始終將此主機顯示在清單頂部", - "portKnockingSequence": "連接埠敲擊順序", - "addKnockBtn": "新增敲擊", - "noPortKnocking": "未配置連接埠敲門。", - "knockPort": "敲擊港", - "protocol": "協定", - "delayAfterMs": "延遲時間(毫秒)", - "useSocks5Proxy": "使用 SOCKS5 代理", - "useSocks5ProxyDesc": "透過代理伺服器路由連接", - "proxyHost": "代理主機", - "proxyPort": "代理端口", - "proxyUsername": "代理用戶名", - "proxyPassword": "代理密碼", - "proxySingleMode": "單代理", - "proxyChainMode": "代理鏈", - "you": "你", - "jumpHostChainLabel": "跳轉宿主鏈", - "addJumpBtn": "添加跳轉", - "noJumpHosts": "未配置跳轉主機。", - "selectAServer": "選擇伺服器...", - "sshPort": "SSH埠", - "authMethod": "身份驗證方法", - "storedCredential": "已儲存憑證", - "selectACredential": "選擇憑證...", - "keyTypeLabel": "關鍵類型", - "keyTypeAuto": "自動偵測", - "keyPasteTab": "貼上", - "keyUploadTab": "上傳", - "keyFileLoaded": "密鑰檔案已加載", - "keyUploadClick": "點選上傳 .pem / .key / .ppk 文件", - "clearKey": "清除密鑰", - "keySaved": "SSH金鑰已儲存", - "keyReplaceNotice": "請在下方貼上新密鑰以替換它", - "keyPassphraseSaved": "密碼已儲存,輸入即可更改", - "replaceKey": "更換鑰匙", - "forceKeyboardInteractiveLabel": "強制鍵盤交互", - "forceKeyboardInteractiveShortDesc": "即使鑰匙在手,也強製手動輸入密碼。", - "terminalAppearance": "終端外觀", - "colorTheme": "顏色主題", - "fontFamilyLabel": "字體系列", - "fontSizeLabel": "字體大小", - "cursorStyleLabel": "遊標樣式", - "letterSpacingPx": "字母間距(像素)", - "lineHeightLabel": "行高", - "bellStyleLabel": "貝爾風格", - "backspaceModeLabel": "退格模式", - "cursorBlinking": "遊標閃爍", - "cursorBlinkingDesc": "啟用終端遊標閃爍動畫", - "rightClickSelectsWordLabel": "右鍵選擇 Word", - "rightClickSelectsWordShortDesc": "右鍵單擊,選擇遊標下的單字。", - "behaviorAndAdvanced": "行為與高階", - "scrollbackBufferLabel": "回滾緩衝區", - "scrollbackMaxLines": "歷史記錄中保留的最大行數", - "sshAgentForwardingLabel": "SSH代理轉發", - "sshAgentForwardingShortDesc": "將您的本機 SSH 金鑰傳遞給此主機", - "enableAutoMosh": "啟用自動 Mosh", - "enableAutoMoshDesc": "如果可用,優先選擇 Mosh 而不是 SSH。", - "enableAutoTmux": "啟用自動 Tmux", - "enableAutoTmuxDesc": "自動啟動或連線到 tmux 會話", - "sudoPasswordAutoFillLabel": "Sudo 密碼自動填充", - "sudoPasswordAutoFillShortDesc": "提示時自動提供 sudo 密碼", - "sudoPasswordLabel": "Sudo 密碼", - "environmentVariablesLabel": "環境變數", - "addVariableBtn": "新增變數", - "noEnvVars": "未配置任何環境變數。", - "fastScrollModifierLabel": "快速滾動修飾符", - "fastScrollSensitivityLabel": "快速滾動靈敏度", + "copyTerminalUrl": "Copy Terminal URL", + "copyFileManagerUrl": "Copy File Manager URL", + "copyRemoteDesktopUrl": "Copy Remote Desktop URL", + "failedToConnect": "Failed to connect to console", + "connect": "Connect", + "disconnect": "Disconnect", + "start": "Start", + "enableStatusCheck": "Enable Status Check", + "enableMetrics": "Enable Metrics", + "bulkUpdateFailed": "Bulk update failed", + "selectAll": "Select All", + "deselectAll": "Deselect All", + "protocols": "Protocols", + "secureShell": "Secure Shell", + "virtualNetwork": "Virtual Network", + "unencryptedShell": "Unencrypted shell", + "addressIp": "Address / IP", + "friendlyName": "Friendly Name", + "macAddress": "MAC Address", + "folderAndAdvanced": "Folder & Advanced", + "privateNotes": "Private Notes", + "privateNotesPlaceholder": "Details about this server...", + "pinToTop": "Pin to Top", + "pinToTopDesc": "Always show this host at the top of the list", + "portKnockingSequence": "Port Knocking Sequence", + "addKnockBtn": "Add Knock", + "noPortKnocking": "No port knocking configured.", + "knockPort": "Knock Port", + "protocol": "Protocol", + "delayAfterMs": "Delay After (ms)", + "useSocks5Proxy": "Use SOCKS5 Proxy", + "useSocks5ProxyDesc": "Route connection through a proxy server", + "proxyHost": "Proxy Host", + "proxyPort": "Proxy Port", + "proxyUsername": "Proxy Username", + "proxyPassword": "Proxy Password", + "proxySingleMode": "Single Proxy", + "proxyChainMode": "Proxy Chain", + "you": "You", + "jumpHostChainLabel": "Jump Host Chain", + "addJumpBtn": "Add Jump", + "noJumpHosts": "No jump hosts configured.", + "selectAServer": "Select a server...", + "sshPort": "SSH Port", + "authMethod": "Auth Method", + "storedCredential": "Stored Credential", + "selectACredential": "Select a credential...", + "keyTypeLabel": "Key Type", + "keyTypeAuto": "Auto Detect", + "keyPasteTab": "Paste", + "keyUploadTab": "Upload", + "keyFileLoaded": "Key file loaded", + "keyUploadClick": "Click to upload .pem / .key / .ppk", + "clearKey": "Clear key", + "keySaved": "SSH key saved", + "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", + "replaceKey": "Replace key", + "docsLink": "View docs", + "opksshLabel": "OPKSSH", + "opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.", + "tailscaleDeviceSelect": "Select Tailscale device", + "tailscaleDeviceSelectPlaceholder": "Select a device...", + "tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.", + "tailscaleDocsLink": "View docs", + "tailscaleLoadingDevices": "Loading devices...", + "tailscaleNoDevices": "No devices found in your tailnet.", + "tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.", + "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", + "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", + "terminalAppearance": "Terminal Appearance", + "colorTheme": "Color Theme", + "fontFamilyLabel": "Font Family", + "fontSizeLabel": "Font Size", + "cursorStyleLabel": "Cursor Style", + "letterSpacingPx": "Letter Spacing (px)", + "lineHeightLabel": "Line Height", + "bellStyleLabel": "Bell Style", + "backspaceModeLabel": "Backspace Mode", + "cursorBlinking": "Cursor Blinking", + "cursorBlinkingDesc": "Enable blinking animation for the terminal cursor", + "rightClickSelectsWordLabel": "Right-click Selects Word", + "rightClickSelectsWordShortDesc": "Select the word under cursor on right-click", + "syntaxHighlightingLabel": "Syntax Highlighting", + "syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)", + "syntaxHighlightingCategories": "Highlight Categories", + "syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize", + "syntaxCategoryLogLevels": "Log Levels", + "syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal", + "syntaxCategoryPaths": "File Paths", + "syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt", + "syntaxCategoryTimestamps": "Timestamps", + "syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15", + "syntaxCategoryIpAddresses": "IP Addresses", + "syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080", + "syntaxCategoryUrls": "URLs", + "syntaxCategoryUrlsDesc": "https://example.com", + "syntaxCategoryNumbers": "Labeled Numbers", + "syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404", + "behaviorAndAdvanced": "Behavior & Advanced", + "scrollbackBufferLabel": "Scrollback Buffer", + "scrollbackMaxLines": "Maximum number of lines kept in history", + "sshAgentForwardingLabel": "SSH Agent Forwarding", + "sshAgentForwardingShortDesc": "Pass your local SSH keys to this host", + "enableAutoMosh": "Enable Auto-Mosh", + "enableAutoMoshDesc": "Prefer Mosh over SSH if available", + "enableAutoTmux": "Enable Auto-Tmux", + "enableAutoTmuxDesc": "Automatically launch or attach to tmux session", + "enableSessionLogging": "Session Logging", + "enableSessionLoggingDesc": "Record terminal session output for later review", + "enableCommandHistory": "Command History", + "enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete", + "sudoPasswordAutoFillLabel": "Sudo Password Auto-fill", + "sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted", + "sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.", + "sudoPasswordLabel": "Sudo Password", + "environmentVariablesLabel": "Environment Variables", + "addVariableBtn": "Add Variable", + "noEnvVars": "No environment variables configured.", + "fastScrollModifierLabel": "Fast Scroll Modifier", + "fastScrollSensitivityLabel": "Fast Scroll Sensitivity", "moshCommandLabel": "Mosh Command", - "startupSnippetLabel": "啟動片段", - "keepaliveIntervalLabel": "保持連線間隔(秒)", - "maxKeepaliveMisses": "最大保命失誤", - "tunnelSettings": "隧道設定", - "enableTunneling": "啟用隧道", - "enableTunnelingDesc": "為此主機啟用 SSH 隧道功能", - "serverTunnelsSection": "伺服器隧道", - "addTunnelBtn": "添加隧道", - "noTunnelsConfigured": "未配置隧道。", - "tunnelLabel": "隧道 {{number}}", - "tunnelType": "隧道類型", - "tunnelModeLocalDesc": "將本機連接埠轉送到遠端伺服器(或可從遠端伺服器存取的主機)上的連接埠。", - "tunnelModeRemoteDesc": "將遠端伺服器上的連接埠轉送到您電腦上的本機連接埠。", - "tunnelModeDynamicDesc": "在本機連接埠上建立 SOCKS5 代理,用於動態連接埠轉送。", - "sameHost": "此主機(直接隧道)", - "endpointHost": "端點主機", - "endpointPort": "端點埠", - "bindHost": "綁定主機", - "sourcePort": "來源連接埠", - "maxRetries": "最大重試次數", - "retryIntervalS": "重試間隔(秒)", - "autoStartLabel": "自動啟動", - "autoStartDesc": "主機載入完畢後自動連線此隧道", - "tunnelConnecting": "隧道連接…", - "tunnelDisconnected": "隧道斷開", - "failedToConnectTunnel": "連線失敗", - "failedToDisconnectTunnel": "斷開連線失敗", - "dockerIntegration": "Docker 集成", - "enableDockerMonitor": "啟用 Docker", - "enableDockerMonitorDesc": "透過 Docker 監控和管理此主機上的容器", - "enableFileManagerMonitor": "啟用文件管理器", - "enableFileManagerMonitorDesc": "透過 SFTP 瀏覽和管理此主機上的文件", - "defaultPathLabel": "預設路徑", - "fileManagerPathHint": "當此主機上的檔案管理器啟動時要開啟的目錄。", - "statusChecksLabel": "狀態檢查", - "enableStatusChecks": "啟用狀態檢查", - "enableStatusChecksDesc": "定期 ping 此主機以驗證其可用性", - "useGlobalInterval": "使用全域間隔", - "useGlobalIntervalDesc": "使用伺服器範圍的狀態檢查間隔進行覆蓋", - "checkIntervalS": "檢查間隔(秒)", - "checkIntervalDesc": "每次連接 ping 之間的秒數", - "metricsCollectionLabel": "指標收集", - "enableMetricsLabel": "啟用指標", - "enableMetricsDesc": "收集此主機的 CPU、記憶體、磁碟和網路使用情況", - "useGlobalMetrics": "使用全域間隔", - "useGlobalMetricsDesc": "使用伺服器範圍的指標間隔進行覆蓋", - "metricsIntervalS": "指標間隔(秒)", - "metricsIntervalDesc2": "指標快照之間的秒數", - "visibleWidgets": "可見小部件", - "cpuUsageLabel": "CPU 使用率", - "cpuUsageDesc": "CPU 使用率、平均負載、迷你圖", - "memoryLabel": "記憶體使用情況", - "memoryDesc": "記憶體使用情況、交換空間、緩存", - "storageLabel": "磁碟使用情況", - "storageDesc": "每個掛載點的磁碟使用情況", - "networkLabel": "網路介面", - "networkDesc": "介面列表和頻寬", - "uptimeLabel": "正常運作時間", - "uptimeDesc": "系統運作時間和啟動時間", - "systemInfoLabel": "系統資訊", - "systemInfoDesc": "作業系統、核心、主機名稱、架構", - "recentLoginsLabel": "最近登入", - "recentLoginsDesc": "登入成功和失敗事件", - "topProcessesLabel": "頂級流程", - "topProcessesDesc": "PID、CPU%、記憶體%、指令", - "listeningPortsLabel": "監聽埠", - "listeningPortsDesc": "打開帶有進程和狀態的端口", - "firewallLabel": "防火牆", - "firewallDesc": "防火牆、AppArmor、SELinux 狀態", - "quickActionsLabel": "快速操作", - "quickActionsToolbar": "快速操作會以按鈕的形式顯示在伺服器統計工具列中,只需單擊即可執行命令。", - "noQuickActions": "目前暫無快速行動。", - "buttonLabel": "按鈕標籤", - "selectSnippetPlaceholder": "選擇片段…", - "addActionBtn": "新增操作", - "hostSharedSuccessfully": "主機共享成功", - "failedToShareHost": "共享主機失敗", - "accessRevoked": "存取權限已撤銷", - "failedToRevokeAccess": "撤銷存取權限失敗", - "cancelBtn": "取消", - "savingBtn": "儲存...", - "addHostBtn": "新增主機", - "hostUpdated": "主機已更新", - "hostCreated": "主機已建立", - "failedToSave": "儲存主機失敗", - "credentialUpdated": "憑證已更新", - "credentialCreated": "已建立憑證", - "failedToSaveCredential": "儲存憑證失敗", - "backToHosts": "返回主持人", - "backToCredentials": "返回憑證", - "pinned": "置頂", - "noHostsFound": "未找到主機", - "tryDifferentTerm": "換個說法", - "addFirstHost": "新增您的第一個主機即可開始", - "noCredentialsFound": "未找到憑證", - "addCredentialBtn": "新增憑證", - "updateCredentialBtn": "更新憑證", - "features": "特徵", - "noFolder": "(無資料夾)", - "deleteSelected": "刪除", - "exitSelection": "出口選擇", - "importSkip": "導入(跳過現有)", - "importOverwrite": "導入(覆蓋)", - "collapseBtn": "坍塌", - "importExportBtn": "進出口", - "hostStatusesRefreshed": "主機狀態已刷新", - "failedToRefreshHosts": "刷新主機失敗", - "movedHostTo": "將 {{host}} 移到 \"{{folder}}\"", - "failedToMoveHost": "主機遷移失敗", - "folderRenamedTo": "資料夾已重新命名為“{{name}}”", - "deletedFolder": "已刪除資料夾“{{name}}”", - "failedToDeleteFolder": "刪除資料夾失敗", - "deleteAllInFolder": "刪除「{{name}}」中的所有主機?此操作無法撤銷。", - "deletedHost": "已刪除 {{name}}", - "copiedToClipboard": "已複製到剪貼簿", - "terminalUrlCopied": "已複製終端 URL", - "fileManagerUrlCopied": "文件管理器 URL 已複製", - "tunnelUrlCopied": "隧道 URL 已複製", - "dockerUrlCopied": "Docker URL 已複製", - "serverStatsUrlCopied": "伺服器統計資料 URL 已複製", - "rdpUrlCopied": "複製的 RDP URL", - "vncUrlCopied": "VNC URL 已複製", - "telnetUrlCopied": "Telnet URL 已複製", - "remoteDesktopUrlCopied": "已複製遠端桌面 URL", - "expandActions": "展開操作", - "collapseActions": "坍塌動作", - "wakeOnLanAction": "區域網路喚醒", - "wakeOnLanSuccess": "魔法資料包已寄至 {{name}}", - "wakeOnLanError": "發送魔術包失敗", - "cloneHostAction": "複製主機", - "copyAddress": "副本地址", - "copyLink": "複製連結", - "copyTerminalUrlAction": "複製終端 URL", - "copyFileManagerUrlAction": "複製文件管理器 URL", - "copyTunnelUrlAction": "複製隧道 URL", - "copyDockerUrlAction": "複製 Docker URL", - "copyServerStatsUrlAction": "複製伺服器統計資料 URL", - "copyRdpUrlAction": "複製 RDP URL", - "copyVncUrlAction": "複製 VNC URL", - "copyTelnetUrlAction": "複製 Telnet URL", - "copyRemoteDesktopUrlAction": "複製遠端桌面 URL", - "deleteCredentialConfirm": "刪除憑證「{{name}}」?", - "deletedCredential": "已刪除 {{name}}", - "deploySSHKeyTitle": "部署 SSH 金鑰", - "deployingBtn": "正在部署…", - "deployBtn": "部署", - "failedToDeployKey": "金鑰部署失敗", - "deleteHostsConfirm": "刪除 {{count}} 主機{{plural}}?此操作無法撤銷。", - "movedToRoot": "已移至根目錄", - "failedToMoveHosts": "主機遷移失敗", - "enableTerminalFeature": "啟用終端", - "disableTerminalFeature": "禁用終端", - "enableFilesFeature": "啟用文件", - "disableFilesFeature": "停用檔案", - "enableTunnelsFeature": "啟用隧道", - "disableTunnelsFeature": "禁用隧道", - "enableDockerFeature": "啟用 Docker", - "disableDockerFeature": "禁用 Docker", - "addTagsPlaceholder": "添加標籤…", - "authDetails": "身份驗證詳情", - "credType": "類型", - "generateKeyPairDesc": "產生新的金鑰對,私鑰和公鑰將自動填入。", - "generatingKey": "正在生成...", - "generateLabel": "生成 {{label}}", - "uploadFileBtn": "上傳文件", - "keyPassphraseOptional": "密鑰口令(可選)", - "sshPublicKeyOptional": "SSH 公鑰(可選)", - "publicKeyGenerated": "產生的公鑰", - "failedToGeneratePublicKey": "無法匯出公鑰", - "publicKeyCopied": "公鑰已複製", + "startupSnippetLabel": "Startup Snippet", + "keepaliveIntervalLabel": "Keepalive Interval (seconds)", + "maxKeepaliveMisses": "Max Keepalive Misses", + "tunnelSettings": "Tunnel Settings", + "enableTunneling": "Enable Tunneling", + "enableTunnelingDesc": "Enable SSH tunnel functionality for this host", + "serverTunnelsSection": "Server Tunnels", + "addTunnelBtn": "Add Tunnel", + "noTunnelsConfigured": "No tunnels configured.", + "tunnelLabel": "Tunnel {{number}}", + "tunnelType": "Tunnel Type", + "tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).", + "tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.", + "tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.", + "sameHost": "This host (direct tunnel)", + "endpointHost": "Endpoint Host", + "endpointPort": "Endpoint Port", + "bindHost": "Bind Host", + "sourcePort": "Source Port", + "maxRetries": "Max Retries", + "retryIntervalS": "Retry Interval (s)", + "autoStartLabel": "Auto-start", + "autoStartDesc": "Automatically connect this tunnel when the host is loaded", + "tunnelConnecting": "Tunnel connecting...", + "tunnelDisconnected": "Tunnel disconnected", + "failedToConnectTunnel": "Failed to connect", + "failedToDisconnectTunnel": "Failed to disconnect", + "dockerIntegration": "Docker Integration", + "enableDockerMonitor": "Enable Docker", + "enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker", + "enableTmuxMonitor": "Enable Tmux Monitor", + "enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar", + "tabProxmox": "Proxmox", + "proxmoxIntegration": "Proxmox Integration", + "enableProxmox": "Enable Proxmox", + "enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.", + "proxmoxDefaultCredential": "Default Credential", + "proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts. The credential's username is applied to all guests.", + "proxmoxWindowsDetection": "Windows / RDP detection", + "proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)", + "proxmoxDockerDetection": "Docker detection", + "proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests", + "proxmoxPreferredRanges": "Preferred IP ranges", + "proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces", + "proxmoxDiscoverAction": "Discover & import Proxmox guests", + "proxmoxImportTitle": "Import from Proxmox", + "proxmoxSelectHost": "Select a Proxmox host…", + "proxmoxDiscover": "Discover", + "proxmoxDiscovering": "Discovering…", + "proxmoxDiscoverGuests": "Discover guests", + "proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected", + "proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected", + "proxmoxSelectAll": "Select all", + "proxmoxDeselectAll": "Deselect all", + "proxmoxNoGuests": "No guests found on this Proxmox node.", + "proxmoxImportButton_one": "Import {{count}} host", + "proxmoxImportButton_other": "Import {{count}} hosts", + "proxmoxResultImported": "{{count}} imported", + "proxmoxResultUpdated": "{{count}} updated", + "proxmoxResultFailed": "{{count}} failed", + "proxmoxImportComplete": "Proxmox import complete: {{summary}}", + "proxmoxDiscoveryFailed": "Discovery failed", + "proxmoxImportFailed": "Import failed", + "enableFileManagerMonitor": "Enable File Manager", + "enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP", + "defaultPathLabel": "Default Path", + "fileManagerPathHint": "The directory to open when the file manager launches for this host.", + "statusChecksLabel": "Status Checks", + "enableStatusChecks": "Enable Status Checks", + "enableStatusChecksDesc": "Periodically ping this host to verify availability", + "useGlobalInterval": "Use Global Interval", + "useGlobalIntervalDesc": "Override with the server-wide status check interval", + "checkIntervalS": "Check Interval (s)", + "checkIntervalDesc": "Seconds between each connectivity ping", + "metricsCollectionLabel": "Metrics Collection", + "enableMetricsLabel": "Enable Metrics", + "enableMetricsDesc": "Collect CPU, RAM, disk, and network usage from this host", + "useGlobalMetrics": "Use Global Interval", + "useGlobalMetricsDesc": "Override with the server-wide metrics interval", + "metricsIntervalS": "Metrics Interval (s)", + "metricsIntervalDesc2": "Seconds between metric snapshots", + "visibleWidgets": "Visible Widgets", + "widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.", + "cpuUsageLabel": "CPU Usage", + "cpuUsageDesc": "CPU percent, load averages, sparkline graph", + "memoryLabel": "Memory Usage", + "memoryDesc": "RAM usage, swap, cached", + "storageLabel": "Disk Usage", + "storageDesc": "Disk usage per mount point", + "networkLabel": "Network Interfaces", + "networkDesc": "Interface list and bandwidth", + "uptimeLabel": "Uptime", + "uptimeDesc": "System uptime and boot time", + "systemInfoLabel": "System Info", + "systemInfoDesc": "OS, kernel, hostname, architecture", + "recentLoginsLabel": "Recent Logins", + "recentLoginsDesc": "Successful and failed login events", + "topProcessesLabel": "Top Processes", + "topProcessesDesc": "PID, CPU%, MEM%, command", + "listeningPortsLabel": "Listening Ports", + "listeningPortsDesc": "Open ports with process and state", + "firewallLabel": "Firewall", + "firewallDesc": "Firewall, AppArmor, SELinux status", + "quickActionsLabel": "Quick Actions", + "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.", + "noQuickActions": "No quick actions yet.", + "buttonLabel": "Button label", + "selectSnippetPlaceholder": "Select snippet...", + "addActionBtn": "Add Action", + "hostSharedSuccessfully": "Host shared successfully", + "failedToShareHost": "Failed to share host", + "accessRevoked": "Access revoked", + "failedToRevokeAccess": "Failed to revoke access", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "addHostBtn": "Add Host", + "hostUpdated": "Host updated", + "hostCreated": "Host created", + "failedToSave": "Failed to save host", + "credentialUpdated": "Credential updated", + "credentialCreated": "Credential created", + "failedToSaveCredential": "Failed to save credential", + "backToHosts": "Back to Hosts", + "backToCredentials": "Back to Credentials", + "pinned": "Pinned", + "noHostsFound": "No hosts found", + "tryDifferentTerm": "Try a different term", + "addFirstHost": "Add your first host to get started", + "noCredentialsFound": "No credentials found", + "addCredentialBtn": "Add Credential", + "updateCredentialBtn": "Update Credential", + "features": "Features", + "noFolder": "(No folder)", + "deleteSelected": "Delete", + "exitSelection": "Exit selection", + "importSkip": "Import (skip existing)", + "importOverwrite": "Import (overwrite)", + "collapseBtn": "Collapse", + "importExportBtn": "Import / Export", + "hostStatusesRefreshed": "Host statuses refreshed", + "failedToRefreshHosts": "Failed to refresh hosts", + "movedHostTo": "Moved {{host}} to \"{{folder}}\"", + "failedToMoveHost": "Failed to move host", + "folderRenamedTo": "Folder renamed to \"{{name}}\"", + "deletedFolder": "Deleted folder \"{{name}}\"", + "failedToDeleteFolder": "Failed to delete folder", + "deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.", + "folderPickerPlaceholder": "No folder", + "folderPickerSearch": "Search or create (use / for subfolders)...", + "folderPickerNone": "No folder", + "folderPickerCreate": "Create \"{{path}}\"", + "folderPickerEmpty": "No matching folders", + "newFolder": "New folder", + "createFolderTitle": "Create folder", + "editFolderTitle": "Edit folder", + "folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.", + "folderNameLabel": "Folder name", + "folderNamePlaceholder": "e.g. Production/Web", + "folderNestingHint": "Use / to separate levels and create nested folders.", + "folderColor": "Color", + "folderIcon": "Icon", + "folderPreview": "Preview", + "folderNameFallback": "Untitled folder", + "createFolderButton": "Create folder", + "saveFolderButton": "Save folder", + "cancel": "Cancel", + "iconSearchPlaceholder": "Search icons...", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderSaved": "Folder saved", + "failedToSaveFolder": "Failed to save folder", + "folderDeleted": "Deleted folder \"{{name}}\"", + "deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.", + "failedToMoveHosts": "Failed to move hosts", + "expandAll": "Expand all folders", + "collapseAll": "Collapse all folders", + "moreActions": "More", + "groupBy": "Group by", + "GroupByFolder": "Folder", + "GroupByTag": "Tag", + "GroupByStatus": "Status", + "GroupByProtocol": "Protocol", + "GroupByAuth": "Auth type", + "groupUngrouped": "Ungrouped", + "deletedHost": "Deleted {{name}}", + "copiedToClipboard": "Copied to clipboard", + "terminalUrlCopied": "Terminal URL copied", + "fileManagerUrlCopied": "File Manager URL copied", + "tunnelUrlCopied": "Tunnel URL copied", + "dockerUrlCopied": "Docker URL copied", + "hostMetricsUrlCopied": "Host Metrics URL copied", + "tmuxMonitorUrlCopied": "Tmux Monitor URL copied", + "rdpUrlCopied": "RDP URL copied", + "vncUrlCopied": "VNC URL copied", + "telnetUrlCopied": "Telnet URL copied", + "remoteDesktopUrlCopied": "Remote Desktop URL copied", + "expandActions": "Expand actions", + "collapseActions": "Collapse actions", + "wakeOnLanAction": "Wake on LAN", + "wakeOnLanSuccess": "Magic packet sent to {{name}}", + "wakeOnLanError": "Failed to send magic packet", + "cloneHostAction": "Clone Host", + "copyAddress": "Copy Address", + "copyLink": "Copy Link", + "copyTerminalUrlAction": "Copy Terminal URL", + "copyFileManagerUrlAction": "Copy File Manager URL", + "copyTunnelUrlAction": "Copy Tunnel URL", + "copyDockerUrlAction": "Copy Docker URL", + "copyHostMetricsUrlAction": "Copy Host Metrics URL", + "copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL", + "copyRdpUrlAction": "Copy RDP URL", + "copyVncUrlAction": "Copy VNC URL", + "copyTelnetUrlAction": "Copy Telnet URL", + "copyRemoteDesktopUrlAction": "Copy Remote Desktop URL", + "deleteCredentialConfirm": "Delete credential \"{{name}}\"?", + "deletedCredential": "Deleted {{name}}", + "deploySSHKeyTitle": "Deploy SSH Key", + "deployingBtn": "Deploying...", + "deployBtn": "Deploy", + "failedToDeployKey": "Failed to deploy key", + "deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.", + "movedToRoot": "Moved to root", + "enableTerminalFeature": "Enable Terminal", + "disableTerminalFeature": "Disable Terminal", + "enableFilesFeature": "Enable Files", + "disableFilesFeature": "Disable Files", + "enableTunnelsFeature": "Enable Tunnels", + "disableTunnelsFeature": "Disable Tunnels", + "enableDockerFeature": "Enable Docker", + "disableDockerFeature": "Disable Docker", + "enableProxmoxFeature": "Enable Proxmox", + "disableProxmoxFeature": "Disable Proxmox", + "addTagsPlaceholder": "Add tags...", + "authDetails": "Authentication Details", + "credType": "Type", + "generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.", + "generatingKey": "Generating...", + "generateLabel": "Generate {{label}}", + "uploadFileBtn": "Upload file", + "keyPassphraseOptional": "Key Passphrase (Optional)", + "sshPublicKeyOptional": "SSH Public Key (Optional)", + "publicKeyGenerated": "Public key generated", + "failedToGeneratePublicKey": "Failed to derive public key", + "publicKeyCopied": "Public key copied", "keyPairGenerated": "{{label}} key pair generated", - "failedToGenerateKeyPair": "密鑰對產生失敗", - "searchHostsPlaceholder": "搜尋主機、位址、標籤…", - "searchCredentialsPlaceholder": "搜尋憑證…", - "refreshBtn": "重新整理", - "addTag": "添加標籤…", - "deleteConfirmBtn": "刪除", - "tunnelRequirementsText": "SSH 伺服器必須在 /etc/ssh/sshd_config 中設定 GatewayPorts yes、AllowTcpForwarding yes 和 PermitRootLogin yes。", - "deleteHostConfirm": "刪除“{{name}}”?", - "enableAtLeastOneProtocol": "啟用上述至少一種協定以配置身份驗證和連線設定。", - "keyPassphrase": "密鑰口令", - "connectBtn": "連接", - "disconnectBtn": "斷開", - "basicInformation": "基本訊息", - "authDetailsSection": "身份驗證詳情", - "credTypeLabel": "類型", - "hostsTab": "主持人", - "credentialsTab": "證書", - "selectMultiple": "選擇多個", - "selectHosts": "選擇主機", - "connectionLabel": "聯繫", - "authenticationLabel": "驗證", - "generateKeyPairTitle": "產生密鑰對", - "generateKeyPairDescription": "產生新的金鑰對,私鑰和公鑰將自動填入。", - "generateFromPrivateKey": "從私鑰生成", - "refreshBtn2": "重新整理", - "exitSelectionTitle": "出口選擇", - "exportAll": "全部導出", - "addHostBtn2": "新增主機", - "addCredentialBtn2": "新增憑證", - "checkingHostStatuses": "正在檢查主機狀態...", - "pinnedSection": "置頂", - "hostsExported": "主機匯出成功", - "exportFailed": "匯出主機失敗", - "sampleDownloaded": "下載範例文件", - "failedToDeleteCredential2": "刪除憑證失敗", - "noFolderOption": "(無資料夾)", - "nSelected": "{{count}} 已選擇", - "featuresMenu": "特徵", - "moveMenu": "移動", - "cancelSelection": "取消", - "deployDialogDesc": "將 {{name}} 部署到主機的 authorized_keys 中。", - "targetHostLabel": "目標主機", - "selectHostOption": "選擇主機...", - "keyDeployedSuccess": "金鑰已成功部署", - "failedToDeployKey2": "金鑰部署失敗", - "deletedCount": "已刪除 {{count}} 主機", - "failedToDeleteCount": "刪除 {{count}} 主機失敗", - "duplicatedHost": "重複的“{{name}}”", - "failedToDuplicateHost": "複製主機失敗", - "updatedCount": "已更新 {{count}} 主機", - "friendlyNameLabel": "友善名稱", - "descriptionLabel": "描述", - "loadingHost": "正在加載主機...", - "loadingHosts": "正在載入主機……", - "loadingCredentials": "正在加載憑證...", - "noHostsYet": "目前還沒有主持人", - "noHostsMatchSearch": "沒有符合您搜尋條件的主機", - "hostNotFound": "未找到主機", - "searchHosts": "搜尋主機...", - "sortHosts": "排序主機", - "sortDefault": "預設訂單", - "sortNameAsc": "姓名(A → Z)", - "sortNameDesc": "姓名(Z → A)", - "sortIpAsc": "IP位址(升序)", - "sortIpDesc": "IP位址(說明)", - "sortOnlineFirst": "線上優先", - "sortOfflineFirst": "離線優先", - "sortPinnedFirst": "置頂", - "filterHosts": "過濾主機", - "filterClearAll": "清除篩選條件", - "filterStatusGroup": "地位", - "filterOnline": "在線的", - "filterOffline": "離線", - "filterPinned": "置頂", - "filterAuthGroup": "身份驗證類型", - "filterAuthPassword": "密碼", - "filterAuthKey": "SSH金鑰", - "filterAuthCredential": "憑證", - "filterAuthNone": "沒有任何", + "failedToGenerateKeyPair": "Failed to generate key pair", + "searchHostsPlaceholder": "Search hosts, addresses, tags…", + "searchCredentialsPlaceholder": "Search credentials…", + "refreshBtn": "Refresh", + "addTag": "Add tags...", + "deleteConfirmBtn": "Delete", + "tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.", + "deleteHostConfirm": "Delete \"{{name}}\"?", + "enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.", + "keyPassphrase": "Key Passphrase", + "connectBtn": "Connect", + "disconnectBtn": "Disconnect", + "basicInformation": "Basic Information", + "authDetailsSection": "Authentication Details", + "credTypeLabel": "Type", + "hostsTab": "Hosts", + "credentialsTab": "Credentials", + "selectMultiple": "Select multiple", + "selectHosts": "Select hosts", + "connectionLabel": "Connection", + "authenticationLabel": "Authentication", + "generateKeyPairTitle": "Generate Key Pair", + "generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.", + "generateFromPrivateKey": "Generate from Private Key", + "refreshBtn2": "Refresh", + "exitSelectionTitle": "Exit selection", + "exportAll": "Export All", + "addHostBtn2": "Add Host", + "addCredentialBtn2": "Add Credential", + "checkingHostStatuses": "Checking host statuses...", + "pinnedSection": "Pinned", + "hostsExported": "Hosts exported successfully", + "exportFailed": "Failed to export hosts", + "sampleDownloaded": "Sample file downloaded", + "failedToDeleteCredential2": "Failed to delete credential", + "noFolderOption": "(No folder)", + "nSelected": "{{count}} selected", + "featuresMenu": "Features", + "moveMenu": "Move", + "cancelSelection": "Cancel", + "deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.", + "targetHostLabel": "Target Host", + "selectHostOption": "Select a host...", + "keyDeployedSuccess": "Key deployed successfully", + "failedToDeployKey2": "Failed to deploy key", + "deletedCount": "Deleted {{count}} hosts", + "failedToDeleteCount": "Failed to delete {{count}} hosts", + "duplicatedHost": "Duplicated \"{{name}}\"", + "failedToDuplicateHost": "Failed to duplicate host", + "updatedCount": "Updated {{count}} hosts", + "friendlyNameLabel": "Friendly Name", + "descriptionLabel": "Description", + "loadingHost": "Loading host...", + "loadingHosts": "Loading hosts...", + "loadingCredentials": "Loading credentials...", + "noHostsYet": "No hosts yet", + "noHostsMatchSearch": "No hosts match your search", + "hostNotFound": "Host not found", + "searchHosts": "Search hosts...", + "sortHosts": "Sort Hosts", + "sortDefault": "Default Order", + "sortNameAsc": "Name (A → Z)", + "sortNameDesc": "Name (Z → A)", + "sortIpAsc": "IP Address (Asc)", + "sortIpDesc": "IP Address (Desc)", + "sortOnlineFirst": "Online First", + "sortOfflineFirst": "Offline First", + "sortPinnedFirst": "Pinned First", + "filterHosts": "Filter Hosts", + "filterClearAll": "Clear Filters", + "filterStatusGroup": "Status", + "filterOnline": "Online", + "filterOffline": "Offline", + "filterPinned": "Pinned", + "filterAuthGroup": "Auth Type", + "filterAuthPassword": "Password", + "filterAuthKey": "SSH Key", + "filterAuthCredential": "Credential", + "filterAuthNone": "None", "filterAuthOpkssh": "OPKSSH", - "filterProtocolGroup": "協定", + "filterProtocolGroup": "Protocol", "filterProtocolSsh": "SSH", - "filterProtocolRdp": "遠端桌面協定", + "filterProtocolRdp": "RDP", "filterProtocolVnc": "VNC", "filterProtocolTelnet": "Telnet", - "filterFeaturesGroup": "特徵", - "filterFeatureTerminal": "終端", - "filterFeatureFileManager": "文件管理器", - "filterFeatureTunnel": "隧道", + "filterFeaturesGroup": "Features", + "filterFeatureTerminal": "Terminal", + "filterFeatureFileManager": "File Manager", + "filterFeatureTunnel": "Tunnel", "filterFeatureDocker": "Docker", - "filterTagsGroup": "標籤", - "shareHost": "共享主機", - "shareHostTitle": "分享: {{name}}", + "filterTagsGroup": "Tags", + "shareHost": "Share Host", + "shareHostTitle": "Share: {{name}}", "sharing": { - "requiresCredential": "此主機必須使用憑證才能啟用共用。請先編輯主機並指派憑證。" + "requiresCredential": "This host must use a credential to enable sharing. Edit the host and assign a credential first." }, "guac": { - "connection": "聯繫", - "authentication": "驗證", - "connectionSettings": "連接設定", - "displaySettings": "顯示設定", - "audioSettings": "音訊設定", - "rdpPerformance": "RDP性能", - "deviceRedirection": "裝置重定向", - "session": "會議", - "gateway": "閘道", + "connection": "Connection", + "authentication": "Authentication", + "connectionSettings": "Connection Settings", + "displaySettings": "Display Settings", + "audioSettings": "Audio Settings", + "rdpPerformance": "RDP Performance", + "deviceRedirection": "Device Redirection", + "session": "Session", + "gateway": "Gateway", "remoteApp": "RemoteApp", - "clipboard": "剪貼簿", - "sessionRecording": "會話錄製", - "wakeOnLan": "區域網路喚醒", - "vncSettings": "VNC 設定", - "terminalSettings": "終端設定", - "rdpPort": "RDP埠", - "username": "使用者名稱", - "password": "密碼", - "domain": "領域", - "securityMode": "安全模式", - "colorDepth": "色彩深度", - "width": "寬度", - "height": "高度", - "dpi": "乾粉", - "resizeMethod": "調整大小方法", - "clientName": "客戶名稱", - "initialProgram": "初始計劃", - "serverLayout": "伺服器佈局", - "timezone": "時區", - "gatewayHostname": "網關主機名", - "gatewayPort": "網關連接埠", - "gatewayUsername": "網關用戶名", - "gatewayPassword": "網關密碼", - "gatewayDomain": "閘道", - "remoteAppProgram": "RemoteApp 程式", - "workingDirectory": "工作目錄", - "arguments": "論點", - "normalizeLineEndings": "標準化行尾", - "recordingPath": "錄影路徑", - "recordingName": "錄音名稱", - "macAddress": "MAC位址", - "broadcastAddress": "廣播位址", - "udpPort": "UDP埠", - "waitTimeS": "等待時間(秒)", - "driveName": "磁碟機名稱", - "drivePath": "驅動路徑", - "ignoreCertificate": "忽略證書", - "ignoreCertificateDesc": "允許連接到具有自簽名憑證的主機", - "forceLossless": "無損力", - "forceLosslessDesc": "強制無損影像編碼(更高品質,更高頻寬)", - "disableAudio": "關閉音訊", - "disableAudioDesc": "將遠端會話中的所有音訊靜音", - "enableAudioInput": "啟用音訊輸入(麥克風)", - "enableAudioInputDesc": "把本地麥克風轉送到遠端會話", - "wallpaper": "壁紙", - "wallpaperDesc": "顯示桌面桌布(停用可提升效能)", - "theming": "主題", - "themingDesc": "啟用視覺主題和樣式", - "fontSmoothing": "字體平滑", - "fontSmoothingDesc": "啟用 ClearType 字體渲染", - "fullWindowDrag": "全視窗拖曳", - "fullWindowDragDesc": "拖曳時顯示視窗內容", - "desktopComposition": "桌面合成", - "desktopCompositionDesc": "啟用 Aero 玻璃效果", - "menuAnimations": "選單動畫", - "menuAnimationsDesc": "啟用選單淡入淡出和滑動動畫", - "disableBitmapCaching": "禁用位圖緩存", - "disableBitmapCachingDesc": "關閉位圖快取(可能有助於解決故障)", - "disableOffscreenCaching": "禁用螢幕外緩存", - "disableOffscreenCachingDesc": "關閉螢幕外緩存", - "disableGlyphCaching": "禁用字形快取", - "disableGlyphCachingDesc": "關閉字形快取", - "enableGfx": "啟用圖形", - "enableGfxDesc": "使用 RemoteFX 圖形管線", - "enablePrinting": "啟用列印", - "enablePrintingDesc": "將本機印表機重定向到遠端會話", - "enableDriveRedirection": "啟用驅動器重定向", - "enableDriveRedirectionDesc": "將本機資料夾對應為遠端會話中的磁碟機", - "createDrivePath": "建立驅動器路徑", - "createDrivePathDesc": "如果資料夾不存在,則自動建立該資料夾。", - "disableDownload": "停用下載", - "disableDownloadDesc": "阻止從遠端會話下載文件", - "disableUpload": "禁用上傳", - "disableUploadDesc": "阻止向遠端會話上傳文件", - "enableTouch": "啟用觸控功能", - "enableTouchDesc": "啟用觸控輸入轉發", - "consoleSession": "控制台會話", - "consoleSessionDesc": "連接到控制台(會話 0),而不是新建會話。", - "sendWolPacket": "發送 WOL 封包", - "sendWolPacketDesc": "連線前發送喚醒包以喚醒此主機", - "disableCopy": "停用複製", - "disableCopyDesc": "阻止從遠端會話複製文本", - "disablePaste": "停用貼上", - "disablePasteDesc": "阻止將文字貼到遠端會話中", - "createPathIfMissing": "如果路徑缺失,則建立路徑。", - "createPathIfMissingDesc": "自動建立錄製目錄", - "excludeOutput": "排除輸出", - "excludeOutputDesc": "不要錄製螢幕輸出(僅錄製元資料)", - "excludeMouse": "排除滑鼠", - "excludeMouseDesc": "請勿記錄滑鼠移動", - "includeKeystrokes": "包含按鍵操作", - "includeKeystrokesDesc": "除了螢幕輸出外,還要記錄原始按鍵操作。", - "vncPort": "VNC埠", - "vncPassword": "VNC密碼", - "vncUsernameOptional": "使用者名稱(可選)", - "vncLeaveBlank": "如不需要,請留空。", - "cursorMode": "遊標模式", - "swapRedBlue": "交換紅/藍", - "swapRedBlueDesc": "交換紅色和藍色通道(修復一些顏色問題)", - "readOnly": "只讀", - "readOnlyDesc": "無需發送任何輸入即可查看遠端螢幕", - "telnetPort": "Telnet 連接埠", - "terminalType": "終端類型", - "fontName": "字體名稱", - "fontSize": "字體大小", - "colorScheme": "配色方案", - "backspaceKey": "退格鍵", - "saveHostFirst": "先保存主機。", - "sharingOptionsAfterSave": "儲存主機後即可使用分享選項。", - "sharingLoadError": "共享資料加載失敗。請檢查您的網路連線並重試。", - "shareHostSection": "共享主機", - "shareWithUser": "與用戶分享", - "shareWithRole": "分享角色", - "selectUser": "選擇用戶", - "selectRole": "選擇角色", - "selectUserOption": "選擇用戶...", - "selectRoleOption": "選擇一個角色…", - "permissionLevel": "權限等級", - "expiresInHours": "剩餘時間(小時)", - "noExpiryPlaceholder": "留空即可,無有效期限限制。", - "shareBtn": "分享", - "currentAccess": "目前存取權限", - "typeHeader": "類型", - "targetHeader": "目標", - "permissionHeader": "允許", - "grantedByHeader": "授予", - "expiresHeader": "過期", - "noAccessEntries": "暫無訪問記錄。", - "expiredLabel": "已到期", - "neverLabel": "絕不", - "revokeBtn": "撤銷", - "cancelBtn": "取消", - "savingBtn": "儲存...", - "updateHostBtn": "更新主機", - "addHostBtn": "新增主機" + "clipboard": "Clipboard", + "sessionRecording": "Session Recording", + "wakeOnLan": "Wake-on-LAN", + "vncSettings": "VNC Settings", + "terminalSettings": "Terminal Settings", + "rdpPort": "RDP Port", + "username": "Username", + "password": "Password", + "domain": "Domain", + "securityMode": "Security Mode", + "colorDepth": "Color Depth", + "width": "Width", + "height": "Height", + "dpi": "DPI", + "resizeMethod": "Resize Method", + "clientName": "Client Name", + "initialProgram": "Initial Program", + "serverLayout": "Server Layout", + "timezone": "Timezone", + "gatewayHostname": "Gateway Hostname", + "gatewayPort": "Gateway Port", + "gatewayUsername": "Gateway Username", + "gatewayPassword": "Gateway Password", + "gatewayDomain": "Gateway Domain", + "remoteAppProgram": "RemoteApp Program", + "workingDirectory": "Working Directory", + "arguments": "Arguments", + "normalizeLineEndings": "Normalize Line Endings", + "recordingPath": "Recording Path", + "recordingName": "Recording Name", + "macAddress": "MAC Address", + "broadcastAddress": "Broadcast Address", + "udpPort": "UDP Port", + "waitTimeS": "Wait Time (s)", + "driveName": "Drive Name", + "drivePath": "Drive Path", + "ignoreCertificate": "Ignore Certificate", + "ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates", + "forceLossless": "Force Lossless", + "forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)", + "disableAudio": "Disable Audio", + "disableAudioDesc": "Mute all audio from the remote session", + "enableAudioInput": "Enable Audio Input (Microphone)", + "enableAudioInputDesc": "Forward local microphone to the remote session", + "wallpaper": "Wallpaper", + "wallpaperDesc": "Show desktop wallpaper (disabling improves performance)", + "theming": "Theming", + "themingDesc": "Enable visual themes and styles", + "fontSmoothing": "Font Smoothing", + "fontSmoothingDesc": "Enable ClearType font rendering", + "fullWindowDrag": "Full Window Drag", + "fullWindowDragDesc": "Show window contents while dragging", + "desktopComposition": "Desktop Composition", + "desktopCompositionDesc": "Enable Aero glass effects", + "menuAnimations": "Menu Animations", + "menuAnimationsDesc": "Enable menu fade and slide animations", + "disableBitmapCaching": "Disable Bitmap Caching", + "disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)", + "disableOffscreenCaching": "Disable Offscreen Caching", + "disableOffscreenCachingDesc": "Turn off offscreen cache", + "disableGlyphCaching": "Disable Glyph Caching", + "disableGlyphCachingDesc": "Turn off glyph cache", + "enableGfx": "Enable GFX", + "enableGfxDesc": "Use RemoteFX graphics pipeline", + "enablePrinting": "Enable Printing", + "enablePrintingDesc": "Redirect local printers to the remote session", + "enableDriveRedirection": "Enable Drive Redirection", + "enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session", + "createDrivePath": "Create Drive Path", + "createDrivePathDesc": "Automatically create the folder if it does not exist", + "disableDownload": "Disable Download", + "disableDownloadDesc": "Prevent downloading files from the remote session", + "disableUpload": "Disable Upload", + "disableUploadDesc": "Prevent uploading files to the remote session", + "enableTouch": "Enable Touch", + "enableTouchDesc": "Enable touch input forwarding", + "consoleSession": "Console Session", + "consoleSessionDesc": "Connect to the console (session 0) instead of a new session", + "sendWolPacket": "Send WOL Packet", + "sendWolPacketDesc": "Send a magic packet to wake this host before connecting", + "disableCopy": "Disable Copy", + "disableCopyDesc": "Prevent copying text from the remote session", + "disablePaste": "Disable Paste", + "disablePasteDesc": "Prevent pasting text into the remote session", + "createPathIfMissing": "Create Path if Missing", + "createPathIfMissingDesc": "Automatically create the recording directory", + "excludeOutput": "Exclude Output", + "excludeOutputDesc": "Do not record screen output (metadata only)", + "excludeMouse": "Exclude Mouse", + "excludeMouseDesc": "Do not record mouse movements", + "includeKeystrokes": "Include Keystrokes", + "includeKeystrokesDesc": "Record raw keystrokes in addition to screen output", + "vncPort": "VNC Port", + "vncPassword": "VNC Password", + "vncUsernameOptional": "Username (optional)", + "vncLeaveBlank": "Leave blank if not required", + "cursorMode": "Cursor Mode", + "swapRedBlue": "Swap Red/Blue", + "swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)", + "readOnly": "Read-only", + "readOnlyDesc": "View the remote screen without sending any input", + "telnetPort": "Telnet Port", + "terminalType": "Terminal Type", + "fontName": "Font Name", + "fontSize": "Font Size", + "colorScheme": "Color Scheme", + "backspaceKey": "Backspace Key", + "saveHostFirst": "Save the host first.", + "sharingOptionsAfterSave": "Sharing options are available after the host has been saved.", + "sharingLoadError": "Failed to load sharing data. Check your connection and try again.", + "shareHostSection": "Share Host", + "shareWithUser": "Share with User", + "shareWithRole": "Share with Role", + "selectUser": "Select User", + "selectRole": "Select Role", + "selectUserOption": "Select a user...", + "selectRoleOption": "Select a role...", + "permissionLevel": "Permission Level", + "expiresInHours": "Expires in (hours)", + "noExpiryPlaceholder": "Leave empty for no expiry", + "shareBtn": "Share", + "currentAccess": "Current Access", + "typeHeader": "Type", + "targetHeader": "Target", + "permissionHeader": "Permission", + "grantedByHeader": "Granted By", + "expiresHeader": "Expires", + "noAccessEntries": "No access entries yet.", + "expiredLabel": "Expired", + "neverLabel": "Never", + "revokeBtn": "Revoke", + "cancelBtn": "Cancel", + "savingBtn": "Saving...", + "updateHostBtn": "Update Host", + "addHostBtn": "Add Host" } }, "commandPalette": { - "searchPlaceholder": "搜尋主機、指令或設定…", - "quickActions": "快速操作", - "hostManager": "主機管理器", - "hostManagerDesc": "管理、新增或編輯主機", - "addNewHost": "新增主機", - "addNewHostDesc": "註冊新主機", - "adminSettings": "管理員設定", - "adminSettingsDesc": "配置系統首選項和用戶", - "userProfile": "用戶個人資料", - "userProfileDesc": "管理您的帳戶和偏好設定", - "addCredential": "新增憑證", - "addCredentialDesc": "儲存 SSH 金鑰或密碼", - "recentActivity": "近期活動", - "serversAndHosts": "伺服器和主機", - "noHostsFound": "找不到與「{{search}}」相符的主機", - "links": "連結", - "navigate": "導航", - "select": "選擇", - "toggleWith": "切換" + "searchPlaceholder": "Search hosts, commands, or settings...", + "quickActions": "Quick Actions", + "hostManager": "Host Manager", + "hostManagerDesc": "Manage, add, or edit hosts", + "addNewHost": "Add New Host", + "addNewHostDesc": "Register a new host", + "adminSettings": "Admin Settings", + "adminSettingsDesc": "Configure system preferences and users", + "userProfile": "User Profile", + "userProfileDesc": "Manage your account and preferences", + "addCredential": "Add Credential", + "addCredentialDesc": "Store SSH keys or passwords", + "tmuxMonitor": "Tmux Monitor", + "tmuxMonitorDesc": "Monitor tmux sessions across your hosts", + "recentActivity": "Recent Activity", + "serversAndHosts": "Servers & Hosts", + "noHostsFound": "No hosts found matching \"{{search}}\"", + "links": "Links", + "navigate": "Navigate", + "select": "Select", + "toggleWith": "Toggle with" }, "splitScreen": { - "paneEmpty": "窗格 {{index}} - 空", - "noTabAssigned": "未分配標籤頁", - "focusedPane": "活動窗格" + "paneEmpty": "Pane {{index}} - empty", + "noTabAssigned": "No tab assigned", + "focusedPane": "Active pane" }, "connections": { - "noConnections": "沒有連接", - "noConnectionsDesc": "開啟終端機、檔案總管或遠端桌面即可在此處查看連線。", - "connectedFor": "已連線 {{duration}}", - "connected": "已連接", - "disconnected": "斷開連接", - "closeTab": "關閉標籤頁", - "closeConnection": "緊密聯繫", - "forgetTab": "忘記", - "removeBackground": "消除", - "reconnect": "重新連接", - "reopenTab": "重新開放", - "sectionOpen": "打開", - "sectionBackground": "背景", - "backgroundDesc": "斷開連線後,會話仍會保持 30 分鐘,並且可以重新連線。", - "persisted": "在背景持續運行", - "expiresIn": "有效期限至 {{duration}}", - "search": "搜尋關聯...", - "noSearchResults": "沒有找到與您的搜尋相符的結果。" + "noConnections": "No connections", + "noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here", + "connectedFor": "Connected for {{duration}}", + "connected": "Connected", + "disconnected": "Disconnected", + "closeTab": "Close tab", + "closeConnection": "Close connection", + "forgetTab": "Forget", + "removeBackground": "Remove", + "reconnect": "Reconnect", + "reopenTab": "Reopen", + "sectionOpen": "Open", + "sectionBackground": "Background", + "backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.", + "persisted": "Persisted in background", + "expiresIn": "Expires in {{duration}}", + "search": "Search connections...", + "noSearchResults": "No connections match your search" }, "guacamole": { - "connecting": "正在連線到 {{type}} 會話...", - "connectionError": "連線錯誤", - "connectionFailed": "連線失敗", - "failedToConnect": "取得連線令牌失敗", - "hostNotFound": "未找到主機", - "noHostSelected": "未選擇主機", - "reconnect": "重新連接", - "retry": "重試", - "guacdUnavailable": "遠端桌面服務 (guacd) 不可用。請確保 guacd 正在運行、可訪問,並且在管理設定中已正確配置。", + "connecting": "Connecting to {{type}} session...", + "connectionError": "Connection error", + "connectionFailed": "Connection failed", + "failedToConnect": "Failed to get connection token", + "hostNotFound": "Host not found", + "noHostSelected": "No host selected", + "reconnect": "Reconnect", + "retry": "Retry", + "guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.", "ctrlAltDel": "Ctrl+Alt+Del", "toolbar": { "ctrlAltDel": "Ctrl+Alt+Del", - "winL": "Win+L(鎖定畫面)", - "winKey": "Windows 鍵", + "winL": "Win+L (Lock Screen)", + "winKey": "Windows Key", "ctrl": "Ctrl", - "alt": "另類", - "shift": "轉移", - "win": "贏", - "stickyActive": "{{key}} (已鎖定 - 按一下釋放)", - "stickyInactive": "{{key}} (點選鎖定)", - "esc": "逃脫", - "tab": "標籤頁", - "home": "家", - "end": "結尾", - "pageUp": "上一頁", - "pageDown": "往下翻頁", - "arrowUp": "向上箭頭", - "arrowDown": "向下箭頭", - "arrowLeft": "左箭頭", - "arrowRight": "向右箭頭", - "fnToggle": "功能鍵", - "reconnect": "重新連接會話", - "collapse": "折疊工具列", - "expand": "展開工具列", - "dragHandle": "拖曳以重新定位" + "alt": "Alt", + "shift": "Shift", + "win": "Win", + "stickyActive": "{{key}} (latched - click to release)", + "stickyInactive": "{{key}} (click to latch)", + "esc": "Escape", + "tab": "Tab", + "home": "Home", + "end": "End", + "pageUp": "Page Up", + "pageDown": "Page Down", + "arrowUp": "Arrow Up", + "arrowDown": "Arrow Down", + "arrowLeft": "Arrow Left", + "arrowRight": "Arrow Right", + "fnToggle": "Function Keys", + "reconnect": "Reconnect Session", + "collapse": "Collapse toolbar", + "expand": "Expand toolbar", + "dragHandle": "Drag to reposition" } }, "terminal": { - "connect": "連接到主機", - "clear": "清除", - "paste": "貼上", - "reconnect": "重新連接", - "connectionLost": "連線遺失", - "connected": "已連接", - "clipboardWriteFailed": "複製到剪貼簿失敗。請確保頁面是透過 HTTPS 或本地主機存取的。", - "clipboardReadFailed": "讀取剪貼簿內容失敗。請確保已授予剪貼簿權限。", - "clipboardHttpWarning": "貼上操作需要 HTTPS。請使用 Ctrl+Shift+V 或透過 HTTPS 提供 Termix 服務。", - "unknownError": "發生未知錯誤", - "websocketError": "WebSocket 連線錯誤", - "connecting": "正在連接...", - "noHostSelected": "未選擇主機", - "reconnecting": "正在重新連線... ({{attempt}}/{{max}})", - "reconnected": "已成功重新連接", - "tmuxSessionCreated": "tmux 會話已建立: {{name}}", - "tmuxSessionAttached": "tmux 會話已連線: {{name}}", - "tmuxUnavailable": "遠端主機上未安裝 tmux,回退到標準 shell", - "tmuxSessionPickerTitle": "tmux 會話", - "tmuxSessionPickerDesc": "此主機上已存在 tmux 會話。請選擇一個會話重新連線或建立新會話。", - "tmuxWindows": "視窗", - "tmuxWindowCount": "{{count}} 窗口", - "tmuxAttached": "附屬客戶", - "tmuxAttachedCount": "{{count}} 已連接", - "tmuxLastActivity": "上次活動", - "tmuxTimeJustNow": "現在", - "tmuxTimeMinutes": "{{count}}幾分鐘前", - "tmuxTimeHours": "{{count}}小時前", - "tmuxTimeDays": "{{count}}天前", - "tmuxCreateNew": "開始新會話", - "tmuxCopyHint": "調整選取範圍並按 Enter 鍵複製到剪貼簿", - "tmuxDetach": "從 tmux 會話中分離", - "tmuxDetached": "已從 tmux 會話中分離", - "maxReconnectAttemptsReached": "已達最大重連嘗試次數", - "closeTab": "關閉", - "connectionTimeout": "連線逾時", - "terminalTitle": "終端機 - {{host}}", - "terminalWithPath": "終端 - {{host}}:{{path}}", - "runTitle": "運行 {{command}} - {{host}}", - "totpRequired": "需要雙重認證", - "totpCodeLabel": "驗證碼", - "totpVerify": "核實", - "warpgateAuthRequired": "需要傳送門認證", - "warpgateSecurityKey": "安全金鑰", - "warpgateAuthUrl": "身份驗證 URL", - "warpgateOpenBrowser": "在瀏覽器中開啟", - "warpgateContinue": "我已經完成身份驗證", - "opksshAuthRequired": "需要 OPKSSH 驗證", - "opksshAuthDescription": "請在瀏覽器中完成身份驗證以繼續。此會話有效期限為 24 小時。", - "opksshOpenBrowser": "開啟瀏覽器進行身份驗證", - "opksshWaitingForAuth": "正在等待瀏覽器驗證...", - "opksshAuthenticating": "正在處理身份驗證...", - "opksshTimeout": "身份驗證超時,請重試。", - "opksshAuthFailed": "身份驗證失敗。請檢查您的憑證並重試。", - "opksshSignInWith": "使用 {{provider}} 登入", - "sudoPasswordPopupTitle": "輸入密碼?", - "websocketAbnormalClose": "連線意外關閉。這可能是由於反向代理或 SSL 配置問題導致的。請檢查伺服器日誌。", - "connectionLogTitle": "連線日誌", - "connectionLogCopy": "將日誌複製到剪貼簿", - "connectionLogEmpty": "暫無連線日誌", - "connectionLogWaiting": "正在等待連線日誌...", - "connectionLogCopied": "連線日誌已複製到剪貼簿", - "connectionLogCopyFailed": "無法將日誌複製到剪貼簿", - "connectionRejected": "伺服器拒絕連線。請檢查您的身份驗證和網路配置。", - "hostKeyRejected": "SSH主機金鑰驗證失敗。連線已取消。", - "sessionTakenOver": "會話已在另一個標籤頁中開啟。正在重新連線…" + "connect": "Connect to Host", + "clear": "Clear", + "paste": "Paste", + "reconnect": "Reconnect", + "connectionLost": "Connection lost", + "connected": "Connected", + "clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.", + "clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.", + "clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.", + "unknownError": "Unknown error occurred", + "websocketError": "WebSocket connection error", + "connecting": "Connecting...", + "noHostSelected": "No host selected", + "reconnecting": "Reconnecting... ({{attempt}}/{{max}})", + "reconnected": "Reconnected successfully", + "tmuxSessionCreated": "tmux session created: {{name}}", + "tmuxSessionAttached": "tmux session attached: {{name}}", + "tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell", + "tmuxSessionPickerTitle": "tmux Sessions", + "tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.", + "tmuxWindows": "Windows", + "tmuxWindowCount": "{{count}} window", + "tmuxAttached": "Attached clients", + "tmuxAttachedCount": "{{count}} attached", + "tmuxLastActivity": "Last activity", + "tmuxTimeJustNow": "just now", + "tmuxTimeMinutes": "{{count}}m ago", + "tmuxTimeHours": "{{count}}h ago", + "tmuxTimeDays": "{{count}}d ago", + "tmuxCreateNew": "Start new session", + "tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard", + "tmuxDetach": "Detach from tmux session", + "tmuxDetached": "Detached from tmux session", + "maxReconnectAttemptsReached": "Maximum reconnection attempts reached", + "closeTab": "Close", + "connectionTimeout": "Connection timeout", + "terminalTitle": "Terminal - {{host}}", + "terminalWithPath": "Terminal - {{host}}:{{path}}", + "runTitle": "Running {{command}} - {{host}}", + "totpRequired": "Two-Factor Authentication Required", + "totpCodeLabel": "Verification Code", + "totpVerify": "Verify", + "warpgateAuthRequired": "Warpgate Authentication Required", + "warpgateSecurityKey": "Security Key", + "warpgateAuthUrl": "Authentication URL", + "warpgateOpenBrowser": "Open in Browser", + "warpgateContinue": "I've Completed Authentication", + "opksshAuthRequired": "OPKSSH Authentication Required", + "opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.", + "opksshOpenBrowser": "Open Browser to Authenticate", + "opksshWaitingForAuth": "Waiting for authentication in browser...", + "opksshAuthenticating": "Processing authentication...", + "opksshTimeout": "Authentication timed out. Please try again.", + "opksshAuthFailed": "Authentication failed. Please check your credentials and try again.", + "opksshSignInWith": "Sign in with {{provider}}", + "sudoPasswordPopupTitle": "Insert Password?", + "websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.", + "connectionLogTitle": "Connection Log", + "connectionLogCopy": "Copy logs to clipboard", + "connectionLogEmpty": "No connection logs yet", + "connectionLogWaiting": "Waiting for connection logs...", + "connectionLogCopied": "Connection logs copied to clipboard", + "connectionLogCopyFailed": "Failed to copy logs to clipboard", + "connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.", + "hostKeyRejected": "SSH host key verification rejected. Connection cancelled.", + "sessionTakenOver": "Session was opened in another tab. Reconnecting..." }, "fileManager": { - "noHostSelected": "未選擇主機", - "initializingEditor": "正在初始化編輯器...", - "file": "檔案", - "folder": "資料夾", - "uploadFile": "上傳檔案", - "downloadFile": "下載", - "extractArchive": "提取存檔", - "extractingArchive": "正在提取 {{name}}...", - "archiveExtractedSuccessfully": "{{name}} 提取成功", - "extractFailed": "提取失敗", - "compressFile": "壓縮檔案", - "compressFiles": "壓縮檔案", - "compressFilesDesc": "將 {{count}} 個項目壓縮到一個檔案中", - "archiveName": "存檔名稱", - "enterArchiveName": "請輸入存檔名稱...", - "compressionFormat": "壓縮格式", - "selectedFiles": "選定檔案", - "andMoreFiles": "及 {{count}} 個其他檔案...", - "compress": "壓縮", - "compressingFiles": "將 {{count}} 個項目壓縮到 {{name}}...", - "filesCompressedSuccessfully": "{{name}} 建立成功", - "compressFailed": "壓縮失敗", - "edit": "編輯", - "preview": "預覽", - "previous": "以前的", - "next": "下一個", - "pageXOfY": "第 {{current}} 頁,共 {{total}} 頁", - "zoomOut": "縮小", - "zoomIn": "放大", - "newFile": "新檔案", - "newFolder": "新建資料夾", - "rename": "重新命名", - "uploading": "正在上傳...", - "uploadingFile": "正在上傳 {{name}}...", - "fileName": "檔案名稱", - "folderName": "資料夾名稱", - "fileUploadedSuccessfully": "檔案「{{name}}」已成功上傳", - "failedToUploadFile": "檔案上載失敗", - "fileDownloadedSuccessfully": "檔案「{{name}}」已成功下載", - "failedToDownloadFile": "檔案下載失敗", - "fileCreatedSuccessfully": "檔案“{{name}}”已成功建立", - "folderCreatedSuccessfully": "資料夾“{{name}}”已成功建立", - "failedToCreateItem": "建立專案失敗", - "operationFailed": "{{operation}} 操作對 {{name}}: {{error}} 失敗", - "failedToResolveSymlink": "解析符號連結失敗", - "itemsDeletedSuccessfully": "{{count}} 項目已成功刪除", - "failedToDeleteItems": "刪除項目失敗", - "sudoPasswordRequired": "需要管理員密碼", - "enterSudoPassword": "請輸入 sudo 密碼以繼續此操作", - "sudoPassword": "Sudo 密碼", - "sudoOperationFailed": "sudo 操作失敗", - "sudoAuthFailed": "sudo 驗證失敗", - "dragFilesToUpload": "將檔案拖曳至此處以上傳", - "emptyFolder": "此資料夾為空。", - "searchFiles": "搜尋檔案...", - "upload": "上傳", - "selectHostToStart": "選擇主機以啟動檔案管理", - "sshRequiredForFileManager": "檔案管理器需要 SSH 連線。此主機未啟用 SSH 連線。", - "failedToConnect": "SSH 連線失敗", - "failedToLoadDirectory": "載入目錄失敗", - "noSSHConnection": "沒有可用的 SSH 連接", - "copy": "複製", - "cut": "剪下", - "paste": "貼上", - "copyPath": "複製路徑", - "copyPaths": "複製路徑", - "delete": "刪除", - "properties": "內容", - "refresh": "重新整理", - "downloadFiles": "下載 {{count}} 檔案到瀏覽器", - "copyFiles": "複製 {{count}} 項", - "cutFiles": "剪力 {{count}} 項", - "deleteFiles": "刪除 {{count}} 項", - "filesCopiedToClipboard": "{{count}} 個項目已複製到剪貼簿", - "filesCutToClipboard": "{{count}} 個項目已剪下至剪貼簿", - "pathCopiedToClipboard": "路徑已複製到剪貼簿", - "pathsCopiedToClipboard": "{{count}} 個路徑已複製到剪貼簿", - "failedToCopyPath": "無法將路徑複製到剪貼簿", - "movedItems": "已移動 {{count}} 個項目", - "failedToDeleteItem": "刪除項目失敗", - "itemRenamedSuccessfully": "{{type}} 已成功重新命名", - "failedToRenameItem": "重新命名專案失敗", - "download": "下載", - "permissions": "權限", - "size": "尺寸", - "modified": "修改的", - "path": "路徑", - "confirmDelete": "您確定要刪除 {{name}} 嗎?", - "permissionDenied": "權限被拒絕", - "serverError": "伺服器錯誤", - "fileSavedSuccessfully": "成功儲存檔案", - "failedToSaveFile": "儲存檔案失敗", - "confirmDeleteSingleItem": "您確定要永久刪除“{{name}}”嗎?", - "confirmDeleteMultipleItems": "您確定要永久刪除 {{count}} 項目嗎?", - "confirmDeleteMultipleItemsWithFolders": "您確定要永久刪除 {{count}} 項目嗎?這包括資料夾及其內容。", - "confirmDeleteFolder": "您確定要永久刪除資料夾「{{name}}」及其所有內容嗎?", - "permanentDeleteWarning": "此操作無法撤銷。物品將從伺服器永久刪除。", - "recent": "最近的", - "pinned": "置頂", - "folderShortcuts": "資料夾快捷方式", - "failedToReconnectSSH": "SSH 會話重新連線失敗", - "openTerminalHere": "在此處打開終端機", - "run": "執行", - "openTerminalInFolder": "在此資料夾中開啟終端機", - "openTerminalInFileLocation": "打開終端機,指向檔案位置", - "runningFile": "運行中 - {{file}}", - "onlyRunExecutableFiles": "只能運行可執行檔案", - "directories": "目錄", - "removedFromRecentFiles": "從最近檔案中移除“{{name}}”", - "removeFailed": "移除失敗", - "unpinnedSuccessfully": "已成功取消置頂“{{name}}”", - "unpinFailed": "解除鎖定失敗", - "removedShortcut": "已移除捷徑“{{name}}”", - "removeShortcutFailed": "移除快捷方式失敗", - "clearedAllRecentFiles": "已清除所有最近檔案", - "clearFailed": "清除失敗", - "removeFromRecentFiles": "從最近檔案中刪除", - "clearAllRecentFiles": "清除所有最近檔案", - "unpinFile": "解壓縮檔案", - "removeShortcut": "移除快捷方式", - "pinFile": "置頂檔案", - "addToShortcuts": "新增到快捷方式", - "pasteFailed": "貼上失敗", - "noUndoableActions": "沒有可撤銷的操作", - "undoCopySuccess": "撤銷複製操作:已刪除 {{count}} 個已複製的檔案", - "undoCopyFailedDelete": "撤銷失敗:無法刪除任何已複製的檔案", - "undoCopyFailedNoInfo": "撤銷失敗:找不到已複製的檔案訊息", - "undoMoveSuccess": "撤銷移動操作:已將 {{count}} 個檔案移回原始位置", - "undoMoveFailedMove": "撤銷失敗:無法將任何檔案移回。", - "undoMoveFailedNoInfo": "撤銷失敗:找不到已移動的檔案訊息", - "undoDeleteNotSupported": "刪除操作無法撤銷:檔案已從伺服器永久刪除。", - "undoTypeNotSupported": "不支援的撤銷操作類型", - "undoOperationFailed": "撤銷操作失敗", - "unknownError": "未知錯誤", - "confirm": "確認", - "find": "尋找...", - "replace": "代替", - "downloadInstead": "下載", - "keyboardShortcuts": "鍵盤快速鍵", - "searchAndReplace": "搜尋和替換", - "editing": "編輯", - "search": "搜尋", - "findNext": "尋找下一個", - "findPrevious": "找上一個", - "save": "儲存", - "selectAll": "全選", - "undo": "撤銷", - "redo": "重做", - "moveLineUp": "移動陣容", - "moveLineDown": "向下移動線路", - "toggleComment": "切換評論", - "autoComplete": "自動完成", - "imageLoadError": "圖片載入失敗", - "startTyping": "開始輸入…", - "unknownSize": "尺寸未知", - "fileIsEmpty": "空白檔案", - "largeFileWarning": "大檔案警告", - "largeFileWarningDesc": "此檔案大小為 {{size}} ,以文字格式開啟時可能會導致效能問題。", - "fileNotFoundAndRemoved": "檔案「{{name}}」未找到,已從最近/置頂檔案移除。", - "failedToLoadFile": "檔案載入失敗: {{error}}", - "serverErrorOccurred": "伺服器出錯,請稍後再試。", - "autoSaveFailed": "自動儲存失敗", - "fileAutoSaved": "文件自動儲存", - "moveFileFailed": "移動失敗 {{name}}", - "moveOperationFailed": "移動操作失敗", - "canOnlyCompareFiles": "只能比較兩個檔案", - "comparingFiles": "正在比較檔案: {{file1}} 和 {{file2}}", - "dragFailed": "拖曳操作失敗", - "filePinnedSuccessfully": "檔案「{{name}}」已成功置頂", - "pinFileFailed": "檔案置頂失敗", - "fileUnpinnedSuccessfully": "檔案「{{name}}」已成功取消置頂", - "unpinFileFailed": "取消置頂檔案失敗", - "shortcutAddedSuccessfully": "資料夾捷徑「{{name}}」已成功新增", - "addShortcutFailed": "新增快捷方式失敗", - "operationCompletedSuccessfully": "{{operation}} {{count}} 個專案已成功", - "operationCompleted": "{{operation}} {{count}} 項目", - "downloadFileSuccess": "檔案 {{name}} 下載成功", - "downloadFileFailed": "下載失敗", - "moveTo": "移至 {{name}}", - "diffCompareWith": "與 {{name}} 的差異比較", - "dragOutsideToDownload": "向視窗外拖曳以下載({{count}} 檔案)", - "newFolderDefault": "新建資料夾", + "noHostSelected": "No host selected", + "initializingEditor": "Initializing editor...", + "file": "File", + "folder": "Folder", + "uploadFile": "Upload File", + "downloadFile": "Download", + "extractArchive": "Extract Archive", + "extractingArchive": "Extracting {{name}}...", + "archiveExtractedSuccessfully": "{{name}} extracted successfully", + "extractFailed": "Extract failed", + "compressFile": "Compress File", + "compressFiles": "Compress Files", + "compressFilesDesc": "Compress {{count}} items into an archive", + "archiveName": "Archive Name", + "enterArchiveName": "Enter archive name...", + "compressionFormat": "Compression Format", + "selectedFiles": "Selected files", + "andMoreFiles": "and {{count}} more...", + "compress": "Compress", + "compressingFiles": "Compressing {{count}} items into {{name}}...", + "filesCompressedSuccessfully": "{{name}} created successfully", + "compressFailed": "Compression failed", + "edit": "Edit", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "pageXOfY": "Page {{current}} of {{total}}", + "zoomOut": "Zoom Out", + "zoomIn": "Zoom In", + "newFile": "New File", + "newFolder": "New Folder", + "rename": "Rename", + "uploading": "Uploading...", + "uploadingFile": "Uploading {{name}}...", + "fileName": "File Name", + "folderName": "Folder Name", + "fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully", + "failedToUploadFile": "Failed to upload file", + "fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully", + "failedToDownloadFile": "Failed to download file", + "fileCreatedSuccessfully": "File \"{{name}}\" created successfully", + "folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully", + "failedToCreateItem": "Failed to create item", + "operationFailed": "{{operation}} operation failed for {{name}}: {{error}}", + "failedToResolveSymlink": "Failed to resolve symlink", + "itemsDeletedSuccessfully": "{{count}} items deleted successfully", + "failedToDeleteItems": "Failed to delete items", + "sudoPasswordRequired": "Administrator Password Required", + "enterSudoPassword": "Enter sudo password to continue this operation", + "sudoPassword": "Sudo password", + "sudoOperationFailed": "Sudo operation failed", + "sudoAuthFailed": "Sudo authentication failed", + "dragFilesToUpload": "Drop files here to upload", + "emptyFolder": "This folder is empty", + "searchFiles": "Search files...", + "upload": "Upload", + "selectHostToStart": "Select a host to start file management", + "sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.", + "failedToConnect": "Failed to connect to SSH", + "failedToLoadDirectory": "Failed to load directory", + "noSSHConnection": "No SSH connection available", + "copy": "Copy", + "cut": "Cut", + "paste": "Paste", + "copyPath": "Copy Path", + "copyPaths": "Copy Paths", + "delete": "Delete", + "properties": "Properties", + "refresh": "Refresh", + "downloadFiles": "Download {{count}} files to Browser", + "copyFiles": "Copy {{count}} items", + "cutFiles": "Cut {{count}} items", + "deleteFiles": "Delete {{count}} items", + "filesCopiedToClipboard": "{{count}} items copied to clipboard", + "filesCutToClipboard": "{{count}} items cut to clipboard", + "pathCopiedToClipboard": "Path copied to clipboard", + "pathsCopiedToClipboard": "{{count}} paths copied to clipboard", + "failedToCopyPath": "Failed to copy path to clipboard", + "movedItems": "Moved {{count}} items", + "failedToDeleteItem": "Failed to delete item", + "itemRenamedSuccessfully": "{{type}} renamed successfully", + "failedToRenameItem": "Failed to rename item", + "download": "Download", + "permissions": "Permissions", + "size": "Size", + "modified": "Modified", + "path": "Path", + "confirmDelete": "Are you sure you want to delete {{name}}?", + "permissionDenied": "Permission denied", + "serverError": "Server Error", + "fileSavedSuccessfully": "File saved successfully", + "failedToSaveFile": "Failed to save file", + "confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?", + "confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?", + "confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.", + "confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?", + "permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.", + "recent": "Recent", + "pinned": "Pinned", + "folderShortcuts": "Folder Shortcuts", + "failedToReconnectSSH": "Failed to reconnect SSH session", + "openTerminalHere": "Open Terminal Here", + "run": "Run", + "openTerminalInFolder": "Open Terminal in This Folder", + "openTerminalInFileLocation": "Open Terminal at File Location", + "runningFile": "Running - {{file}}", + "onlyRunExecutableFiles": "Can only run executable files", + "directories": "Directories", + "removedFromRecentFiles": "Removed \"{{name}}\" from recent files", + "removeFailed": "Remove failed", + "unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully", + "unpinFailed": "Unpin failed", + "removedShortcut": "Removed shortcut \"{{name}}\"", + "removeShortcutFailed": "Remove shortcut failed", + "clearedAllRecentFiles": "Cleared all recent files", + "clearFailed": "Clear failed", + "removeFromRecentFiles": "Remove from recent files", + "clearAllRecentFiles": "Clear all recent files", + "unpinFile": "Unpin file", + "removeShortcut": "Remove shortcut", + "pinFile": "Pin file", + "addToShortcuts": "Add to shortcuts", + "pasteFailed": "Paste failed", + "noUndoableActions": "No undoable actions", + "undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files", + "undoCopyFailedDelete": "Undo failed: Could not delete any copied files", + "undoCopyFailedNoInfo": "Undo failed: Could not find copied file information", + "undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location", + "undoMoveFailedMove": "Undo failed: Could not move any files back", + "undoMoveFailedNoInfo": "Undo failed: Could not find moved file information", + "undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server", + "undoTypeNotSupported": "Unsupported undo operation type", + "undoOperationFailed": "Undo operation failed", + "unknownError": "Unknown error", + "confirm": "Confirm", + "find": "Find...", + "replace": "Replace", + "downloadInstead": "Download Instead", + "keyboardShortcuts": "Keyboard Shortcuts", + "searchAndReplace": "Search & Replace", + "editing": "Editing", + "search": "Search", + "findNext": "Find Next", + "findPrevious": "Find Previous", + "save": "Save", + "selectAll": "Select All", + "undo": "Undo", + "redo": "Redo", + "moveLineUp": "Move Line Up", + "moveLineDown": "Move Line Down", + "toggleComment": "Toggle Comment", + "autoComplete": "Auto Complete", + "imageLoadError": "Failed to load image", + "startTyping": "Start typing...", + "unknownSize": "Unknown size", + "fileIsEmpty": "File is empty", + "largeFileWarning": "Large File Warning", + "largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.", + "fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files", + "failedToLoadFile": "Failed to load file: {{error}}", + "serverErrorOccurred": "Server error occurred. Please try again later.", + "autoSaveFailed": "Auto-save failed", + "fileAutoSaved": "File auto-saved", + "moveFileFailed": "Failed to move {{name}}", + "moveOperationFailed": "Move operation failed", + "canOnlyCompareFiles": "Can only compare two files", + "comparingFiles": "Comparing files: {{file1}} and {{file2}}", + "dragFailed": "Drag operation failed", + "filePinnedSuccessfully": "File \"{{name}}\" pinned successfully", + "pinFileFailed": "Failed to pin file", + "fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully", + "unpinFileFailed": "Failed to unpin file", + "shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully", + "addShortcutFailed": "Failed to add shortcut", + "operationCompletedSuccessfully": "{{operation}} {{count}} items successfully", + "operationCompleted": "{{operation}} {{count}} items", + "downloadFileSuccess": "File {{name}} downloaded successfully", + "downloadFileFailed": "Download failed", + "moveTo": "Move to {{name}}", + "diffCompareWith": "Diff compare with {{name}}", + "dragOutsideToDownload": "Drag outside window to download ({{count}} files)", + "newFolderDefault": "NewFolder", "newFileDefault": "NewFile.txt", - "successfullyMovedItems": "已成功將 {{count}} 個項目移至 {{target}}", - "move": "移動", - "searchInFile": "在檔案中搜尋(Ctrl+F)", - "showKeyboardShortcuts": "顯示鍵盤快速鍵", - "startWritingMarkdown": "開始寫你的 Markdown 內容…", - "loadingFileComparison": "正在載入檔案比較...", - "reload": "重新載入", - "compare": "比較", - "sideBySide": "並排", - "inline": "排隊", - "fileComparison": "檔案比較: {{file1}} vs {{file2}}", - "fileTooLarge": "檔案過大: {{error}}", - "sshConnectionFailed": "SSH 連線失敗。請檢查您與 {{name}} ({{ip}}:{{port}} ) 的連接。", - "loadFileFailed": "檔案載入失敗: {{error}}", - "connecting": "正在連接...", - "connectedSuccessfully": "連線成功", - "totpVerificationFailed": "TOTP驗證失敗", - "warpgateVerificationFailed": "Warpgate 身份驗證失敗", - "authenticationFailed": "身份驗證失敗", - "verificationCodePrompt": "驗證碼:", - "changePermissions": "更改權限", - "currentPermissions": "目前權限", - "owner": "擁有者", - "group": "群組", - "others": "其他的", - "read": "讀", - "write": "寫", - "execute": "執行", - "permissionsChangedSuccessfully": "權限已成功更改", - "failedToChangePermissions": "更改權限失敗", - "name": "姓名", - "sortByName": "姓名", - "sortByDate": "修改日期", - "sortBySize": "尺寸", - "ascending": "上升", - "descending": "下降", - "root": "根", - "new": "新的", - "sortBy": "排序方式", - "items": "專案", - "selected": "已選", - "editor": "編輯", - "octal": "八進位", - "storage": "貯存", - "disk": "磁碟", - "used": "用過的", - "of": "的", - "toggleSidebar": "切換側邊欄", - "cannotLoadPdf": "無法載入PDF", - "pdfLoadError": "載入此PDF檔案時出錯。", - "loadingPdf": "正在載入PDF文件...", - "loadingPage": "頁面正在加載..." + "successfullyMovedItems": "Successfully moved {{count}} items to {{target}}", + "move": "Move", + "searchInFile": "Search in file (Ctrl+F)", + "showKeyboardShortcuts": "Show keyboard shortcuts", + "startWritingMarkdown": "Start writing your markdown content...", + "loadingFileComparison": "Loading file comparison...", + "reload": "Reload", + "compare": "Compare", + "sideBySide": "Side by Side", + "inline": "Inline", + "fileComparison": "File Comparison: {{file1}} vs {{file2}}", + "fileTooLarge": "File too large: {{error}}", + "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", + "loadFileFailed": "Failed to load file: {{error}}", + "connecting": "Connecting...", + "connectedSuccessfully": "Connected successfully", + "totpVerificationFailed": "TOTP verification failed", + "warpgateVerificationFailed": "Warpgate authentication failed", + "authenticationFailed": "Authentication failed", + "verificationCodePrompt": "Verification code:", + "changePermissions": "Change Permissions", + "currentPermissions": "Current Permissions", + "owner": "Owner", + "group": "Group", + "others": "Others", + "read": "Read", + "write": "Write", + "execute": "Execute", + "permissionsChangedSuccessfully": "Permissions changed successfully", + "failedToChangePermissions": "Failed to change permissions", + "name": "Name", + "sortByName": "Name", + "sortByDate": "Date Modified", + "sortBySize": "Size", + "ascending": "Ascending", + "descending": "Descending", + "root": "Root", + "new": "New", + "sortBy": "Sort By", + "items": "Items", + "selected": "Selected", + "editor": "Editor", + "octal": "Octal", + "storage": "Storage", + "disk": "Disk", + "used": "Used", + "of": "of", + "toggleSidebar": "Toggle Sidebar", + "cannotLoadPdf": "Cannot load PDF", + "pdfLoadError": "There was an error loading this PDF file.", + "loadingPdf": "Loading PDF...", + "loadingPage": "Loading page..." }, "transfer": { - "copyToHost": "複製到主機…", - "moveToHost": "移至主機…", - "copyItemsToHost": "將 {{count}} 項目複製到主機…", - "moveItemsToHost": "將 {{count}} 個物品移至主機…", - "noHostsConnected": "沒有其他檔案總管主機可用。", - "noHostsConnectedHint": "在主機管理員中新增另一台啟用了檔案管理器的 SSH 主機。", - "selectDestinationHost": "選擇目標主機", - "destinationPath": "目標路徑", - "recentDestinations": "近期目的地", - "collapseRecentDestinations": "收起最近的目的地", - "expandRecentDestinations": "展開最近目的地", - "browseFolders": "瀏覽目標資料夾", - "browseDestination": "瀏覽或輸入路徑", - "confirmCopy": "複製", - "confirmMove": "移動", - "transferring": "轉移…", - "compressing": "正在壓縮…", - "extracting": "提取…", - "transferringItems": "轉移 {{current}} 個物品,共 {{total}} 個物品,共… 個物品。", - "transferSuccess": "轉帳完成", - "transferError": "轉帳失敗", - "transferPartial": "轉帳完成,但出現 {{count}} 錯誤", - "transferPartialHint": "轉帳失敗: {{paths}}", - "itemsSummary": "{{count}} 項目", - "destMustBeDirectory": "對於多項傳輸,目標位置必須是目錄。", - "selectThisFolder": "選擇此資料夾", - "browsePathWillBeCreated": "此資料夾尚不存在。傳輸開始時將建立此資料夾。", - "browsePathError": "無法在目標主機上開啟此路徑。", - "goUp": "上", - "copyFolderToHost": "將資料夾複製到主機…", - "moveFolderToHost": "將資料夾移至主機…", - "hostReady": "準備好", - "hostConnecting": "正在連接…", - "hostDisconnected": "未連接", - "hostAuthRequired": "需要身份驗證-請先在此主機上開啟檔案總管。", - "hostConnectionFailed": "連線失敗", - "metricsTitle": "轉帳時間", - "metricsPrepare": "準備目的地: {{duration}}", - "metricsCompress": "壓縮來源檔: {{duration}}", - "metricsHopSourceRead": "來源 → 伺服器: {{throughput}}", - "metricsHopDestSftpWrite": "伺服器 → 目標(SFTP): {{throughput}}", - "metricsHopDestLocalWrite": "伺服器 → 目標(本地): {{throughput}}", - "metricsTransfer": "端對端: {{throughput}} ({{duration}})", - "metricsExtract": "提取到目標位置: {{duration}}", - "metricsSourceDelete": "從來源移除: {{duration}}", - "metricsTotal": "總計: {{duration}}", - "progressCompressing": "在來源主機上進行壓縮…", - "progressExtracting": "正在從目標位置… 提取", - "progressTransferring": "正在傳輸資料…", - "progressReconnecting": "正在重新連線…", - "parallelSegmentsLabel": "平行換乘車道", - "parallelSegmentsOption": "{{count}} 車道", - "parallelSegmentsHint": "大檔案會被分割成 256 MB 的資料塊。多通道使用獨立的連線(類似於同時啟動多個傳輸)以提高總吞吐量。", + "copyToHost": "Copy to host…", + "moveToHost": "Move to host…", + "copyItemsToHost": "Copy {{count}} items to host…", + "moveItemsToHost": "Move {{count}} items to host…", + "noHostsConnected": "No other file-manager hosts available.", + "noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.", + "selectDestinationHost": "Select destination host", + "destinationPath": "Destination path", + "recentDestinations": "Recent destinations", + "collapseRecentDestinations": "Collapse recent destinations", + "expandRecentDestinations": "Expand recent destinations", + "browseFolders": "Browse destination folders", + "browseDestination": "Browse or enter path", + "confirmCopy": "Copy", + "confirmMove": "Move", + "transferring": "Transferring…", + "compressing": "Compressing…", + "extracting": "Extracting…", + "transferringItems": "Transferring {{current}} of {{total}} items…", + "transferSuccess": "Transfer complete", + "transferError": "Transfer failed", + "transferPartial": "Transfer completed with {{count}} errors", + "transferPartialHint": "Could not transfer: {{paths}}", + "itemsSummary": "{{count}} items", + "destMustBeDirectory": "Destination must be a directory for multi-item transfers", + "selectThisFolder": "Select this folder", + "browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.", + "browsePathError": "Could not open this path on the destination host.", + "goUp": "Go up", + "copyFolderToHost": "Copy folder to host…", + "moveFolderToHost": "Move folder to host…", + "hostReady": "Ready", + "hostConnecting": "Connecting…", + "hostDisconnected": "Not connected", + "hostAuthRequired": "Authentication required — open File Manager on this host first", + "hostConnectionFailed": "Connection failed", + "metricsTitle": "Transfer timings", + "metricsPrepare": "Prepare destination: {{duration}}", + "metricsCompress": "Compress on source: {{duration}}", + "metricsHopSourceRead": "Source → server: {{throughput}}", + "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", + "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}", + "metricsTransfer": "End-to-end: {{throughput}} ({{duration}})", + "metricsExtract": "Extract on destination: {{duration}}", + "metricsSourceDelete": "Remove from source: {{duration}}", + "metricsTotal": "Total: {{duration}}", + "progressCompressing": "Compressing on source host…", + "progressExtracting": "Extracting on destination…", + "progressTransferring": "Transferring data…", + "progressReconnecting": "Reconnecting…", + "parallelSegmentsLabel": "Parallel transfer lanes", + "parallelSegmentsOption": "{{count}} lanes", + "parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.", "progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)", - "progressTransferringItems": "正在傳輸檔案({{current}} 中的 {{total}})…", + "progressTransferringItems": "Transferring files ({{current}} of {{total}})…", "progressBytes": "{{transferred}} / {{total}}", - "progressItems": "{{current}} / {{total}} 文件", - "sourceNotDeletedPartial": "保留來源檔案(部分傳輸)", - "jumpHostLimitation": "兩台主機都必須能從Termix伺服器存取。不支援主機之間的直接路由。", - "cancel": "取消", - "methodLabel": "轉移方法", - "methodAuto": "汽車", - "methodTar": "Tar 歸檔", - "methodItemSftp": "按文件 SFTP", - "methodAutoHint": "根據檔案數量、大小和壓縮率選擇 tar 協定或按檔案 SFTP 協定。單一檔案始終使用串流 SFTP 協定。", - "methodTarHint": "在源端壓縮,傳輸單一壓縮包,在目標端解壓縮。兩個 Unix 主機都需要 tar 指令。", - "methodItemSftpHint": "透過 SFTP 逐一傳輸檔案。支援所有主機,包括 Windows 系統。", - "methodPreviewLoading": "計算轉移方法…", - "methodPreviewError": "無法預覽傳輸方式。伺服器將在您啟動時選擇一種傳輸方式。", - "methodPreviewWillUseTar": "將使用:Tar 歸檔", - "methodPreviewWillUseItemSftp": "將使用:按檔案 SFTP", - "methodPreviewScanSummary": "{{fileCount}} 個文件, {{totalSize}} 總計(在來源主機上掃描)。", - "methodItemSftpLimitation": "每個檔案都使用同一個 SFTP 流,以單一檔案複製的方式依序進行。所有檔案的進度是合併計算的,因此在處理大檔案時進度條移動較慢。", + "progressItems": "{{current}} / {{total}} files", + "sourceNotDeletedPartial": "Source files kept (partial transfer)", + "jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.", + "cancel": "Cancel", + "methodLabel": "Transfer method", + "methodAuto": "Auto", + "methodTar": "Tar archive", + "methodItemSftp": "Per-file SFTP", + "methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.", + "methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.", + "methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.", + "methodPreviewLoading": "Calculating transfer method…", + "methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.", + "methodPreviewWillUseTar": "Will use: Tar archive", + "methodPreviewWillUseItemSftp": "Will use: Per-file SFTP", + "methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).", + "methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.", "methodReason": { - "user_item_sftp": "您選擇了按檔案SFTP傳輸。", - "user_tar": "您選擇了tar歸檔。", - "tar_unavailable": "Tar 在一台或兩台主機上不可用-將改用按檔案 SFTP。", - "windows_host": "涉及 Windows 主機-未使用 tar。", - "auto_multi_large": "自動:多個文件,包括一個大文件({{largestSize}}),其中包含可壓縮資料-tar 打包成一個傳輸。", - "auto_single_large_in_archive": "自動:此集合中有一個大檔案({{largestSize}})—按檔案 SFTP。", - "auto_many_incompressible": "自動:主要為不可壓縮資料-按檔案 SFTP。", - "auto_many_files": "自動:多個檔案({{fileCount}})— tar 減少每個檔案的開銷。", - "auto_default": "自動:此資料集的每個檔案都使用 SFTP 進行管理。" + "user_item_sftp": "You chose per-file SFTP.", + "user_tar": "You chose tar archive.", + "tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.", + "windows_host": "A Windows host is involved — tar is not used.", + "auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.", + "auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.", + "auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.", + "auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.", + "auto_default": "Auto: per-file SFTP for this set." }, - "progressCancel": "取消", - "progressCancelling": "取消…", - "progressStalled": "停滯", - "resumedHint": "已重新連線到在另一個視窗中啟動的活動傳輸。", - "transferCancelled": "轉帳已取消", - "transferCancelledCopyHint": "目標位置可能仍會保留部分檔案。", - "transferCancelledMoveHint": "目標位置可能仍保留部分檔案。來源檔案未被刪除。", - "cleanupDestFiles": "清理目的地", - "cleanupDestFilesSuccess": "已從目標位置刪除部分文件", - "cleanupDestFilesPartial": "部分檔案無法刪除。", - "cleanupDestFilesNothing": "目的地無需清理", - "cleanupDestFilesError": "清理失敗", - "retryTransfer": "重試", - "retryTransferError": "重試失敗", - "transferFailedRetryHint": "目標端保留了部分資料。連線恢復後,系統將恢復重試。" + "progressCancel": "Cancel", + "progressCancelling": "Cancelling…", + "progressStalled": "Stalled", + "resumedHint": "Reconnected to an active transfer started in another window.", + "transferCancelled": "Transfer cancelled", + "transferCancelledCopyHint": "Partial files may remain on the destination.", + "transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.", + "cleanupDestFiles": "Clean up destination", + "cleanupDestFilesSuccess": "Removed partial files from the destination", + "cleanupDestFilesPartial": "Some partial files could not be removed", + "cleanupDestFilesNothing": "Nothing to clean up on the destination", + "cleanupDestFilesError": "Cleanup failed", + "retryTransfer": "Retry", + "retryTransferError": "Retry failed", + "transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back." }, "tunnels": { - "noSshTunnels": "沒有 SSH 隧道", - "createFirstTunnelMessage": "您尚未建立任何 SSH 隧道。請在主機管理員中設定隧道連線以開始使用。", - "connected": "已連接", - "disconnected": "斷開連接", - "connecting": "正在連接...", - "error": "錯誤", - "canceling": "正在取消…", - "connect": "連接", - "disconnect": "斷開", - "cancel": "取消", - "port": "連接埠", - "localPort": "本地連接埠", - "remotePort": "遠端埠", - "currentHostPort": "目前主機連接埠", - "endpointPort": "端點埠", - "bindIp": "本地IP", - "endpointSshConfig": "端點 SSH 配置", - "endpointSshHost": "端點 SSH 主機", - "endpointSshHostPlaceholder": "選擇已配置的主機", - "endpointSshHostRequired": "為每個用戶端隧道選擇一個 SSH 端點主機。", - "attempt": "試 {{current}} 的 {{max}}", - "nextRetryIn": "下次重試時間為 {{seconds}} 秒", - "clientTunnels": "用戶端隧道", - "clientTunnel": "用戶端隧道", - "addClientTunnel": "新增客戶端隧道", - "noClientTunnels": "此桌面未配置客戶端隧道。", - "tunnelName": "隧道名稱", - "remoteHost": "遠端主機", - "autoStart": "自動啟動", - "clientAutoStartDesc": "當此桌面用戶端開啟時開始,並保持連線。", - "clientManualStartDesc": "使用此行中的“開始”和“停止”按鈕。 Termix 不會自動開啟它。", - "clientRemoteServerNote": "遠端轉送可能需要在終端 SSH 伺服器上啟用 AllowTcpForwarding 和 GatewayPorts 選項。當此桌面斷開連線時,遠端連接埠將關閉。", - "clientTunnelStarted": "用戶端隧道已啟動", - "clientTunnelStopped": "用戶端隧道已停止", - "tunnelTestSucceeded": "隧道試驗成功", - "tunnelTestFailed": "隧道測試失敗", - "localSaved": "用戶端隧道已儲存", - "localSaveError": "保存本機用戶端隧道失敗", - "invalidBindIp": "本機IP位址必須是有效的IPv4位址。", - "invalidLocalTargetIp": "本機目標IP位址必須是有效的IPv4位址。", - "invalidLocalPort": "本機連接埠必須介於 1 和 65535 之間。", - "invalidRemotePort": "遠端連接埠必須介於 1 和 65535 之間。", - "invalidLocalTargetPort": "本地目標連接埠必須介於 1 和 65535 之間。", - "invalidEndpointPort": "端點連接埠必須介於 1 和 65535 之間。", - "duplicateAutoStartBind": "只能有一個自動啟動客戶端隧道使用 {{bind}}。", - "manualControlError": "隧道狀態更新失敗。", - "active": "積極的", - "start": "開始", - "stop": "停止", - "test": "測試", - "type": "隧道類型", - "typeLocal": "本地(-L)", - "typeRemote": "遙控器(-R)", - "typeDynamic": "動態(-D)", - "typeServerLocalDesc": "目前主機到端點。", - "typeServerRemoteDesc": "傳回目前主機的端點。", - "typeClientLocalDesc": "本地計算機到終端機。", - "typeClientRemoteDesc": "傳回本機的端點。", - "typeClientDynamicDesc": "本機上的 SOCKS。", - "typeDynamicDesc": "透過 SSH 轉送 SOCKS5 CONNECT 流量。", - "forwardDescriptionServerLocal": "目前主機 {{sourcePort}} → 端點 {{endpointPort}}。", - "forwardDescriptionServerRemote": "端點 {{endpointPort}} → 目前主機 {{sourcePort}}。", - "forwardDescriptionServerDynamic": "目前主機上的 SOCKS {{sourcePort}}。", - "forwardDescriptionClientLocal": "本地 {{sourcePort}} → 遠端 {{endpointPort}}。", - "forwardDescriptionClientRemote": "遠端 {{sourcePort}} → 本地 {{endpointPort}}。", - "forwardDescriptionClientDynamic": "本機連接埠上的 SOCKS {{sourcePort}}。", + "noSshTunnels": "No SSH Tunnels", + "createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "error": "Error", + "canceling": "Canceling...", + "connect": "Connect", + "disconnect": "Disconnect", + "cancel": "Cancel", + "port": "Port", + "localPort": "Local Port", + "remotePort": "Remote Port", + "currentHostPort": "Current Host Port", + "endpointPort": "Endpoint Port", + "bindIp": "Local IP", + "endpointSshConfig": "Endpoint SSH Configuration", + "endpointSshHost": "Endpoint SSH Host", + "endpointSshHostPlaceholder": "Select a configured host", + "endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.", + "attempt": "Attempt {{current}} of {{max}}", + "nextRetryIn": "Next retry in {{seconds}} seconds", + "clientTunnels": "Client Tunnels", + "clientTunnel": "Client Tunnel", + "addClientTunnel": "Add Client Tunnel", + "noClientTunnels": "No client tunnels configured on this desktop.", + "tunnelName": "Tunnel Name", + "remoteHost": "Remote Host", + "autoStart": "Auto Start", + "clientAutoStartDesc": "Starts when this desktop client opens and stays connected.", + "clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.", + "clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.", + "clientTunnelStarted": "Client tunnel started", + "clientTunnelStopped": "Client tunnel stopped", + "tunnelTestSucceeded": "Tunnel test succeeded", + "tunnelTestFailed": "Tunnel test failed", + "localSaved": "Client tunnels saved", + "localSaveError": "Failed to save local client tunnels", + "invalidBindIp": "Local IP must be a valid IPv4 address.", + "invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.", + "invalidLocalPort": "Local port must be between 1 and 65535.", + "invalidRemotePort": "Remote port must be between 1 and 65535.", + "invalidLocalTargetPort": "Local target port must be between 1 and 65535.", + "invalidEndpointPort": "Endpoint port must be between 1 and 65535.", + "duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.", + "manualControlError": "Failed to update tunnel state.", + "active": "Active", + "start": "Start", + "stop": "Stop", + "test": "Test", + "type": "Tunnel Type", + "typeLocal": "Local (-L)", + "typeRemote": "Remote (-R)", + "typeDynamic": "Dynamic (-D)", + "typeServerLocalDesc": "Current host to endpoint.", + "typeServerRemoteDesc": "Endpoint back to current host.", + "typeClientLocalDesc": "Local computer to endpoint.", + "typeClientRemoteDesc": "Endpoint back to local computer.", + "typeClientDynamicDesc": "SOCKS on local computer.", + "typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH", + "forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.", + "forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.", + "forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.", + "forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.", + "forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.", + "forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}", "summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}", - "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS 透過 {{endpoint}}", - "autoNameClientLocal": "本地 {{localPort}} → {{endpoint}} {{remotePort}}", - "autoNameClientRemote": "{{endpoint}} {{remotePort}} → 本地 {{localPort}}", - "autoNameClientDynamic": "襪子 {{localPort}} 透過 {{endpoint}}", - "route": "路線:", - "lastStarted": "上次開始", - "lastTested": "上次測試", - "lastError": "最後錯誤", - "maxRetries": "最大重試次數", - "maxRetriesDescription": "最大重試次數。", - "retryInterval": "重試間隔(秒)", - "retryIntervalDescription": "重試之間需要等待一段時間。", - "local": "本地", - "remote": "偏僻的", - "destination": "目的地", - "host": "主持人", - "mode": "模式", - "noHostSelected": "未選擇主機", - "working": "在職的..." + "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}", + "autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}", + "autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}", + "autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}", + "route": "Route:", + "lastStarted": "Last started", + "lastTested": "Last tested", + "lastError": "Last error", + "maxRetries": "Max Retries", + "maxRetriesDescription": "Maximum amount of retry attempts.", + "retryInterval": "Retry Interval (seconds)", + "retryIntervalDescription": "Time to wait between retry attempts.", + "local": "Local", + "remote": "Remote", + "destination": "Destination", + "host": "Host", + "mode": "Mode", + "noHostSelected": "No host selected", + "working": "Working..." }, - "serverStats": { - "cpu": "中央處理器", - "memory": "記憶", - "disk": "磁碟", - "network": "網路", - "uptime": "正常運作時間", - "processes": "程序", - "available": "可用的", - "free": "自由的", - "connecting": "正在連接...", - "connectionFailed": "連線伺服器失敗", - "naCpus": "N/A CPU", - "cpuCores_one": "{{count}} 核心", - "cpuCores_other": "{{count}} 核心", - "cpuUsage": "CPU 使用率", - "memoryUsage": "記憶體使用情況", - "diskUsage": "磁碟使用情況", - "failedToFetchHostConfig": "取得主機配置失敗", - "serverOffline": "伺服器離線", - "cannotFetchMetrics": "無法從離線伺服器取得指標", - "totpFailed": "TOTP驗證失敗", - "noneAuthNotSupported": "伺服器統計資訊不支援「無」身份驗證類型。", - "load": "載入", - "systemInfo": "系統資訊", - "hostname": "主機名稱", - "operatingSystem": "作業系統", - "kernel": "核心", - "seconds": "秒", - "networkInterfaces": "網路介面", - "noInterfacesFound": "未找到網路介面", - "noProcessesFound": "未找到任何進程", - "loginStats": "SSH 登入統計訊息", - "noRecentLoginData": "沒有最近的登入數據", - "executingQuickAction": "正在執行 {{name}}...", - "quickActionSuccess": "{{name}} 已成功完成", - "quickActionFailed": "{{name}} 失敗", - "quickActionError": "執行 {{name}} 失敗", + "cardGrid": { + "dragToMove": "Drag to move", + "dragToResize": "Drag to resize", + "changeWidth": "Change width", + "removeCard": "Remove {{label}}", + "addCard": "Add", + "columns": "Columns", + "empty": "No cards. Use Add below to place cards." + }, + "hostMetrics": { + "cpu": "CPU", + "memory": "Memory", + "disk": "Disk", + "network": "Network", + "uptime": "Uptime", + "processes": "Processes", + "available": "Available", + "free": "Free", + "connecting": "Connecting...", + "connectionFailed": "Failed to connect to server", + "naCpus": "N/A CPU(s)", + "cpuCores_one": "{{count}} Core", + "cpuCores_other": "{{count}} Cores", + "cpuUsage": "CPU Usage", + "memoryUsage": "Memory Usage", + "diskUsage": "Disk Usage", + "failedToFetchHostConfig": "Failed to fetch host configuration", + "serverOffline": "Server Offline", + "cannotFetchMetrics": "Cannot fetch metrics from offline server", + "totpFailed": "TOTP verification failed", + "noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.", + "noHostSelected": "No host selected", + "load": "Load", + "systemInfo": "System Information", + "hostname": "Hostname", + "operatingSystem": "Operating System", + "kernel": "Kernel", + "seconds": "seconds", + "networkInterfaces": "Network Interfaces", + "noInterfacesFound": "No network interfaces found", + "noProcessesFound": "No processes found", + "processesTotal": "total", + "processesRunning": "running", + "loginStats": "SSH Login Statistics", + "noRecentLoginData": "No recent login data", + "executingQuickAction": "Executing {{name}}...", + "quickActionSuccess": "{{name}} completed successfully", + "quickActionFailed": "{{name}} failed", + "quickActionError": "Failed to execute {{name}}", "ports": { - "title": "監聽埠", - "protocol": "協定", - "port": "連接埠", - "address": "地址", - "process": "過程", - "noData": "無監聽埠數據" + "title": "Listening Ports", + "protocol": "Protocol", + "port": "Port", + "address": "Address", + "process": "Process", + "search": "Search ports...", + "allProtocols": "All", + "noData": "No listening ports data" }, "firewall": { - "title": "防火牆", - "inactive": "非活躍狀態", - "policy": "政策", - "rules": "規則", - "noData": "沒有可用的防火牆數據", - "action": "行動", - "protocol": "原型", - "port": "連接埠", - "source": "來源", - "anywhere": "任何地方" + "title": "Firewall", + "inactive": "Inactive", + "policy": "Policy", + "rules": "rules", + "noRules": "No rules in this chain", + "noData": "No firewall data available", + "action": "Action", + "protocol": "Proto", + "port": "Port", + "source": "Source", + "anywhere": "Anywhere", + "chains": "chains" }, - "loadAvg": "平均負載", - "swap": "交換", - "architecture": "建築學", - "refresh": "重新整理", - "retry": "重試" + "loadAvg": "Load Avg", + "swap": "Swap", + "architecture": "Architecture", + "refresh": "Refresh", + "retry": "Retry", + "customize": "Customize layout", + "reset": "Reset", + "editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.", + "managers": { + "services": "Services", + "processInspector": "Process Inspector", + "logViewer": "Log Viewer", + "cron": "Cron Jobs", + "packages": "Packages", + "ssl": "SSL Certificates", + "firewall": "Firewall", + "users": "Users & Permissions", + "healthCheck": "Health Checks", + "diskBreakdown": "Disk Breakdown", + "systemdTimers": "Timers", + "topMemory": "Top by Memory", + "noData": "No data", + "sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.", + "filter": "Filter...", + "start": "Start", + "stop": "Stop", + "restart": "Restart", + "actionDone": "{{name}} updated", + "actionFailed": "Action failed", + "signalSent": "Signal sent to PID {{pid}}", + "killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).", + "working": "Working...", + "update": "Update", + "upgradeAll": "Upgrade all", + "allUpToDate": "Everything is up to date", + "save": "Save", + "command": "Command", + "enabled": "Enabled", + "cronSaved": "Crontab updated", + "clients": "Clients", + "dryRun": "Dry run", + "renew": "Renew", + "noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.", + "addInputRule": "Add INPUT rule", + "firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.", + "ruleApplied": "Rule applied", + "invalidPort": "Enter a valid port (1-65535)", + "newUsername": "New username", + "addUser": "Add", + "deleteUser": "Delete user", + "noHealthChecks": "No health checks configured yet.", + "follow": "Follow", + "noLogData": "No log output.", + "tree": "Tree", + "enableDisable": "Enable / disable at boot", + "grantSudo": "Grant sudo", + "revokeSudo": "Revoke sudo", + "sslIssueCert": "Issue certificate", + "sslExpired": "Expired", + "sslInDays": "in {{days}}d", + "sslNeedDomain": "Enter at least one domain", + "sslIssued": "Certificate issued", + "sslDomainsPlaceholder": "example.com, www.example.com", + "sslHttpStandalone": "HTTP (standalone)", + "sslHttpWebroot": "HTTP (webroot)", + "sslDns": "DNS", + "sslDnsProvider": "DNS provider (e.g. cloudflare)", + "sslIssueHint": "DNS provider credentials must already be configured on the host.", + "sslRevoke": "Revoke certificate", + "sslRevoked": "Certificate revoked", + "sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.", + "healthRun": "Run", + "healthName": "Name", + "healthTarget": "Host / address", + "healthAddCheck": "Add check", + "healthSaved": "Health checks saved", + "healthMissingFields": "Each check needs a name and target", + "logFile": "File", + "logUnit": "Unit", + "logCustomPath": "Custom path under /var/log (optional)", + "logGrep": "Filter lines...", + "firewallPersist": "Persist rules", + "firewallPersisted": "Firewall rules persisted" + } }, "auth": { - "tagline": "自託管 SSH 和遠端桌面管理", - "loginTitle": "登入 Termix", - "registerTitle": "建立帳戶", - "forgotPassword": "忘記密碼?", - "rememberMe": "記住設備30天(包含TOTP)", - "noAccount": "還沒有帳號?", - "hasAccount": "已有帳號?", - "twoFactorAuth": "雙重身份驗證", - "enterCode": "請輸入驗證碼", - "backupCode": "或使用備用代碼", - "verifyCode": "驗證碼", - "redirectingToApp": "正在重定向到應用程式...", - "sshAuthenticationRequired": "需要 SSH 驗證", - "sshNoKeyboardInteractive": "鍵盤互動式驗證不可用", - "sshAuthenticationFailed": "身份驗證失敗", - "sshAuthenticationTimeout": "身份驗證逾時", - "sshNoKeyboardInteractiveDescription": "伺服器不支援鍵盤互動式身份驗證。請提供您的密碼或SSH密鑰。", - "sshAuthFailedDescription": "您提供的憑證不正確。請使用有效的憑證重試。", - "sshTimeoutDescription": "身份驗證嘗試超時,請重試。", - "sshProvideCredentialsDescription": "請提供您的 SSH 憑證以連接到此伺服器。", - "sshPasswordDescription": "請輸入此 SSH 連線的密碼。", - "sshKeyPasswordDescription": "如果您的 SSH 金鑰已加密,請在此輸入密碼。", - "passphraseRequired": "需要密碼", - "passphraseRequiredDescription": "SSH金鑰已加密。請輸入密碼短語進行解鎖。", - "back": "後退", - "firstUser": "第一位用戶", - "firstUserMessage": "您是第一個用戶,將被授予管理員權限。您可以在側邊欄使用者下拉式選單中查看管理員設定。如果您認為這是一個錯誤,請查看 Docker 日誌或在 GitHub 上建立一個 issue。", - "external": "外部的", - "loginWithExternal": "使用外部提供者登入", - "loginWithExternalDesc": "使用您設定的外部身分提供者登入", - "externalNotSupportedInElectron": "Electron 應用程式目前尚不支援外部身份驗證。請使用網頁版進行 OIDC 登入。", - "resetPasswordButton": "重設密碼", - "sendResetCode": "發送重置代碼", - "resetCodeDesc": "請輸入您的使用者名稱以取得密碼重設代碼。該程式碼將記錄在 Docker 容器日誌中。", - "resetCode": "重置程式碼", - "verifyCodeButton": "驗證碼", - "enterResetCode": "請輸入 Docker 容器日誌中該使用者的 6 位元代碼:", - "newPassword": "新密碼", - "confirmNewPassword": "確認密碼", - "enterNewPassword": "請輸入使用者的新密碼:", - "signUp": "報名", - "desktopApp": "桌面應用程式", - "loggingInToDesktopApp": "登入桌面應用程式", - "loadingServer": "伺服器正在載入...", - "dataLossWarning": "使用此方法重設密碼將刪除您所有已儲存的 SSH 主機、憑證和其他加密資料。此操作無法撤銷。僅當您忘記密碼且未登入時才使用此方法。", - "authenticationDisabled": "身份驗證已停用", - "authenticationDisabledDesc": "所有身份驗證方式目前均已停用。請聯絡您的管理員。", - "attemptsRemaining": "{{count}} 剩餘嘗試次數" + "tagline": "Self-hosted SSH and remote desktop management", + "loginTitle": "Login to Termix", + "registerTitle": "Create Account", + "forgotPassword": "Forgot Password?", + "rememberMe": "Remember Device for 30 Days (includes TOTP)", + "noAccount": "Don't have an account?", + "hasAccount": "Already have an account?", + "twoFactorAuth": "Two-Factor Authentication", + "enterCode": "Enter verification code", + "backupCode": "Or use backup code", + "verifyCode": "Verify Code", + "redirectingToApp": "Redirecting to app...", + "sshAuthenticationRequired": "SSH Authentication Required", + "sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable", + "sshAuthenticationFailed": "Authentication Failed", + "sshAuthenticationTimeout": "Authentication Timeout", + "sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.", + "sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.", + "sshTimeoutDescription": "The authentication attempt timed out. Please try again.", + "sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.", + "sshPasswordDescription": "Enter the password for this SSH connection.", + "sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.", + "passphraseRequired": "Passphrase Required", + "passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.", + "back": "Back", + "firstUser": "First User", + "firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.", + "external": "External", + "loginWithExternal": "Login with External Provider", + "loginWithExternalDesc": "Login using your configured external identity provider", + "externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.", + "loginWithProvider": "Login with {{name}}", + "orContinueWith": "or continue with", + "ldapUsername": "LDAP Username", + "ldapPassword": "LDAP Password", + "ldapSignIn": "Sign In", + "ldapLoginFailed": "LDAP login failed", + "resetPasswordButton": "Reset Password", + "sendResetCode": "Send Reset Code", + "resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.", + "resetCode": "Reset Code", + "verifyCodeButton": "Verify Code", + "enterResetCode": "Enter the 6-digit code from the docker container logs for user:", + "newPassword": "New Password", + "confirmNewPassword": "Confirm Password", + "enterNewPassword": "Enter your new password for user:", + "signUp": "Sign Up", + "desktopApp": "Desktop App", + "loggingInToDesktopApp": "Logging in to the desktop app", + "loadingServer": "Loading server...", + "dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.", + "authenticationDisabled": "Authentication Disabled", + "authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.", + "attemptsRemaining": "{{count}} attempts remaining" }, "hostKey": { - "verifyNewHost": "驗證 SSH 主機金鑰", - "keyChangedWarning": "SSH主機金鑰已更改", - "firstConnectionTitle": "首次連線到此主機", - "firstConnectionDescription": "無法確認此主機的真實性。請驗證指紋是否符合預期。", - "keyChangedDescription": "此伺服器的主機金鑰自您上次連線以來已變更。這可能表示存在安全問題。", - "previousKey": "上一個密鑰", - "newFingerprint": "新指紋", - "fingerprint": "指紋", - "verifyInstructions": "如果您信任此主機,請按一下「接受」繼續並儲存此指紋以便將來連線。", - "securityWarning": "安全警告", - "acceptAndContinue": "接受並繼續", - "acceptNewKey": "接受新密鑰並繼續" + "verifyNewHost": "Verify SSH Host Key", + "keyChangedWarning": "SSH Host Key Changed", + "firstConnectionTitle": "First time connecting to this host", + "firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.", + "keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.", + "previousKey": "Previous Key", + "newFingerprint": "New Fingerprint", + "fingerprint": "Fingerprint", + "verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.", + "securityWarning": "Security Warning", + "acceptAndContinue": "Accept & Continue", + "acceptNewKey": "Accept New Key & Continue" }, "errors": { - "databaseConnection": "無法連線到資料庫", - "unknownError": "未知錯誤", - "loginFailed": "登入失敗", - "failedPasswordReset": "密碼重置失敗", - "failedVerifyCode": "重置程式碼驗證失敗", - "failedCompleteReset": "密碼重置失敗", - "invalidTotpCode": "無效的 TOTP 程式碼", - "failedOidcLogin": "OIDC 登入啟動失敗", - "silentSigninOidcUnavailable": "已請求靜默登錄,但 OIDC 登入不可用。", - "failedUserInfo": "登入後取得使用者資訊失敗", - "oidcAuthFailed": "OIDC 驗證失敗", - "invalidAuthUrl": "從後端收到的授權 URL 無效", - "requiredField": "此欄位是必需的", - "minLength": "最小長度為 {{min}}", - "passwordMismatch": "密碼不匹配", - "passwordLoginDisabled": "目前已停用使用者名稱/密碼登入。", - "sessionExpired": "會話已過期 - 請重新登錄", - "totpRateLimited": "頻率限制:TOTP驗證嘗試次數過多。請稍後再試。", - "totpRateLimitedWithTime": "速率限制:TOTP 驗證嘗試次數過多。請稍候 {{time}} 秒後再試。", - "resetCodeRateLimited": "驗證次數過多,請稍後再試。", - "resetCodeRateLimitedWithTime": "驗證次數過多,請稍等 {{time}} 秒後再試。", - "authTokenSaveFailed": "保存身份驗證令牌失敗", - "failedToLoadServer": "伺服器載入失敗" + "databaseConnection": "Could not connect to the database", + "unknownError": "Unknown error", + "loginFailed": "Login failed", + "failedPasswordReset": "Failed to initiate password reset", + "failedVerifyCode": "Failed to verify reset code", + "failedCompleteReset": "Failed to complete password reset", + "invalidTotpCode": "Invalid TOTP code", + "failedOidcLogin": "Failed to start OIDC login", + "silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.", + "failedUserInfo": "Failed to get user info after login", + "oidcAuthFailed": "OIDC authentication failed", + "invalidAuthUrl": "Invalid authorization URL received from backend", + "requiredField": "This field is required", + "minLength": "Minimum length is {{min}}", + "passwordMismatch": "Passwords do not match", + "passwordLoginDisabled": "Username/password login is currently disabled", + "sessionExpired": "Session expired - please log in again", + "totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.", + "totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.", + "resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.", + "resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.", + "authTokenSaveFailed": "Failed to save authentication token", + "failedToLoadServer": "Failed to load server" }, "messages": { - "registrationDisabled": "管理員已停用新帳號註冊功能。請登入或聯絡管理員。", - "userNotAllowed": "您的帳戶未獲得註冊授權。請聯絡管理員。", - "databaseConnectionFailed": "連線資料庫伺服器失敗", - "resetCodeSent": "重置代碼已傳送至 Docker 日誌", - "codeVerified": "程式碼驗證成功", - "passwordResetSuccess": "密碼重置成功", - "loginSuccess": "登入成功", - "registrationSuccess": "註冊成功" + "registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.", + "userNotAllowed": "Your account is not authorized to register. Please contact an administrator.", + "databaseConnectionFailed": "Failed to connect to the database server", + "resetCodeSent": "Reset code sent to Docker logs", + "codeVerified": "Code verified successfully", + "passwordResetSuccess": "Password reset successfully", + "loginSuccess": "Login successful", + "registrationSuccess": "Registration successful" }, "profile": { - "c2sTunnelConfigDesc": "針對已設定 SSH 主機的本機桌面隧道。", - "c2sTunnelPresets": "客戶端隧道預設", - "c2sTunnelPresetsDesc": "將此桌面用戶端的本機隧道清單儲存為命名伺服器預設,或將預設載入回此用戶端。", - "c2sTunnelPresetsUnavailable": "客戶端隧道預設僅在桌面用戶端中可用。", - "c2sPresetName": "預設名稱", - "c2sPresetNamePlaceholder": "客戶預設名稱", - "c2sPresetToLoad": "預設載入", - "c2sNoPresetSelected": "未選擇預設值", - "c2sNoPresets": "未儲存任何預設。", - "c2sLoadPreset": "載入", - "c2sCurrentLocalConfig": "{{count}} 此桌面上配置了本機用戶端隧道。", - "c2sPresetSyncNote": "預設是明確的快照;載入一個預設會取代此桌面用戶端的本機用戶端隧道清單。", - "c2sPresetSaved": "客戶端隧道預設已儲存", - "c2sPresetLoaded": "客戶端隧道預設已本地加載", - "c2sPresetRenamed": "用戶端隧道預設已重新命名", - "c2sPresetDeleted": "客戶端隧道預設已刪除", - "c2sPresetLoadError": "客戶端隧道預設載入失敗" + "c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.", + "c2sTunnelPresets": "Client Tunnel Presets", + "c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.", + "c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.", + "c2sPresetName": "Preset Name", + "c2sPresetNamePlaceholder": "Client preset name", + "c2sPresetToLoad": "Preset To Load", + "c2sNoPresetSelected": "No preset selected", + "c2sNoPresets": "No presets saved", + "c2sLoadPreset": "Load", + "c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.", + "c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.", + "c2sPresetSaved": "Client tunnel preset saved", + "c2sPresetLoaded": "Client tunnel preset loaded locally", + "c2sPresetRenamed": "Client tunnel preset renamed", + "c2sPresetDeleted": "Client tunnel preset deleted", + "c2sPresetLoadError": "Failed to load client tunnel presets" }, "placeholders": { "maxRetries": "3", "retryInterval": "10", - "language": "語言", - "keyPassword": "密鑰密碼", - "pastePrivateKey": "把你的私鑰貼到這裡…", - "localListenerHost": "127.0.0.1(本地收聽)", - "localTargetHost": "127.0.0.1(此計算機上的目標)", - "socksListenerHost": "127.0.0.1(SOCKS 監聽器)", - "enterPassword": "請輸入您的密碼", + "language": "Language", + "keyPassword": "key password", + "pastePrivateKey": "Paste your private key here...", + "localListenerHost": "127.0.0.1 (listen locally)", + "localTargetHost": "127.0.0.1 (target on this computer)", + "socksListenerHost": "127.0.0.1 (SOCKS listener)", + "enterPassword": "Enter your password", "defaultPort": "22", "defaultEndpointPort": "224" }, "dashboard": { - "title": "儀表板", - "loading": "正在載入儀錶板……", + "title": "Dashboard", + "loading": "Loading dashboard...", "github": "GitHub", - "support": "支援", + "support": "Support", "discord": "Discord", - "serverOverview": "伺服器概覽", - "version": "版本", - "upToDate": "最新", - "updateAvailable": "更新可用", + "docs": "Docs", + "serverOverview": "Server Overview", + "version": "Version", + "upToDate": "Up to Date", + "updateAvailable": "Update Available", "beta": "Beta", - "uptime": "正常運作時間", - "database": "資料庫", - "healthy": "健康", - "error": "錯誤", - "totalHosts": "主持人總數", - "totalTunnels": "隧道總數", - "totalCredentials": "證書總數", - "recentActivity": "近期活動", - "reset": "重置", - "loadingRecentActivity": "正在載入最近的活動……", - "noRecentActivity": "近期無活動", - "quickActions": "快速操作", - "addHost": "新增主機", - "addCredential": "新增憑證", - "adminSettings": "管理員設定", - "userProfile": "用戶個人資料", - "serverStats": "伺服器統計訊息", - "loadingServerStats": "正在載入伺服器統計資料…", - "noServerData": "伺服器資料不可用", - "cpu": "中央處理器", - "ram": "記憶體", - "customizeLayout": "自訂儀錶板", - "dashboardSettings": "儀表板設定", - "enableDisableCards": "啟用/停用卡片", - "resetLayout": "重設為預設值", - "serverOverviewCard": "伺服器概覽", - "recentActivityCard": "近期活動", - "networkGraphCard": "網路圖", - "networkGraph": "網路圖", - "quickActionsCard": "快速操作", - "serverStatsCard": "伺服器統計訊息", - "panelMain": "主要的", - "panelSide": "邊", - "justNow": "現在" + "uptime": "Uptime", + "database": "Database", + "healthy": "Healthy", + "error": "Error", + "totalHosts": "Total Hosts", + "totalTunnels": "Total Tunnels", + "totalCredentials": "Total Credentials", + "recentActivity": "Recent Activity", + "reset": "Reset", + "loadingRecentActivity": "Loading recent activity...", + "noRecentActivity": "No recent activity", + "quickActions": "Quick Actions", + "addHost": "Add Host", + "addCredential": "Add Credential", + "adminSettings": "Admin Settings", + "userProfile": "User Profile", + "serverStats": "Server Stats", + "loadingServerStats": "Loading server stats...", + "noServerData": "No server data available", + "cpu": "CPU", + "ram": "RAM", + "customizeLayout": "Customize Dashboard", + "dashboardSettings": "Dashboard Settings", + "enableDisableCards": "Enable/Disable Cards", + "resetLayout": "Reset to Default", + "serverOverviewCard": "Server Overview", + "recentActivityCard": "Recent Activity", + "networkGraphCard": "Network Graph", + "networkGraph": "Network Graph", + "quickActionsCard": "Quick Actions", + "serverStatsCard": "Server Stats", + "panelMain": "Main", + "panelSide": "Side", + "justNow": "just now" }, "dashboardTab": { - "stable": "穩定的", - "hostsOnline": "線上主持人", - "activeTunnels": "活躍隧道", - "registerNewServer": "註冊新伺服器", - "storeSshKeysOrPasswords": "儲存 SSH 金鑰或密碼", - "manageUsersAndRoles": "管理使用者和角色", - "manageYourAccount": "管理您的帳戶", - "hostStatus": "主機狀態", - "noHostsConfigured": "未配置任何主機", - "online": "在線的", - "offline": "離線", - "onlineLower": "在線的", - "nodes": "{{count}} 節點", - "add": "添加:", - "commandPalette": "命令面板", - "done": "完畢", - "editModeInstructions": "拖曳卡片重新排序 · 拖曳列分隔線調整列寬 · 拖曳卡片底部邊緣調整卡片高度 · 點選垃圾桶圖示刪除卡片", - "empty": "空的", - "clear": "清除" + "stable": "STABLE", + "hostsOnline": "Hosts Online", + "activeTunnels": "Active Tunnels", + "registerNewServer": "Register a new server", + "storeSshKeysOrPasswords": "Store SSH keys or passwords", + "manageUsersAndRoles": "Manage users and roles", + "manageYourAccount": "Manage your account", + "hostStatus": "Host Status", + "noHostsConfigured": "No hosts configured", + "online": "ONLINE", + "offline": "OFFLINE", + "onlineLower": "Online", + "nodes": "{{count}} nodes", + "add": "Add:", + "commandPalette": "Command Palette", + "done": "Done", + "editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove", + "empty": "Empty", + "clear": "Clear" + }, + "sessionLogs": { + "title": "Session Logs", + "noLogs": "No session logs yet", + "noLogsDesc": "Enable session logging on a host to start recording", + "duration": "Duration", + "viewLog": "View log", + "downloadLog": "Download", + "deleteLog": "Delete", + "confirmDelete": "Delete this session log?", + "confirmDeleteDesc": "This action cannot be undone.", + "copyContent": "Copy", + "copied": "Copied!", + "loadError": "Failed to load session logs", + "deleteError": "Failed to delete session log", + "filterByHost": "Filter by host..." }, "networkGraph": { - "addHost": "新增主機", - "addGroup": "新增群組", - "addLink": "添加連結", - "zoomIn": "放大", - "zoomOut": "縮小", - "resetView": "重置視圖", - "selectHost": "選擇主機", - "chooseHost": "選擇主機…", - "parentGroup": "父群組", - "noGroup": "無組", - "groupName": "組名", - "color": "顏色", - "source": "來源", - "target": "目標", - "moveToGroup": "移至群組", - "selectGroup": "選擇群組...", - "addConnection": "新增連接", - "hostDetails": "主機詳情", - "removeFromGroup": "從群組移除", - "addHostHere": "在此新增主機", - "editGroup": "編輯群組", - "delete": "刪除", - "add": "添加", - "create": "建立", - "move": "移動", - "connect": "連接", - "createGroup": "建立群組", - "selectSourcePlaceholder": "選擇來源...", - "selectTargetPlaceholder": "選擇目標...", - "invalidFile": "無效檔案", - "hostAlreadyExists": "主機已在拓樸結構中", - "connectionExists": "連線已存在", - "unknown": "未知", - "name": "姓名", + "addHost": "Add Host", + "addGroup": "Add Group", + "addLink": "Add Link", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "resetView": "Reset View", + "selectHost": "Select Host", + "chooseHost": "Choose a host...", + "parentGroup": "Parent Group", + "noGroup": "No Group", + "groupName": "Group Name", + "color": "Color", + "source": "Source", + "target": "Target", + "moveToGroup": "Move to Group", + "selectGroup": "Select group...", + "addConnection": "Add Connection", + "hostDetails": "Host Details", + "removeFromGroup": "Remove from Group", + "addHostHere": "Add Host Here", + "editGroup": "Edit Group", + "delete": "Delete", + "add": "Add", + "create": "Create", + "move": "Move", + "connect": "Connect", + "createGroup": "Create Group", + "selectSourcePlaceholder": "Select Source...", + "selectTargetPlaceholder": "Select Target...", + "invalidFile": "Invalid File", + "hostAlreadyExists": "Host is already in the topology", + "connectionExists": "Connection already exists", + "unknown": "Unknown", + "name": "Name", "ip": "IP", - "status": "地位", - "failedToAddNode": "新增節點失敗", - "sourceDifferentFromTarget": "源和目標必須不同", - "exportJSON": "導出 JSON", - "importJSON": "導入 JSON", - "terminal": "終端機", - "fileManager": "檔案管理器", - "tunnel": "隧道", + "status": "Status", + "failedToAddNode": "Failed to add node", + "sourceDifferentFromTarget": "Source and target must be different", + "exportJSON": "Export JSON", + "importJSON": "Import JSON", + "terminal": "Terminal", + "fileManager": "File Manager", + "tunnel": "Tunnel", "docker": "Docker", - "serverStats": "伺服器統計訊息", - "noNodes": "暫無節點" + "serverStats": "Host Metrics", + "hostMetrics": "Host Metrics", + "noNodes": "No nodes yet" }, "docker": { - "notEnabled": "此主機未啟用 Docker", - "validating": "正在驗證 Docker...", - "connecting": "正在連接...", - "error": "錯誤", + "notEnabled": "Docker is not enabled for this host", + "validating": "Validating Docker...", + "connecting": "Connecting...", + "error": "Error", "version": "Docker {{version}}", - "connectionFailed": "連線 Docker 失敗", - "containerStarted": "容器 {{name}} 已啟動", - "failedToStartContainer": "啟動容器失敗 {{name}}", - "containerStopped": "容器 {{name}} 已停止", - "failedToStopContainer": "停止容器 {{name}} 失敗", - "containerRestarted": "容器 {{name}} 已重啟", - "failedToRestartContainer": "重啟容器失敗 {{name}}", - "containerPaused": "容器 {{name}} 已暫停", - "containerUnpaused": "容器 {{name}} 已解除暫停", - "failedToTogglePauseContainer": "切換容器 {{name}} 的暫停狀態失敗", - "containerRemoved": "容器 {{name}} 已移除", - "failedToRemoveContainer": "移除容器 {{name}} 失敗", - "image": "影像", - "ports": "連接埠", - "noPorts": "無連接埠", - "start": "開始", - "confirmRemoveContainer": "您確定要刪除容器「{{name}}」嗎?此操作無法撤銷。", - "runningContainerWarning": "警告:此容器目前正在運行。移除此容器會先停止其運作。", - "loadingContainers": "正在裝載貨櫃…", - "manager": "Docker 管理器", - "autoRefresh": "自動重新整理", - "timestamps": "時間戳", - "lines": "線條", - "filterLogs": "過濾日誌...", - "refresh": "重新整理", - "download": "下載", - "clear": "清除", - "logsDownloaded": "日誌已成功下載", - "last50": "最後50", - "last100": "最近100", - "last500": "最近500", - "last1000": "最近的1000", - "allLogs": "所有日誌", - "noLogsMatching": "沒有與「{{query}}」相符的日誌", - "noLogsAvailable": "沒有可用日誌", - "noContainersFound": "未找到容器", - "noContainersFoundHint": "此主機上沒有可用的 Docker 容器", - "searchPlaceholder": "搜尋容器…", - "allStatuses": "所有狀態", - "stateRunning": "跑步", - "statePaused": "暫停", - "stateExited": "已退出", - "stateRestarting": "重新啟動", - "noContainersMatchFilters": "沒有容器符合您的篩選條件", - "noContainersMatchFiltersHint": "嘗試調整搜尋或篩選條件", - "failedToFetchStats": "取得容器統計資料失敗", - "containerNotRunning": "容器未運行", - "startContainerToViewStats": "啟動容器以查看統計信息", - "loadingStats": "正在載入統計資料…", - "errorLoadingStats": "載入統計資料時出錯", - "noStatsAvailable": "暫無統計數據", - "cpuUsage": "CPU 使用率", - "current": "目前的", - "memoryUsage": "記憶體使用情況", - "networkIo": "網路 I/O", - "input": "輸入", - "output": "輸出", - "blockIo": "塊 I/O", - "read": "讀", - "write": "寫", - "pids": "PID", - "containerInformation": "容器資訊", - "name": "姓名", + "connectionFailed": "Failed to connect to Docker", + "containerStarted": "Container {{name}} started", + "failedToStartContainer": "Failed to start container {{name}}", + "containerStopped": "Container {{name}} stopped", + "failedToStopContainer": "Failed to stop container {{name}}", + "containerRestarted": "Container {{name}} restarted", + "failedToRestartContainer": "Failed to restart container {{name}}", + "containerPaused": "Container {{name}} paused", + "containerUnpaused": "Container {{name}} unpaused", + "failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}", + "containerRemoved": "Container {{name}} removed", + "failedToRemoveContainer": "Failed to remove container {{name}}", + "image": "Image", + "ports": "Ports", + "noPorts": "No ports", + "start": "Start", + "confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.", + "runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.", + "loadingContainers": "Loading containers...", + "manager": "Docker Manager", + "autoRefresh": "Auto Refresh", + "timestamps": "Timestamps", + "lines": "Lines", + "filterLogs": "Filter logs...", + "refresh": "Refresh", + "download": "Download", + "clear": "Clear", + "logsDownloaded": "Logs downloaded successfully", + "last50": "Last 50", + "last100": "Last 100", + "last500": "Last 500", + "last1000": "Last 1000", + "allLogs": "All Logs", + "noLogsMatching": "No logs matching \"{{query}}\"", + "noLogsAvailable": "No logs available", + "noContainersFound": "No containers found", + "noContainersFoundHint": "No Docker containers are available on this host", + "searchPlaceholder": "Search containers...", + "allStatuses": "All Statuses", + "stateRunning": "Running", + "statePaused": "Paused", + "stateExited": "Exited", + "stateRestarting": "Restarting", + "noContainersMatchFilters": "No containers match your filters", + "noContainersMatchFiltersHint": "Try adjusting your search or filter criteria", + "failedToFetchStats": "Failed to fetch container statistics", + "containerNotRunning": "Container not running", + "startContainerToViewStats": "Start the container to view statistics", + "loadingStats": "Loading statistics...", + "errorLoadingStats": "Error loading statistics", + "noStatsAvailable": "No statistics available", + "cpuUsage": "CPU Usage", + "current": "Current", + "memoryUsage": "Memory Usage", + "networkIo": "Network I/O", + "input": "Input", + "output": "Output", + "blockIo": "Block I/O", + "read": "Read", + "write": "Write", + "pids": "PIDs", + "containerInformation": "Container Information", + "name": "Name", "id": "ID", - "state": "狀態", - "containerMustBeRunning": "容器必須正在運行才能存取控制台", - "verificationCodePrompt": "請輸入驗證碼", - "totpVerificationFailed": "TOTP驗證失敗,請重試。", - "warpgateVerificationFailed": "Warpgate身份驗證失敗,請重試。", - "connectedTo": "已連接至 {{containerName}}", - "disconnected": "斷開連接", - "consoleError": "控制台錯誤", - "errorMessage": "錯誤: {{message}}", - "failedToConnect": "連線容器失敗", - "console": "控制台", - "selectShell": "選擇 shell", + "state": "State", + "containerMustBeRunning": "Container must be running to access console", + "verificationCodePrompt": "Enter verification code", + "totpVerificationFailed": "TOTP verification failed. Please try again.", + "warpgateVerificationFailed": "Warpgate authentication failed. Please try again.", + "connectedTo": "Connected to {{containerName}}", + "disconnected": "Disconnected", + "consoleError": "Console error", + "errorMessage": "Error: {{message}}", + "failedToConnect": "Failed to connect to container", + "console": "Console", + "selectShell": "Select shell", "bash": "Bash", "sh": "sh", "ash": "ash", - "connect": "連接", - "disconnect": "斷開", - "notConnected": "未連接", - "clickToConnect": "按一下「連線」以啟動 shell 會話", - "connectingTo": "正在連接到 {{containerName}}...", - "containerNotFound": "未找到容器", - "backToList": "返回列表", - "logs": "紀錄", - "stats": "統計數據", - "consoleTab": "控制台", - "startContainerToAccess": "啟動容器以存取控制台" + "connect": "Connect", + "disconnect": "Disconnect", + "notConnected": "Not connected", + "clickToConnect": "Click connect to start a shell session", + "connectingTo": "Connecting to {{containerName}}...", + "containerNotFound": "Container not found", + "backToList": "Back to List", + "logs": "Logs", + "stats": "Stats", + "consoleTab": "Console", + "startContainerToAccess": "Start the container to access the console" }, "admin": { - "sectionGeneral": "一般的", + "sectionGeneral": "General", "sectionOidc": "OIDC", - "sectionUsers": "使用者", - "sectionSessions": "會議", - "sectionRoles": "角色", - "sectionDatabase": "資料庫", - "sectionApiKeys": "API金鑰", - "allowRegistration": "允許用戶註冊", - "allowRegistrationDesc": "允許新用戶自助註冊", - "allowPasswordLogin": "允許密碼登入", - "allowPasswordLoginDesc": "使用者名稱/密碼登入", - "oidcAutoProvision": "OIDC自動配置", - "oidcAutoProvisionDesc": "即使註冊功能已停用,也應自動為 OIDC 使用者建立帳戶。", - "allowPasswordReset": "允許重置密碼", - "allowPasswordResetDesc": "透過 Docker 日誌重置程式碼", - "sessionTimeout": "會話逾時", - "hours": "小時", - "sessionTimeoutRange": "最短 1 小時 · 最長 720 小時", - "monitoringDefaults": "監控預設設定", - "statusCheck": "狀態檢查", - "metrics": "指標", - "sec": "秒", - "logLevel": "日誌等級", - "enableGuacamole": "啟用酪梨醬", - "enableGuacamoleDesc": "RDP/VNC遠端桌面", + "sectionSso": "SSO Providers", + "ssoAddProvider": "Add Provider", + "ssoDocsLink": "View docs", + "ssoProviderDocsLink": "View docs", + "ssoNoProviders": "No SSO providers configured.", + "ssoProviderName": "Display Name", + "ssoProviderType": "Provider Type", + "ssoDeleteProvider": "Delete Provider", + "ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.", + "ssoTypeOidc": "OIDC", + "ssoTypeLdap": "LDAP", + "ssoTypeGithub": "GitHub", + "ssoTypeGoogle": "Google", + "ssoEnabled": "Enabled", + "ssoDisabled": "Disabled", + "ssoSaveProvider": "Save Provider", + "ssoTestConnection": "Test Connection", + "ssoEditProvider": "Edit Provider", + "ldapHost": "LDAP Host", + "ldapPort": "Port", + "ldapUseTls": "Use TLS (LDAPS)", + "ldapBindDn": "Bind DN", + "ldapBindPassword": "Bind Password", + "ldapUserSearchBase": "User Search Base", + "ldapUserSearchFilter": "User Search Filter", + "ldapUsernameAttr": "Username Attribute", + "ldapDisplayNameAttr": "Display Name Attribute", + "ldapGroupSearchBase": "Group Search Base", + "ldapAdminGroup": "Admin Group", + "ldapAllowedUsers": "Allowed Users", + "sectionUsers": "Users", + "sectionSessions": "Sessions", + "sectionRoles": "Roles", + "sectionDatabase": "Database", + "sectionApiKeys": "API Keys", + "sectionAuditLog": "Audit Log", + "auditLogTotal": "{{total}} total entries", + "auditLogEmpty": "No audit log entries found", + "auditLogSuccess": "Success", + "auditLogFailed": "Failed", + "auditLogClearFilters": "Clear Filters", + "auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)", + "auditLogIp": "IP", + "auditLogResourceId": "Resource ID", + "auditLogFilterUser": "User", + "auditLogFilterAction": "Action", + "auditLogFilterResourceType": "Resource Type", + "auditLogFilterStatus": "Status", + "auditLogFilterFrom": "From", + "auditLogFilterTo": "To", + "auditLogFilterAll": "All", + "allowRegistration": "Allow User Registration", + "allowRegistrationDesc": "Let new users self-register", + "allowPasswordLogin": "Allow Password Login", + "allowPasswordLoginDesc": "Username/password login", + "oidcAutoProvision": "OIDC Auto-Provision", + "oidcAutoProvisionDesc": "Auto-create accounts for OIDC users even when registration is disabled", + "allowPasswordReset": "Allow Password Reset", + "allowPasswordResetDesc": "Reset code via Docker logs", + "commandHistoryEnabled": "Command History", + "commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.", + "updateCommandHistoryFailed": "Failed to update command history setting", + "sessionTimeout": "Session Timeout", + "hours": "hours", + "sessionTimeoutRange": "Min 1h · Max 720h", + "monitoringDefaults": "Monitoring Defaults", + "statusCheck": "Status Check", + "metrics": "Metrics", + "sec": "sec", + "logLevel": "Log Level", + "enableGuacamole": "Enable Guacamole", + "enableGuacamoleDesc": "RDP/VNC remote desktop", + "enableGuacamoleDocsLink": "View docs", "guacdUrl": "guacd URL", - "oidcDescription": "配置 OpenID Connect 以實現單一登入。標示 * 的欄位為必填項。", - "oidcClientId": "客戶ID", - "oidcClientSecret": "客戶機密", - "oidcAuthUrl": "授權 URL", - "oidcIssuerUrl": "發行者 URL", - "oidcTokenUrl": "令牌 URL", - "oidcUserIdentifier": "使用者標識符路徑", - "oidcDisplayName": "顯示名稱路徑", - "oidcScopes": "瞄準鏡", - "oidcUserinfoUrl": "覆蓋使用者資訊 URL", - "oidcAllowedUsers": "允許的用戶", - "oidcAllowedUsersDesc": "每行一個電子郵件地址。留空則允許所有電子郵件地址。", - "removeOidc": "消除", - "usersCount": "{{count}} 用戶", - "createUser": "創造", - "newRole": "新角色", - "roleName": "姓名", - "roleDisplayName": "顯示名稱", - "roleDescription": "描述", - "rolesCount": "{{count}} 角色", - "createRole": "創造", - "creating": "正在創建…", - "exportDatabase": "匯出資料庫", - "exportDatabaseDesc": "下載所有主機、憑證和設定的備份", - "export": "出口", - "exporting": "出口...", - "importDatabase": "導入資料庫", - "importDatabaseDesc": "從 .sqlite 備份檔案恢復", - "importDatabaseSelected": "已選擇: {{name}}", - "selectFile": "選擇文件", - "changeFile": "改變", - "import": "進口", - "importing": "輸入...", - "apiKeysCount": "{{count}} 鍵", - "newApiKey": "新 API 金鑰", - "apiKeyCreatedWarning": "密鑰已建立 - 立即複製,之後不會再顯示。", - "apiKeyName": "姓名", - "apiKeyUser": "使用者", - "apiKeySelectUser": "選擇用戶...", - "apiKeyExpiresAt": "到期時間", - "createKey": "建立密鑰", - "apiKeyNoExpiry": "無有效期限", - "revokedBadge": "撤銷", - "authTypeDual": "雙重認證", + "tailscaleApiKey": "Tailscale API Key", + "tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.", + "tailscaleApiKeyDocsLink": "View docs", + "oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.", + "oidcDocsLink": "View docs", + "oidcClientId": "Client ID", + "oidcClientSecret": "Client Secret", + "oidcAuthUrl": "Authorization URL", + "oidcIssuerUrl": "Issuer URL", + "oidcTokenUrl": "Token URL", + "oidcUserIdentifier": "User Identifier Path", + "oidcDisplayName": "Display Name Path", + "oidcScopes": "Scopes", + "oidcUserinfoUrl": "Override Userinfo URL", + "oidcAllowedUsers": "Allowed Users", + "oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.", + "oidcAdminGroup": "Admin Group", + "oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.", + "oidcGroupClaim": "Group Claim", + "oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).", + "removeOidc": "Remove", + "usersCount": "{{count}} users", + "createUser": "Create", + "newRole": "New Role", + "roleName": "Name", + "roleDisplayName": "Display Name", + "roleDescription": "Description", + "rolesCount": "{{count}} roles", + "createRole": "Create", + "creating": "Creating...", + "exportDatabase": "Export Database", + "exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings", + "export": "Export", + "exporting": "Exporting...", + "importDatabase": "Import Database", + "importDatabaseDesc": "Restore from a .sqlite backup file", + "importDatabaseSelected": "Selected: {{name}}", + "selectFile": "Select File", + "changeFile": "Change", + "import": "Import", + "importing": "Importing...", + "apiKeysCount": "{{count}} keys", + "apiKeysDocsLink": "View docs", + "newApiKey": "New API Key", + "apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.", + "apiKeyName": "Name", + "apiKeyUser": "User", + "apiKeySelectUser": "Select a user...", + "apiKeyExpiresAt": "Expires At", + "createKey": "Create Key", + "apiKeyNoExpiry": "No expiry", + "revokedBadge": "REVOKED", + "authTypeDual": "Dual Auth", "authTypeOidc": "OIDC", - "authTypeLocal": "當地的", - "adminStatusAdministrator": "行政人員", - "adminStatusRegularUser": "普通用戶", - "adminBadge": "行政", - "systemBadge": "系統", - "customBadge": "風俗", - "youBadge": "你", - "sessionsActive": "{{count}} 活躍", - "sessionActive": "目前狀態: {{time}}", + "authTypeLocal": "Local", + "adminStatusAdministrator": "Administrator", + "adminStatusRegularUser": "Regular User", + "adminBadge": "ADMIN", + "systemBadge": "SYS", + "customBadge": "CUSTOM", + "youBadge": "YOU", + "sessionsActive": "{{count}} active", + "sessionActive": "Active: {{time}}", "sessionExpires": "Exp: {{time}}", - "revokeAll": "全部", - "revokeAllSessionsSuccess": "該使用者的所有會話均已撤銷。", - "revokeAllSessionsFailed": "撤銷會話失敗", - "revokeSessionFailed": "撤銷會話失敗", - "addRole": "添加角色", - "noCustomRoles": "未訂自訂角色", - "removeRoleFailed": "移除角色失敗", - "assignRoleFailed": "角色分配失敗", - "deleteRoleFailed": "刪除角色失敗", - "userAdminAccess": "行政人員", - "userAdminAccessDesc": "完全訪問所有管理員設置", - "userRoles": "角色", - "revokeAllUserSessions": "撤銷所有會話", - "revokeAllUserSessionsDesc": "強制所有裝置重新登入", - "revoke": "撤銷", - "deleteUserWarning": "刪除此用戶是永久性的。", - "deleteUser": "刪除 {{username}}", - "deleting": "正在刪除…", - "deleteUserFailed": "刪除用戶失敗", - "deleteUserSuccess": "使用者「{{username}}」已刪除", - "deleteRoleSuccess": "角色「{{name}}」已刪除", - "revokeKeySuccess": "金鑰「{{name}}」已撤銷", - "revokeKeyFailed": "撤銷金鑰失敗", - "copiedToClipboard": "已複製到剪貼簿", - "done": "完畢", - "createUserTitle": "創建用戶", - "createUserDesc": "建立一個新的本機帳戶。", - "createUserUsername": "使用者名稱", - "createUserPassword": "密碼", - "createUserPasswordHint": "至少6個字元。", - "createUserEnterUsername": "請輸入使用者名稱", - "createUserEnterPassword": "輸入密碼", - "createUserSubmit": "創建用戶", - "editUserTitle": "管理用戶: {{username}}", - "editUserDesc": "編輯角色、管理員狀態、會話和帳戶設定。", - "editUserUsername": "使用者名稱", - "editUserAuthType": "身份驗證類型", - "editUserAdminStatus": "管理員狀態", - "editUserUserId": "使用者身分", - "linkAccountTitle": "將 OIDC 連結到密碼帳戶", - "linkAccountDesc": "將 OIDC 帳戶 {{username}} 與現有本地帳戶合併。", - "linkAccountWarningTitle": "這將:", - "linkAccountEffect1": "刪除僅包含 OIDC 的帳戶", - "linkAccountEffect2": "將 OIDC 登入資訊新增至目標帳戶", - "linkAccountEffect3": "允許同時使用 OIDC 和密碼登入", - "linkAccountTargetUsername": "目標使用者名稱", - "linkAccountTargetPlaceholder": "輸入要關聯的本機帳戶使用者名稱", - "linkAccounts": "關聯帳戶", - "linkAccountSuccess": "OIDC 帳戶關聯到“{{username}}”", - "linkAccountFailed": "OIDC帳戶關聯失敗", - "linkAccountInProgress": "正在連接…", - "saving": "儲存...", - "updateRegistrationFailed": "註冊設定更新失敗", - "updatePasswordLoginFailed": "更新密碼登入設定失敗", - "updateOidcAutoProvisionFailed": "更新 OIDC 自動配置設定失敗", - "updatePasswordResetFailed": "密碼重置設定更新失敗", - "sessionTimeoutRange2": "會話逾時時間必須介於 1 小時到 720 小時之間。", - "sessionTimeoutSaved": "會話逾時已儲存", - "sessionTimeoutSaveFailed": "保存會話逾時失敗", - "monitoringIntervalInvalid": "無效的區間值", - "monitoringSaved": "已儲存的監控設定", - "monitoringSaveFailed": "儲存監控設定失敗", - "guacamoleSaved": "酪梨醬設定已儲存", - "guacamoleSaveFailed": "儲存 Guacamole 設定失敗", - "guacamoleUpdateFailed": "更新 Guacamole 設定失敗", - "logLevelUpdateFailed": "更新日誌等級失敗", - "oidcSaved": "OIDC 配置已儲存", - "oidcSaveFailed": "儲存 OIDC 配置失敗", - "oidcRemoved": "已移除 OIDC 配置", - "oidcRemoveFailed": "移除 OIDC 配置失敗", - "createUserRequired": "需要使用者名稱和密碼。", - "createUserPasswordTooShort": "密碼長度必須至少為 6 個字符", - "createUserSuccess": "使用者「{{username}}」創建", - "createUserFailed": "建立使用者失敗", - "updateAdminStatusFailed": "更新管理員狀態失敗", - "allSessionsRevoked": "所有會話均被撤銷", - "revokeSessionsFailed": "撤銷會話失敗", - "createRoleRequired": "姓名和顯示名稱是必填項。", - "createRoleSuccess": "角色“{{name}}”已創建", - "createRoleFailed": "創建角色失敗", - "apiKeyNameRequired": "密鑰名稱為必填項", - "apiKeyUserRequired": "需要使用者 ID", - "apiKeyCreatedSuccess": "API 金鑰「{{name}}」已建立", - "apiKeyCreateFailed": "建立 API 金鑰失敗", - "exportSuccess": "資料庫已成功匯出", - "exportFailed": "資料庫匯出失敗", - "importSelectFile": "請先選擇文件。", - "importCompleted": "匯入完成:已匯入 {{total}} 項,跳過 {{skipped}} 項", - "importFailed": "導入失敗: {{error}}", - "importError": "資料庫導入失敗" + "revokeAll": "All", + "revokeAllSessionsSuccess": "All sessions for user revoked", + "revokeAllSessionsFailed": "Failed to revoke sessions", + "revokeSessionFailed": "Failed to revoke session", + "addRole": "Add role", + "noCustomRoles": "No custom roles defined", + "removeRoleFailed": "Failed to remove role", + "assignRoleFailed": "Failed to assign role", + "deleteRoleFailed": "Failed to delete role", + "userAdminAccess": "Administrator", + "userAdminAccessDesc": "Full access to all admin settings", + "userRoles": "Roles", + "revokeAllUserSessions": "Revoke All Sessions", + "revokeAllUserSessionsDesc": "Force re-login on all devices", + "revoke": "Revoke", + "deleteUserWarning": "Deleting this user is permanent.", + "deleteUser": "Delete {{username}}", + "deleting": "Deleting...", + "deleteUserFailed": "Failed to delete user", + "deleteUserSuccess": "User \"{{username}}\" deleted", + "deleteRoleSuccess": "Role \"{{name}}\" deleted", + "revokeKeySuccess": "Key \"{{name}}\" revoked", + "revokeKeyFailed": "Failed to revoke key", + "copiedToClipboard": "Copied to clipboard", + "done": "Done", + "createUserTitle": "Create User", + "createUserDesc": "Create a new local account.", + "createUserUsername": "Username", + "createUserPassword": "Password", + "createUserPasswordHint": "Minimum 6 characters.", + "createUserEnterUsername": "Enter username", + "createUserEnterPassword": "Enter password", + "createUserSubmit": "Create User", + "editUserTitle": "Manage User: {{username}}", + "editUserDesc": "Edit roles, admin status, sessions, and account settings.", + "editUserUsername": "Username", + "editUserAuthType": "Auth Type", + "editUserAdminStatus": "Admin Status", + "editUserUserId": "User ID", + "linkAccountTitle": "Link Accounts", + "linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.", + "linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.", + "linkAccountWarningTitle": "This will:", + "linkAccountEffect1": "Delete the OIDC-only account", + "linkAccountEffect2": "Add OIDC login to the target account", + "linkAccountEffect3": "Allow both OIDC and password login", + "linkAccountTargetUsername": "Local Account Username", + "linkAccountTargetPlaceholder": "Enter the local account username to link to", + "linkAccountOidcUsername": "OIDC Account Username", + "linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in", + "linkAccountOidcNotFound": "No OIDC-only account found with that username", + "linkAccounts": "Link Accounts", + "linkAccountSuccess": "Accounts linked successfully", + "linkAccountFailed": "Failed to link accounts", + "linkAccountInProgress": "Linking...", + "saving": "Saving...", + "updateRegistrationFailed": "Failed to update registration setting", + "updatePasswordLoginFailed": "Failed to update password login setting", + "updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting", + "updatePasswordResetFailed": "Failed to update password reset setting", + "sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours", + "sessionTimeoutSaved": "Session timeout saved", + "sessionTimeoutSaveFailed": "Failed to save session timeout", + "monitoringIntervalInvalid": "Invalid interval values", + "monitoringSaved": "Monitoring settings saved", + "monitoringSaveFailed": "Failed to save monitoring settings", + "guacamoleSaved": "Guacamole settings saved", + "guacamoleSaveFailed": "Failed to save Guacamole settings", + "guacamoleUpdateFailed": "Failed to update Guacamole setting", + "tailscaleSettingsSaved": "Tailscale settings saved", + "tailscaleSettingsSaveFailed": "Failed to save Tailscale settings", + "logLevelUpdateFailed": "Failed to update log level", + "oidcSaved": "OIDC configuration saved", + "oidcSaveFailed": "Failed to save OIDC config", + "oidcRemoved": "OIDC configuration removed", + "oidcRemoveFailed": "Failed to remove OIDC config", + "createUserRequired": "Username and password are required", + "createUserPasswordTooShort": "Password must be at least 6 characters", + "createUserSuccess": "User \"{{username}}\" created", + "createUserFailed": "Failed to create user", + "updateAdminStatusFailed": "Failed to update admin status", + "allSessionsRevoked": "All sessions revoked", + "revokeSessionsFailed": "Failed to revoke sessions", + "createRoleRequired": "Name and display name are required", + "createRoleSuccess": "Role \"{{name}}\" created", + "createRoleFailed": "Failed to create role", + "apiKeyNameRequired": "Key name is required", + "apiKeyUserRequired": "User ID is required", + "apiKeyCreatedSuccess": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "exportSuccess": "Database exported successfully", + "exportFailed": "Database export failed", + "importSelectFile": "Please select a file first", + "importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped", + "importFailed": "Import failed: {{error}}", + "importError": "Database import failed" }, "newUi": { "sidebar": { "quickConnect": { - "hostLabel": "主持人", - "hostPlaceholder": "192.168.1.1 或 example.com", - "portLabel": "港口", + "hostLabel": "Host", + "hostPlaceholder": "192.168.1.1 or example.com", + "portLabel": "Port", "portPlaceholder": "22", - "usernameLabel": "使用者名稱", - "usernamePlaceholder": "使用者名稱", - "authLabel": "身份驗證", - "passwordLabel": "密碼", - "passwordPlaceholder": "密碼", - "privateKeyLabel": "私鑰", - "privateKeyPlaceholder": "貼上私鑰…", - "credentialLabel": "憑證", - "credentialPlaceholder": "選擇已儲存的憑證", - "connectToTerminal": "連接到終端", - "connectToFiles": "連接到文件" + "usernameLabel": "Username", + "usernamePlaceholder": "username", + "authLabel": "Auth", + "passwordLabel": "Password", + "passwordPlaceholder": "password", + "privateKeyLabel": "Private Key", + "privateKeyPlaceholder": "Paste private key...", + "credentialLabel": "Credential", + "credentialPlaceholder": "Select a saved credential", + "connectToTerminal": "Connect to Terminal", + "connectToFiles": "Connect to Files" }, "history": { - "noTerminalSelected": "未選擇終端", - "noTerminalSelectedHint": "開啟 SSH 終端標籤頁以查看其命令歷史記錄", - "searchPlaceholder": "搜尋紀錄…", - "clearAll": "全部清除", - "noHistoryEntries": "無歷史記錄", - "trackingDisabled": "歷史記錄追蹤已停用", - "trackingDisabledHint": "在個人資料設定中啟用此功能即可錄製指令。" + "noTerminalSelected": "No terminal selected", + "noTerminalSelectedHint": "Open an SSH terminal tab to view its command history", + "searchPlaceholder": "Search history...", + "clearAll": "Clear All", + "noHistoryEntries": "No history entries", + "trackingDisabled": "History tracking is disabled", + "trackingDisabledHint": "Enable it in the host's terminal settings." }, "sshTools": { - "keyRecordingTitle": "關鍵錄音", - "recordToTerminals": "記錄到終端", - "selectAll": "全部", - "selectNone": "沒有任何", - "noTerminalTabsOpen": "沒有打開任何終端標籤頁。", - "selectTerminalsAbove": "選擇上方終端", - "broadcastInputPlaceholder": "在此輸入以廣播按鍵操作…", - "stopRecording": "停止錄製", - "startRecording": "開始錄製", - "settingsTitle": "設定", - "enableRightClickCopyPaste": "啟用右鍵複製/貼上" + "keyRecordingTitle": "Key Recording", + "recordToTerminals": "Record to terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "selectTerminalsAbove": "Select terminals above", + "broadcastInputPlaceholder": "Type here to broadcast keystrokes...", + "stopRecording": "Stop Recording", + "startRecording": "Start Recording", + "settingsTitle": "Settings", + "enableRightClickCopyPaste": "Enable right-click copy/paste" }, "splitScreen": { - "layoutTitle": "佈局", - "selectLayoutAbove": "請選擇上方佈局。", - "selectLayoutHint": "選擇要顯示的窗格數量", - "panesTitle": "窗格", - "openTabsTitle": "開啟標籤頁", - "dragTabsHint": "將標籤拖曳到上方窗格,或使用快速指派功能。", - "dropHere": "落在這裡", - "emptyPane": "空的", - "dashboard": "儀表板", - "clearSplitScreen": "清晰的分割畫面", - "quickAssign": "快速分配", - "alreadyAssigned": "窗格 {{index}}", - "splitTab": "分頁標籤", - "addToSplit": "添加到拆分", - "removeFromSplit": "從分割中移除", - "assignToPane": "指派到窗格" + "layoutTitle": "Layout", + "selectLayoutAbove": "Select a layout above", + "selectLayoutHint": "Choose how many panes to display", + "panesTitle": "Panes", + "openTabsTitle": "Open Tabs", + "dragTabsHint": "Drag tabs into panes above, or use Quick Assign", + "dropHere": "Drop here", + "emptyPane": "Empty", + "dashboard": "Dashboard", + "clearSplitScreen": "Clear Split Screen", + "quickAssign": "Quick Assign", + "alreadyAssigned": "Pane {{index}}", + "splitTab": "Split Tab", + "addToSplit": "Add to Split", + "removeFromSplit": "Remove from Split", + "assignToPane": "Assign to pane" }, "snippets": { - "createSnippetTitle": "建立程式碼片段", - "createSnippetDescription": "建立一個新的命令片段以便快速執行", - "nameLabel": "姓名", - "namePlaceholder": "例如,重啟 Nginx", - "descriptionLabel": "描述", - "descriptionPlaceholder": "選用描述", - "optional": "選修的", - "folderLabel": "資料夾", - "noFolder": "無資料夾(未分類)", - "commandLabel": "命令", - "commandPlaceholder": "例如,sudo systemctl restart nginx", - "cancel": "取消", - "createSnippetButton": "建立程式碼片段", - "createFolderTitle": "建立資料夾", - "createFolderDescription": "將你的程式碼片段整理到資料夾中", - "folderNameLabel": "資料夾名稱", - "folderNamePlaceholder": "例如:系統指令、Docker腳本", - "folderColorLabel": "資料夾顏色", - "folderIconLabel": "資料夾圖示", - "previewLabel": "預覽", - "folderNameFallback": "資料夾名稱", - "createFolderButton": "建立資料夾", - "targetTerminals": "目標終端", - "selectAll": "全部", - "selectNone": "沒有任何", - "noTerminalTabsOpen": "沒有打開任何終端標籤頁。", - "searchPlaceholder": "搜尋摘要…", - "newSnippet": "新程式碼片段", - "newFolder": "新建資料夾", - "run": "跑步", - "noSnippetsInFolder": "此資料夾中沒有程式碼片段", - "uncategorized": "未分類", - "editSnippetTitle": "編輯片段", - "editSnippetDescription": "更新此指令片段", - "saveSnippetButton": "儲存變更", - "createSuccess": "程式碼片段創建成功", - "createFailed": "建立程式碼片段失敗", - "updateSuccess": "程式碼片段已成功更新", - "updateFailed": "更新程式碼片段失敗", - "deleteFailed": "刪除程式碼片段失敗", - "folderCreateSuccess": "資料夾建立成功", - "folderCreateFailed": "建立資料夾失敗", - "editFolderTitle": "編輯資料夾", - "editFolderDescription": "重新命名或更改此資料夾的外觀", - "saveFolderButton": "儲存變更", - "editFolder": "編輯資料夾", - "deleteFolder": "刪除資料夾", - "folderDeleteSuccess": "資料夾「{{name}}」已刪除", - "folderDeleteFailed": "刪除資料夾失敗", - "folderEditSuccess": "資料夾已成功更新", - "folderEditFailed": "更新資料夾失敗", - "confirmRunMessage": "運行“{{name}}”?", - "confirmRunButton": "跑步", - "runSuccess": "在 {{count}} 終端機中執行“{{name}}”", - "copySuccess": "已將「{{name}}」複製到剪貼簿", - "shareTitle": "分享片段", - "shareUser": "使用者", - "shareRole": "角色", - "selectUser": "選擇用戶...", - "selectRole": "選擇一個角色…", - "shareSuccess": "程式碼片段已成功分享", - "shareFailed": "分享片段失敗", - "revokeSuccess": "存取權限已撤銷", - "revokeFailed": "撤銷存取權限失敗", - "currentAccess": "目前存取權限", - "shareLoadError": "共享資料載入失敗", - "loading": "載入中...", - "close": "關閉", - "reorderFailed": "儲存程式碼片段順序失敗" + "title": "Snippets", + "createSnippetTitle": "Create Snippet", + "createSnippetDescription": "Create a new command snippet for quick execution", + "nameLabel": "Name", + "namePlaceholder": "e.g., Restart Nginx", + "descriptionLabel": "Description", + "descriptionPlaceholder": "Optional description", + "optional": "Optional", + "folderLabel": "Folder", + "noFolder": "No folder (Uncategorized)", + "commandLabel": "Command", + "commandPlaceholder": "e.g., sudo systemctl restart nginx", + "cancel": "Cancel", + "createSnippetButton": "Create Snippet", + "createFolderTitle": "Create Folder", + "createFolderDescription": "Organize your snippets into folders", + "folderNameLabel": "Folder Name", + "folderNamePlaceholder": "e.g., System Commands, Docker Scripts", + "folderColorLabel": "Folder Color", + "folderIconLabel": "Folder Icon", + "previewLabel": "Preview", + "folderNameFallback": "Folder Name", + "createFolderButton": "Create Folder", + "targetTerminals": "Target Terminals", + "selectAll": "All", + "selectNone": "None", + "noTerminalTabsOpen": "No terminal tabs open", + "searchPlaceholder": "Search snippets...", + "newSnippet": "New Snippet", + "newFolder": "New Folder", + "run": "Run", + "noSnippetsInFolder": "No snippets in this folder", + "uncategorized": "Uncategorized", + "editSnippetTitle": "Edit Snippet", + "editSnippetDescription": "Update this command snippet", + "saveSnippetButton": "Save Changes", + "createSuccess": "Snippet created successfully", + "createFailed": "Failed to create snippet", + "updateSuccess": "Snippet updated successfully", + "updateFailed": "Failed to update snippet", + "deleteFailed": "Failed to delete snippet", + "folderCreateSuccess": "Folder created successfully", + "folderCreateFailed": "Failed to create folder", + "editFolderTitle": "Edit Folder", + "editFolderDescription": "Rename or change the appearance of this folder", + "saveFolderButton": "Save Changes", + "editFolder": "Edit folder", + "deleteFolder": "Delete folder", + "folderDeleteSuccess": "Folder \"{{name}}\" deleted", + "folderDeleteFailed": "Failed to delete folder", + "folderEditSuccess": "Folder updated successfully", + "folderEditFailed": "Failed to update folder", + "confirmRunMessage": "Run \"{{name}}\"?", + "confirmRunButton": "Run", + "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)", + "copySuccess": "Copied \"{{name}}\" to clipboard", + "shareTitle": "Share Snippet", + "shareUser": "User", + "shareRole": "Role", + "selectUser": "Select a user...", + "selectRole": "Select a role...", + "shareSuccess": "Snippet shared successfully", + "shareFailed": "Failed to share snippet", + "revokeSuccess": "Access revoked", + "revokeFailed": "Failed to revoke access", + "currentAccess": "Current Access", + "shareLoadError": "Failed to load share data", + "loading": "Loading...", + "close": "Close", + "reorderFailed": "Failed to save snippet order" }, "userProfile": { - "sectionAccount": "帳戶", - "sectionAppearance": "外貌", - "sectionSecurity": "安全", - "sectionApiKeys": "API金鑰", - "sectionC2sTunnels": "C2S隧道", - "usernameLabel": "使用者名稱", - "roleLabel": "角色", - "roleAdministrator": "行政人員", - "authMethodLabel": "身份驗證方法", - "authMethodLocal": "當地的", - "twoFaLabel": "雙因素認證", - "twoFaOn": "在", - "twoFaOff": "離開", - "versionLabel": "版本", - "deleteAccount": "刪除帳戶", - "deleteAccountDescription": "永久刪除您的帳戶", - "deleteButton": "刪除", - "deleteAccountPermanent": "此操作不可逆轉,且無法撤銷。", - "deleteAccountWarning": "所有會話、主機、憑證和設定都將永久刪除。", - "confirmPasswordDeletePlaceholder": "請輸入密碼以確認", - "languageLabel": "語言", - "themeLabel": "主題", - "fontSizeLabel": "字體大小", - "accentColorLabel": "強調色", - "settingsTerminal": "終端", - "commandAutocomplete": "命令自動完成", - "commandAutocompleteDesc": "輸入時顯示自動完成功能", - "historyTracking": "歷史追蹤", - "historyTrackingDesc": "追蹤終端命令", - "syntaxHighlighting": "語法高亮", - "syntaxHighlightingDesc": "高亮顯示終端輸出", - "commandPalette": "命令面板", - "commandPaletteDesc": "啟用鍵盤快速鍵", - "reopenTabsOnLogin": "登入後重新開啟標籤頁", - "reopenTabsOnLoginDesc": "即使從其他裝置登入或重新整理頁面,也能恢復您開啟的標籤頁。", - "confirmTabClose": "確認關閉標籤頁", - "confirmTabCloseDesc": "關閉終端標籤頁前請先詢問。", - "settingsSidebar": "側邊欄", - "showHostTags": "節目主持人標籤", - "showHostTagsDesc": "在主機清單中顯示標籤", - "hostTrayOnClick": "點選展開主機操作", - "hostTrayOnClickDesc": "始終顯示連線按鈕;點擊展開管理選項,而不是懸停。", + "storageModeLocal": "Browser", + "storageModeCloud": "Database", + "storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.", + "resetToDefaults": "Reset to Defaults", + "resetToDefaultsSuccess": "Settings reset to defaults.", + "storageModeSwitch": "Preference Storage", + "sectionAccount": "Account", + "sectionAppearance": "Appearance", + "sectionSecurity": "Security", + "sectionApiKeys": "API Keys", + "sectionC2sTunnels": "C2S Tunnels", + "usernameLabel": "Username", + "roleLabel": "Role", + "roleAdministrator": "Administrator", + "authMethodLabel": "Auth Method", + "authMethodLocal": "Local", + "twoFaLabel": "2FA", + "twoFaOn": "On", + "twoFaOff": "Off", + "versionLabel": "Version", + "deleteAccount": "Delete Account", + "deleteAccountDescription": "Permanently delete your account", + "deleteButton": "Delete", + "deleteAccountPermanent": "This action is permanent and cannot be undone.", + "deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.", + "confirmPasswordDeletePlaceholder": "Enter your password to confirm", + "languageLabel": "Language", + "themeLabel": "Theme", + "fontSizeLabel": "Font Size", + "accentColorLabel": "Accent Color", + "settingsTerminal": "Terminal", + "commandAutocomplete": "Command Autocomplete", + "commandAutocompleteDesc": "Show autocomplete while typing", + "historyTracking": "History Tracking", + "historyTrackingDesc": "Track terminal commands", + "commandPalette": "Command Palette", + "commandPaletteDesc": "Enable keyboard shortcut", + "reopenTabsOnLogin": "Reopen Tabs on Login", + "reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device", + "confirmTabClose": "Confirm Tab Close", + "confirmTabCloseDesc": "Ask before closing terminal tabs", + "settingsSidebar": "Sidebar", + "showHostTags": "Show Host Tags", + "showHostTagsDesc": "Display tags in host list", + "hostTrayOnClick": "Click to Expand Host Actions", + "hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover", "pinAppRail": "Pin App Rail", - "pinAppRailDesc": "保持左側邊欄應用導軌始終展開,而不是滑鼠懸停時才展開。", - "settingsSnippets": "片段", - "foldersCollapsed": "資料夾已折疊", - "foldersCollapsedDesc": "預設折疊資料夾", - "confirmExecution": "確認執行", - "confirmExecutionDesc": "運行程式碼片段前請確認", - "settingsUpdates": "更新", - "disableUpdateChecks": "禁用更新檢查", - "disableUpdateChecksDesc": "停止檢查更新", - "totpAuthenticator": "TOTP 驗證器", - "totpEnabled": "已啟用雙重認證", - "totpDisabled": "增加額外的登入安全性", - "disable": "停用", - "enable": "使能夠", - "setupTotp": "設定 TOTP", - "qrCode": "QR 圖碼", - "totpInstructions": "掃描二維碼或在驗證器應用程式中輸入金鑰,然後輸入 6 位數驗證碼", + "pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover", + "settingsNavigation": "Navigation", + "navigationTabsDesc": "Choose which tabs appear in the app rail", + "settingsSnippets": "Snippets", + "foldersCollapsed": "Folders Collapsed", + "foldersCollapsedDesc": "Collapse folders by default", + "confirmExecution": "Confirm Execution", + "confirmExecutionDesc": "Confirm before running snippets", + "settingsUpdates": "Updates", + "disableUpdateChecks": "Disable Update Checks", + "disableUpdateChecksDesc": "Stop checking for updates", + "totpAuthenticator": "TOTP Authenticator", + "totpEnabled": "2FA is enabled", + "totpDisabled": "Add extra login security", + "disable": "Disable", + "enable": "Enable", + "setupTotp": "Setup TOTP", + "qrCode": "QR Code", + "totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code", "totpCodePlaceholder": "000000", - "verify": "核實", - "changePassword": "更改密碼", - "currentPasswordLabel": "目前密碼", - "currentPasswordPlaceholder": "目前密碼", - "newPasswordLabel": "新密碼", - "newPasswordPlaceholder": "新密碼", - "confirmPasswordLabel": "確認新密碼", - "confirmPasswordPlaceholder": "確認新密碼", - "updatePassword": "更新密碼", - "createApiKeyTitle": "建立 API 金鑰", - "createApiKeyDescription": "產生用於程式化存取的新 API 金鑰。", - "apiKeyNameLabel": "姓名", - "apiKeyNamePlaceholder": "例如 CI 流水線", - "expiryDateLabel": "到期日", - "optional": "選修的", - "cancel": "取消", - "createKey": "建立密鑰", - "apiKeyCount": "{{count}} 鍵", - "newKey": "新鑰匙", - "noApiKeys": "暫無API金鑰。", - "apiKeyActive": "積極的", - "apiKeyUsageHint": "請將鑰匙放在裡面。", - "apiKeyUsageHintHeader": "標題。", - "apiKeyPermissionsHint": "金鑰繼承建立使用者的權限。", - "roleUser": "使用者", - "authMethodDual": "雙重認證", + "verify": "Verify", + "changePassword": "Change Password", + "currentPasswordLabel": "Current Password", + "currentPasswordPlaceholder": "Current password", + "newPasswordLabel": "New Password", + "newPasswordPlaceholder": "New password", + "confirmPasswordLabel": "Confirm New Password", + "confirmPasswordPlaceholder": "Confirm new password", + "updatePassword": "Update Password", + "createApiKeyTitle": "Create API Key", + "createApiKeyDescription": "Generate a new API key for programmatic access.", + "apiKeyNameLabel": "Name", + "apiKeyNamePlaceholder": "e.g. CI Pipeline", + "expiryDateLabel": "Expiry Date", + "optional": "optional", + "cancel": "Cancel", + "createKey": "Create Key", + "apiKeyCount": "{{count}} keys", + "newKey": "New Key", + "noApiKeys": "No API keys yet.", + "apiKeyActive": "Active", + "apiKeyUsageHint": "Include your key in the", + "apiKeyUsageHintHeader": "header.", + "apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.", + "roleUser": "User", + "authMethodDual": "Dual Auth", "authMethodOidc": "OIDC", - "totpSetupFailed": "TOTP 設定啟動失敗", - "totpEnter6Digits": "請輸入6位代碼", - "totpEnabledSuccess": "已啟用雙重認證", - "totpInvalidCode": "驗證碼無效,請重試。", - "totpDisableInputRequired": "請輸入您的 TOTP 驗證碼或密碼", - "totpDisabledSuccess": "雙重認證已停用", - "totpDisableFailed": "禁用雙重驗證失敗", - "totpDisableTitle": "禁用雙重認證", - "totpDisablePlaceholder": "輸入 TOTP 驗證碼或密碼", - "totpDisableConfirm": "禁用雙重認證", - "totpContinueVerify": "繼續驗證", - "totpVerifyTitle": "驗證碼", - "totpBackupTitle": "備用代碼", - "totpDownloadBackup": "下載備份代碼", - "done": "完畢", - "secretCopied": "秘密已複製到剪貼簿", - "apiKeyNameRequired": "密鑰名稱為必填項", - "apiKeyCreated": "API 金鑰「{{name}}」已建立", - "apiKeyCreateFailed": "建立 API 金鑰失敗", - "apiKeyUser": "使用者", - "apiKeyExpires": "過期", - "apiKeyRevoked": "API金鑰「{{name}}」已撤銷", - "apiKeyRevokeFailed": "撤銷 API 金鑰失敗", - "passwordFieldsRequired": "需要當前密碼和新密碼。", - "passwordMismatch": "密碼不匹配", - "passwordTooShort": "密碼長度必須至少為 6 個字符", - "passwordUpdated": "密碼已成功更新", - "passwordUpdateFailed": "密碼更新失敗", - "deletePasswordRequired": "刪除帳戶需要密碼。", - "deleteFailed": "刪除帳戶失敗", - "deleting": "正在刪除…", - "colorPickerTooltip": "打開顏色選擇器", - "themeSystem": "系統", - "themeLight": "光", - "themeDark": "黑暗的", - "themeDracula": "德古拉", - "themeCatppuccin": "貓布奇寧", - "themeNord": "諾德", - "themeSolarized": "太陽能化", - "themeTokyoNight": "東京之夜", - "themeOneDark": "黑暗", + "totpSetupFailed": "Failed to start TOTP setup", + "totpEnter6Digits": "Enter a 6-digit code", + "totpEnabledSuccess": "Two-factor authentication enabled", + "totpInvalidCode": "Invalid code, please try again", + "totpDisableInputRequired": "Enter your TOTP code or password", + "totpDisabledSuccess": "Two-factor authentication disabled", + "totpDisableFailed": "Failed to disable 2FA", + "totpDisableTitle": "Disable 2FA", + "totpDisablePlaceholder": "Enter TOTP code or password", + "totpDisableConfirm": "Disable 2FA", + "totpContinueVerify": "Continue to Verify", + "totpVerifyTitle": "Verify Code", + "totpBackupTitle": "Backup Codes", + "totpDownloadBackup": "Download Backup Codes", + "done": "Done", + "secretCopied": "Secret copied to clipboard", + "apiKeyNameRequired": "Key name is required", + "apiKeyCreated": "API key \"{{name}}\" created", + "apiKeyCreateFailed": "Failed to create API key", + "apiKeyUser": "User", + "apiKeyExpires": "Expires", + "apiKeyRevoked": "API key \"{{name}}\" revoked", + "apiKeyRevokeFailed": "Failed to revoke API key", + "passwordFieldsRequired": "Current and new passwords are required", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "passwordUpdated": "Password updated successfully", + "passwordUpdateFailed": "Failed to update password", + "deletePasswordRequired": "Password is required to delete your account", + "deleteFailed": "Failed to delete account", + "deleting": "Deleting...", + "colorPickerTooltip": "Open color picker", + "themeSystem": "System", + "themeLight": "Light", + "themeDark": "Dark", + "themeDracula": "Dracula", + "themeCatppuccin": "Catppuccin", + "themeNord": "Nord", + "themeSolarized": "Solarized", + "themeTokyoNight": "Tokyo Night", + "themeOneDark": "One Dark", "themeGruvbox": "Gruvbox" } } + }, + "tmuxMonitor": { + "title": "Tmux Monitor", + "failedToLoadHosts": "Failed to load hosts", + "failedToLoad": "Failed to load tmux sessions", + "tmuxUnavailable": "tmux is not installed on this host", + "noSessions": "No tmux sessions on this host", + "noHostSelected": "No host selected", + "attached": "Attached", + "detached": "Detached", + "attach": "Attach", + "editTags": "Edit tags", + "tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)", + "tagsSaved": "Tags saved", + "tagsSaveFailed": "Failed to save tags", + "searchPlaceholder": "Search output across sessions...", + "searchResults": "{{count}} matches", + "searchFailed": "Search failed", + "selectPaneHint": "Select a pane to preview its output", + "closePreview": "Close preview", + "newSession": "New session", + "newSessionHint": "Session name (letters, digits, _ @ % + = -)", + "newSessionPlaceholder": "my-session", + "create": "Create", + "sessionCreated": "Session \"{{name}}\" created", + "sessionCreateFailed": "Failed to create session", + "splitRight": "Split right", + "splitDown": "Split down", + "splitFailed": "Failed to split pane", + "newWindow": "New window", + "windowCreateFailed": "Failed to create window", + "attachSessionTooltip": "Attach to {{session}}", + "refresh": "Refresh", + "refreshFailed": "Failed to refresh sessions", + "collapseAll": "Collapse all", + "expandAll": "Expand all", + "moreActions": "More actions", + "sessionStats": "Session stats", + "renameSessionTitle": "Rename session \"{{name}}\"", + "rename": "Rename", + "sessionRenamed": "Session renamed to \"{{name}}\"", + "sessionRenameFailed": "Failed to rename session", + "editTagsTitle": "Edit tags for \"{{name}}\"", + "killPane": "Kill pane", + "resizeTree": "Drag to resize — double-click to reset", + "reattach": "Re-attach (fixes a garbled view)", + "killWindow": "Kill window", + "killWindowTitle": "Kill window {{index}} of \"{{session}}\"?", + "killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.", + "windowKillFailed": "Failed to kill window", + "statusActivity": "Activity", + "statusWindows": "Windows", + "statusPanes": "panes", + "statusTags": "Tags", + "killPaneTitle": "Kill pane {{id}}?", + "killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.", + "paneKillFailed": "Failed to kill pane", + "killSessionTitle": "Kill session \"{{name}}\"?", + "killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.", + "kill": "Kill", + "sessionKilled": "Session \"{{name}}\" killed", + "sessionKillFailed": "Failed to kill session", + "hostUnreachable": "Could not connect to the host. Check that it is online and reachable.", + "noServer": "No tmux server is running on this host.", + "searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", + "closeSearchResults": "Close search results", + "retry": "Retry", + "noHosts": "No SSH hosts available", + "noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.", + "tmuxInstallHint": "Install it on the host with:", + "attachTooltip": "Open a terminal to {{host}}", + "attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}", + "timeJustNow": "just now", + "timeMinutes": "{{count}}m ago", + "timeHours": "{{count}}h ago", + "timeDays": "{{count}}d ago" } } diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 7d9c0673..6b531fa5 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -85,10 +85,91 @@ interface DiskMetrics { availableHuman?: string | null; } +export interface NetworkInterface { + name: string; + ip: string; + state: string; + rx?: string | null; + tx?: string | null; + rxBytes?: string | null; + txBytes?: string | null; +} + +export interface ProcessInfo { + pid: string; + user: string; + cpu: string; + mem: string; + command: string; +} + +export interface LoginRecord { + user: string; + ip: string; + time: string; + status: "success" | "failed"; +} + +export interface ListeningPort { + protocol: "tcp" | "udp"; + localAddress: string; + localPort: number; + state?: string; + pid?: number; + process?: string; +} + +export interface FirewallRule { + chain: string; + target: string; + protocol: string; + source: string; + destination: string; + dport?: string; + sport?: string; + state?: string; + interface?: string; + extra?: string; +} + +export interface FirewallChain { + name: string; + policy: string; + rules: FirewallRule[]; +} + export type ServerMetrics = { cpu: CpuMetrics; memory: MemoryMetrics; disk: DiskMetrics; + network?: { interfaces?: NetworkInterface[] }; + uptime?: { seconds?: number | null; formatted?: string | null }; + system?: { + hostname?: string | null; + os?: string | null; + kernel?: string | null; + arch?: string | null; + }; + processes?: { + total?: number | null; + running?: number | null; + top?: ProcessInfo[]; + }; + login_stats?: { + recentLogins?: LoginRecord[]; + failedLogins?: LoginRecord[]; + totalLogins?: number; + uniqueIPs?: number; + }; + ports?: { + source?: "ss" | "netstat" | "none"; + ports?: ListeningPort[]; + }; + firewall?: { + type?: "iptables" | "nftables" | "none"; + status?: "active" | "inactive" | "unknown"; + chains?: FirewallChain[]; + }; lastChecked: string; }; @@ -780,6 +861,12 @@ function initializeApiInstances() { // Docker Management API (port 30007) dockerApi = createApiInstance(getApiUrl("/docker", 30007), "DOCKER"); + + // Tmux Monitor API (port 30010) --- tmux-monitor --- + tmuxMonitorApi = createApiInstance( + getApiUrl("/tmux_monitor", 30010), + "TMUX_MONITOR", + ); } // Host Management API (port 30001) - supports SSH, RDP, VNC, Telnet @@ -808,6 +895,9 @@ export let rbacApi: AxiosInstance; // Docker Management API (port 30007) export let dockerApi: AxiosInstance; +// Tmux Monitor API (port 30010) --- tmux-monitor --- +export let tmuxMonitorApi: AxiosInstance; + // Pre-initialize with default values to avoid undefined errors during early mounting initializeApiInstances(); @@ -1411,6 +1501,7 @@ export { updateSSHHost, wakeOnLan, bulkImportSSHHosts, + discoverProxmoxGuests, bulkUpdateSSHHosts, deleteSSHHost, getSSHHostById, @@ -1504,7 +1595,17 @@ export { submitMetricsTOTP, refreshServerPolling, notifyHostCreatedOrUpdated, -} from "@/api/server-stats-api"; +} from "@/api/host-metrics-status-api"; + +export { + getHostMetricsLayout, + saveHostMetricsLayout, + getHostPlatform, + managerGet, + managerGetSub, + managerPost, + type PlatformInfo, +} from "@/api/host-metrics-api"; export { getGlobalMonitoringSettings, @@ -1784,10 +1885,12 @@ export async function changePassword(oldPassword: string, newPassword: string) { export async function getOIDCAuthorizeUrl( rememberMe = false, desktopCallbackPort?: number, + providerId?: number, + appCallbackUrl?: string, ): Promise { try { const response = await authApi.get("/users/oidc/authorize", { - params: { rememberMe, desktopCallbackPort }, + params: { rememberMe, desktopCallbackPort, providerId, appCallbackUrl }, }); return response.data; } catch (error) { @@ -1816,6 +1919,8 @@ export { updatePasswordResetAllowed, updateOIDCConfig, disableOIDCConfig, + getCommandHistoryEnabled, + updateCommandHistoryEnabled, type ApiKey, type CreatedApiKey, } from "@/api/user-management-api"; @@ -1967,12 +2072,6 @@ export { getContainerStats, } from "@/api/docker-api"; -export { - getDashboardPreferences, - saveDashboardPreferences, - type DashboardLayout, -} from "@/api/dashboard-preferences-api"; - export { getOpenTabs, syncOpenTabs, diff --git a/src/ui/shell/CommandPalette.tsx b/src/ui/shell/CommandPalette.tsx index d4212067..801f2c75 100644 --- a/src/ui/shell/CommandPalette.tsx +++ b/src/ui/shell/CommandPalette.tsx @@ -25,6 +25,7 @@ import { Network, User, KeyRound, + Layers, // --- tmux-monitor --- Monitor, MousePointerClick, Clock, @@ -55,7 +56,7 @@ const ACTIVITY_ICONS: Record = { const ACTIVITY_TAB_TYPE: Record = { terminal: "terminal", file_manager: "files", - server_stats: "stats", + server_stats: "host-metrics", tunnel: "tunnel", docker: "docker", telnet: "telnet", @@ -81,8 +82,19 @@ function getSshActions(host: Host): { label: "Files", }, host.enableDocker && { type: "docker", icon: Box, label: "Docker" }, + // --- tmux-monitor --- opt-in per host, off by default + host.enableTerminal !== false && + host.enableTmuxMonitor && { + type: "tmux_monitor", + icon: Layers, + label: "Tmux Monitor", + }, host.enableTunnel && { type: "tunnel", icon: Network, label: "Tunnels" }, - metricsEnabled && { type: "stats", icon: Activity, label: "Stats" }, + metricsEnabled && { + type: "host-metrics", + icon: Activity, + label: "Host Metrics", + }, ].filter(Boolean) as { type: TabType; icon: React.ElementType; @@ -249,6 +261,24 @@ export function CommandPalette({
+ {/* --- tmux-monitor --- */} + handleAction(() => onOpenTab("tmux_monitor"))} + className="group flex items-center gap-3 px-3 py-2.5 rounded-none hover:bg-accent-brand/10 cursor-pointer" + > +
+ +
+
+ + {t("commandPalette.tmuxMonitor")} + + + {t("commandPalette.tmuxMonitorDesc")} + +
+
+ handleAction(() => diff --git a/src/ui/shell/Tab.tsx b/src/ui/shell/Tab.tsx index ada835d9..c340a224 100644 --- a/src/ui/shell/Tab.tsx +++ b/src/ui/shell/Tab.tsx @@ -3,6 +3,7 @@ import { Button } from "@/components/button.tsx"; import { useTranslation } from "react-i18next"; import { getHostPassword } from "@/main-axios.ts"; import { cn } from "@/lib/utils"; +import { copyToClipboard } from "@/lib/clipboard"; import { Home, SeparatorVertical, @@ -18,6 +19,7 @@ import { Network, ArrowDownUp as TunnelIcon, Container as DockerIcon, + Layers as TmuxMonitorIcon, // --- tmux-monitor --- Key, } from "lucide-react"; import { toast } from "sonner"; @@ -86,24 +88,9 @@ export function Tab({ return; } - try { - await navigator.clipboard.writeText(passwordToCopy); - toast.success(t("nav.passwordCopied")); - } catch { - try { - const textarea = document.createElement("textarea"); - textarea.value = passwordToCopy; - textarea.style.position = "fixed"; - textarea.style.opacity = "0"; - document.body.appendChild(textarea); - textarea.select(); - document.execCommand("copy"); - document.body.removeChild(textarea); - toast.success(t("nav.passwordCopied")); - } catch { - toast.error(t("nav.failedToCopyPassword")); - } - } + const ok = await copyToClipboard(passwordToCopy); + if (ok) toast.success(t("nav.passwordCopied")); + else toast.error(t("nav.failedToCopyPassword")); }; const hasPassword = @@ -202,28 +189,34 @@ export function Tab({ tabType === "telnet" || tabType === "tunnel" || tabType === "docker" || + tabType === "tmux_monitor" || // --- tmux-monitor --- tabType === "user_profile" ) { const isServer = tabType === "server_stats"; const isFileManager = tabType === "file_manager"; const isTunnel = tabType === "tunnel"; const isDocker = tabType === "docker"; + const isTmuxMonitor = tabType === "tmux_monitor"; // --- tmux-monitor --- const isUserProfile = tabType === "user_profile"; const displayTitle = title || (isServer - ? t("nav.serverStats") + ? t("nav.hostMetrics") : isFileManager ? t("nav.fileManager") : isTunnel ? t("nav.tunnels") : isDocker ? t("nav.docker") - : isUserProfile - ? t("nav.userProfile") - : tabType === "rdp" || tabType === "vnc" || tabType === "telnet" - ? tabType.toUpperCase() - : t("nav.terminal")); + : isTmuxMonitor // --- tmux-monitor --- + ? t("nav.tmuxMonitor") + : isUserProfile + ? t("nav.userProfile") + : tabType === "rdp" || + tabType === "vnc" || + tabType === "telnet" + ? tabType.toUpperCase() + : t("nav.terminal")); const { base, suffix } = splitTitle(displayTitle); @@ -246,6 +239,8 @@ export function Tab({ ) : isDocker ? ( + ) : isTmuxMonitor ? ( // --- tmux-monitor --- + ) : isUserProfile ? ( ) : tabType === "rdp" ? ( diff --git a/src/ui/shell/TabContext.tsx b/src/ui/shell/TabContext.tsx index b19a7a41..b60a532f 100644 --- a/src/ui/shell/TabContext.tsx +++ b/src/ui/shell/TabContext.tsx @@ -112,7 +112,7 @@ export function TabProvider({ children }: TabProviderProps) { (tabType: Tab["type"], desiredTitle: string | undefined): string => { const defaultTitle = tabType === "server_stats" - ? t("nav.serverStats") + ? t("nav.hostMetrics") : tabType === "file_manager" ? t("nav.fileManager") : tabType === "tunnel" @@ -153,8 +153,10 @@ export function TabProvider({ children }: TabProviderProps) { const addTab = useCallback( (tabData: Omit): number => { - if (tabData.type === "ssh_manager") { - const existingTab = tabs.find((t) => t.type === "ssh_manager"); + // --- tmux-monitor --- (tmux_monitor is a singleton tab like ssh_manager: + // re-opening focuses the existing tab instead of adding a duplicate) + if (tabData.type === "ssh_manager" || tabData.type === "tmux_monitor") { + const existingTab = tabs.find((t) => t.type === tabData.type); if (existingTab) { setTabs((prev) => prev.map((t) => @@ -189,7 +191,9 @@ export function TabProvider({ children }: TabProviderProps) { tabData.type === "docker"; const effectiveTitle = needsUniqueTitle ? computeUniqueTitle(tabData.type, tabData.title) - : tabData.title || ""; + : tabData.type === "tmux_monitor" // --- tmux-monitor --- + ? tabData.title || t("nav.tmuxMonitor") + : tabData.title || ""; const newTab: Tab = { ...tabData, id, @@ -211,7 +215,7 @@ export function TabProvider({ children }: TabProviderProps) { setAllSplitScreenTab((prev) => prev.filter((tid) => tid !== id)); return id; }, - [computeUniqueTitle, tabs], + [computeUniqueTitle, tabs, t], // --- tmux-monitor --- (added t) ); const pendingCurrentTabRef = useRef(null); diff --git a/src/ui/shell/tabUtils.tsx b/src/ui/shell/tabUtils.tsx index 31a395c8..8ac14fb5 100644 --- a/src/ui/shell/tabUtils.tsx +++ b/src/ui/shell/tabUtils.tsx @@ -11,6 +11,7 @@ import { User, Activity, TerminalSquare, + Layers, // --- tmux-monitor --- } from "lucide-react"; import { useTranslation } from "react-i18next"; import { CommandHistoryProvider } from "@/features/terminal/command-history/CommandHistoryContext"; @@ -21,7 +22,9 @@ import type { } from "@/features/terminal/Terminal"; import { FileManager } from "@/features/file-manager/FileManager"; import { DockerManager } from "@/features/docker/DockerManager"; -import { ServerStats } from "@/features/server-stats/ServerStats"; +import { HostMetricsTab } from "@/features/host-metrics/HostMetricsTab"; +// --- tmux-monitor --- +import { TmuxMonitor } from "@/features/tmux-monitor/TmuxMonitor"; import GuacamoleApp from "@/features/guacamole/GuacamoleApp"; import { DashboardTab } from "@/dashboard/DashboardTab"; import { TunnelTab } from "@/features/tunnel/TunnelTab"; @@ -96,7 +99,7 @@ export function tabIcon(type: TabType) { return ; case "telnet": return ; - case "stats": + case "host-metrics": return ; case "files": return ; @@ -112,6 +115,9 @@ export function tabIcon(type: TabType) { return ; case "network_graph": return ; + // --- tmux-monitor --- + case "tmux_monitor": + return ; } } @@ -211,13 +217,13 @@ export function renderTabContent( /> ); - case "stats": + case "host-metrics": if (!host) return ( - + ); return ( - ; + // --- tmux-monitor --- + case "tmux_monitor": + return ( + + ); + case "host-manager": case "user-profile": case "admin-settings": diff --git a/src/ui/sidebar/AdminApiKeysSection.tsx b/src/ui/sidebar/AdminApiKeysSection.tsx index 12c1d9e8..c50451f4 100644 --- a/src/ui/sidebar/AdminApiKeysSection.tsx +++ b/src/ui/sidebar/AdminApiKeysSection.tsx @@ -1,5 +1,6 @@ import type { Dispatch, SetStateAction } from "react"; import { useTranslation } from "react-i18next"; +import { copyToClipboard } from "@/lib/clipboard"; import { deleteApiKey } from "@/main-axios"; import type { ApiKey } from "@/main-axios"; import { Button } from "@/components/button"; @@ -62,7 +63,15 @@ export function AdminApiKeysSection({
- {t("admin.apiKeysCount", { count: apiKeys.length })} + {t("admin.apiKeysCount", { count: apiKeys.length })}{" "} + + {t("admin.apiKeysDocsLink")} +
+ )} + +
+
+ + {/* Log table */} + {logs.length === 0 && !loading ? ( +
+ {t("admin.auditLogEmpty")} +
+ ) : ( +
+ {logs.map((log) => ( +
+ setExpandedId(expandedId === log.id ? null : log.id) + } + > +
+ +
+
+ + {log.action.replace(/_/g, " ")} + + + {log.resourceType} + + {log.resourceName && ( + + {log.resourceName} + + )} +
+
+ + {log.username} + + + {formatTimestamp(log.timestamp)} + +
+
+ + {log.success + ? t("admin.auditLogSuccess") + : t("admin.auditLogFailed")} + +
+ + {expandedId === log.id && ( +
+ {log.ipAddress && ( + + {t("admin.auditLogIp")}: {log.ipAddress} + + )} + {log.resourceId && ( + + {t("admin.auditLogResourceId")}: {log.resourceId} + + )} + {log.details && ( + + {log.details} + + )} + {log.errorMessage && ( + + {log.errorMessage} + + )} +
+ )} +
+ ))} +
+ )} + + {/* Pagination */} + {totalPages > 1 && ( +
+ + + {t("admin.auditLogPage", { page, totalPages, total })} + + +
+ )} +
+ + ); +} diff --git a/src/ui/sidebar/AdminManagementSections.tsx b/src/ui/sidebar/AdminManagementSections.tsx index 93ff29b2..3c862b85 100644 --- a/src/ui/sidebar/AdminManagementSections.tsx +++ b/src/ui/sidebar/AdminManagementSections.tsx @@ -65,7 +65,7 @@ type UsersSectionProps = { setEditUserTarget: Dispatch>; setEditUserOpen: Dispatch>; setLinkAccountTarget: Dispatch< - SetStateAction<{ id: string; username: string } | null> + SetStateAction<{ id: string; username: string; isOidc: boolean } | null> >; setLinkAccountOpen: Dispatch>; }; @@ -160,7 +160,7 @@ export function AdminUsersSection({ > - {user.isOidc && ( + {!(user.isOidc && user.passwordHash) && (
); diff --git a/src/ui/sidebar/AdminSettingsSections.tsx b/src/ui/sidebar/AdminSettingsSections.tsx index e5460bf8..cae55cbe 100644 --- a/src/ui/sidebar/AdminSettingsSections.tsx +++ b/src/ui/sidebar/AdminSettingsSections.tsx @@ -3,8 +3,17 @@ import { useTranslation } from "react-i18next"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { SettingRow } from "@/components/section-card"; -import { Database, RefreshCw, Settings, Shield, Trash2 } from "lucide-react"; +import { + Database, + Pencil, + Plus, + RefreshCw, + Settings, + Shield, + Trash2, +} from "lucide-react"; import { AccordionSection, AdminToggle } from "./AdminSettingsShared"; +import type { SSOProvider, SSOProviderType } from "@/types/index"; type GeneralSettingsSectionProps = { open: boolean; @@ -17,6 +26,8 @@ type GeneralSettingsSectionProps = { handleToggleOidcAutoProvision: () => void; allowPasswordReset: boolean; handleTogglePasswordReset: () => void; + commandHistoryEnabled: boolean; + handleToggleCommandHistory: () => void; sessionTimeout: string; setSessionTimeout: Dispatch>; handleSaveSessionTimeout: () => void; @@ -32,6 +43,9 @@ type GeneralSettingsSectionProps = { handleSaveGuacamole: () => void; logLevel: string; handleSaveLogLevel: (level: string) => void; + tailscaleApiKey: string; + setTailscaleApiKey: Dispatch>; + handleSaveTailscaleApiKey: () => void; }; export function AdminGeneralSettingsSection({ @@ -45,6 +59,8 @@ export function AdminGeneralSettingsSection({ handleToggleOidcAutoProvision, allowPasswordReset, handleTogglePasswordReset, + commandHistoryEnabled, + handleToggleCommandHistory, sessionTimeout, setSessionTimeout, handleSaveSessionTimeout, @@ -60,6 +76,9 @@ export function AdminGeneralSettingsSection({ handleSaveGuacamole, logLevel, handleSaveLogLevel, + tailscaleApiKey, + setTailscaleApiKey, + handleSaveTailscaleApiKey, }: GeneralSettingsSectionProps) { const { t } = useTranslation(); @@ -107,8 +126,17 @@ export function AdminGeneralSettingsSection({ onToggle={handleTogglePasswordReset} /> + + + -
+
{t("admin.sessionTimeout")} @@ -187,7 +215,19 @@ export function AdminGeneralSettingsSection({
+ {t("admin.enableGuacamoleDesc")}{" "} + + {t("admin.enableGuacamoleDocsLink")} + + + } > @@ -216,6 +256,42 @@ export function AdminGeneralSettingsSection({ )}
+
+
+ + {t("admin.tailscaleApiKey")} + + + {t("admin.tailscaleApiKeyDescription")}{" "} + + {t("admin.tailscaleApiKeyDocsLink")} + + +
+
+ setTailscaleApiKey(e.target.value)} + placeholder="tskey-api-..." + className="text-sm" + /> + +
+
+
{t("admin.logLevel")} @@ -260,6 +336,10 @@ type OidcSettingsSectionProps = { setOidcUserinfoUrl: Dispatch>; oidcAllowedUsers: string; setOidcAllowedUsers: Dispatch>; + oidcAdminGroup: string; + setOidcAdminGroup: Dispatch>; + oidcGroupClaim: string; + setOidcGroupClaim: Dispatch>; oidcSaving: boolean; handleRemoveOidc: () => void; handleSaveOidc: () => void; @@ -288,6 +368,10 @@ export function AdminOidcSettingsSection({ setOidcUserinfoUrl, oidcAllowedUsers, setOidcAllowedUsers, + oidcAdminGroup, + setOidcAdminGroup, + oidcGroupClaim, + setOidcGroupClaim, oidcSaving, handleRemoveOidc, handleSaveOidc, @@ -305,7 +389,15 @@ export function AdminOidcSettingsSection({ {t("admin.oidcDescription").split("*")[0]} * - {t("admin.oidcDescription").split("*")[1]} + {t("admin.oidcDescription").split("*")[1]}{" "} + + {t("admin.oidcDocsLink")} +
+
+ + + {t("admin.oidcAdminGroupDesc")} + + setOidcAdminGroup(e.target.value)} + placeholder="admin" + className="w-full px-2 py-1.5 text-xs bg-background border border-border text-foreground placeholder:text-muted-foreground outline-none focus:ring-1 focus:ring-ring font-mono" + /> +
+
+ + + {t("admin.oidcGroupClaimDesc")} + + setOidcGroupClaim(e.target.value)} + placeholder="groups" + className="w-full px-2 py-1.5 text-xs bg-background border border-border text-foreground placeholder:text-muted-foreground outline-none focus:ring-1 focus:ring-ring font-mono" + /> +
+ +
+ ))} +
+ )} + +
+ + ); +} diff --git a/src/ui/sidebar/AdminUserDialogs.tsx b/src/ui/sidebar/AdminUserDialogs.tsx index 6c30753f..765fdcab 100644 --- a/src/ui/sidebar/AdminUserDialogs.tsx +++ b/src/ui/sidebar/AdminUserDialogs.tsx @@ -394,8 +394,9 @@ export function AdminEditUserDialog({ type LinkAccountDialogProps = { open: boolean; onOpenChange: Dispatch>; - linkAccountTarget: { id: string; username: string } | null; + linkAccountTarget: { id: string; username: string; isOidc: boolean } | null; setUsers: Dispatch>; + users: AdminUser[]; }; export function AdminLinkAccountDialog({ @@ -403,27 +404,43 @@ export function AdminLinkAccountDialog({ onOpenChange, linkAccountTarget, setUsers, + users, }: LinkAccountDialogProps) { const { t } = useTranslation(); - const [targetUsername, setTargetUsername] = useState(""); + const [otherUsername, setOtherUsername] = useState(""); const [submitting, setSubmitting] = useState(false); useEffect(() => { - if (open) setTargetUsername(""); + if (open) setOtherUsername(""); }, [open, linkAccountTarget]); + const isOidcInitiator = linkAccountTarget?.isOidc ?? true; + const handleSubmit = async () => { - const trimmedUsername = targetUsername.trim(); - if (!linkAccountTarget || !trimmedUsername) return; + const trimmed = otherUsername.trim(); + if (!linkAccountTarget || !trimmed) return; setSubmitting(true); try { - await linkOIDCToPasswordAccount(linkAccountTarget.id, trimmedUsername); - toast.success( - t("admin.linkAccountSuccess", { username: trimmedUsername }), - ); - setUsers((prev) => prev.filter((u) => u.id !== linkAccountTarget.id)); - setTargetUsername(""); + if (isOidcInitiator) { + await linkOIDCToPasswordAccount(linkAccountTarget.id, trimmed); + setUsers((prev) => prev.filter((u) => u.id !== linkAccountTarget.id)); + } else { + const oidcUser = users.find( + (u) => u.username === trimmed && u.isOidc && !u.passwordHash, + ); + if (!oidcUser) { + toast.error(t("admin.linkAccountOidcNotFound")); + return; + } + await linkOIDCToPasswordAccount( + oidcUser.id, + linkAccountTarget.username, + ); + setUsers((prev) => prev.filter((u) => u.id !== oidcUser.id)); + } + toast.success(t("admin.linkAccountSuccess", { username: trimmed })); + setOtherUsername(""); onOpenChange(false); } catch (error: unknown) { toast.error(apiErrorMessage(error, t("admin.linkAccountFailed"))); @@ -440,9 +457,13 @@ export function AdminLinkAccountDialog({ {t("admin.linkAccountTitle")} - {t("admin.linkAccountDesc", { - username: linkAccountTarget?.username, - })} + {isOidcInitiator + ? t("admin.linkAccountDesc", { + username: linkAccountTarget?.username, + }) + : t("admin.linkAccountDescLocal", { + username: linkAccountTarget?.username, + })}
@@ -459,13 +480,19 @@ export function AdminLinkAccountDialog({
setTargetUsername(e.target.value)} - placeholder={t("admin.linkAccountTargetPlaceholder")} + value={otherUsername} + onChange={(e) => setOtherUsername(e.target.value)} + placeholder={ + isOidcInitiator + ? t("admin.linkAccountTargetPlaceholder") + : t("admin.linkAccountOidcPlaceholder") + } autoFocus disabled={submitting} /> @@ -482,9 +509,7 @@ export function AdminLinkAccountDialog({ + +
+ + + + + + handleSortChange("default")} + className="flex items-center gap-1.5" + > + {sortKey === "default" ? ( + + ) : ( + + )} + {t("credentials.sortDefault")} + + + {(["name-asc", "name-desc"] as const).map((key) => ( + handleSortChange(key)} + className="flex items-center gap-1.5" + > + {sortKey === key ? ( + + ) : ( + + )} + {t( + `credentials.sort${key === "name-asc" ? "NameAsc" : "NameDesc"}`, + )} + + ))} + + {(["username-asc", "username-desc"] as const).map((key) => ( + handleSortChange(key)} + className="flex items-center gap-1.5" + > + {sortKey === key ? ( + + ) : ( + + )} + {t( + `credentials.sort${key === "username-asc" ? "UsernameAsc" : "UsernameDesc"}`, + )} + + ))} + + + + + + + + {filterActive && ( + <> + + + {t("credentials.filterClearAll")} + + + + )} + + {t("credentials.filterTypeGroup")} + + {(["password", "key"] as const).map((val) => ( + handleFilterToggle("type", val)} + onSelect={(e) => e.preventDefault()} + > + {t( + `credentials.filterType${val.charAt(0).toUpperCase() + val.slice(1)}`, + )} + + ))} + {allTags.length > 0 && ( + <> + + + {t("credentials.filterTagsGroup")} + + {allTags.map((tag) => ( + handleFilterToggle("tags", tag)} + onSelect={(e) => e.preventDefault()} + > + {tag} + + ))} + + )} + + + + + + +
)} @@ -59,6 +272,9 @@ export function CredentialsPanel({ diff --git a/src/ui/sidebar/FolderMetadataDialog.tsx b/src/ui/sidebar/FolderMetadataDialog.tsx new file mode 100644 index 00000000..5fb0f5f3 --- /dev/null +++ b/src/ui/sidebar/FolderMetadataDialog.tsx @@ -0,0 +1,146 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ChevronRight } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/dialog"; +import { Button } from "@/components/button"; +import { Input } from "@/components/input"; +import { + DEFAULT_FOLDER_COLOR, + DEFAULT_FOLDER_ICON, + FolderIconEl, + HexColorPicker, + IconPicker, +} from "@/components/folder-style"; +import { normalizePath, splitPath } from "./FolderPathPicker"; + +export type FolderMetadataValue = { + name: string; + color: string; + icon: string; +}; + +export function FolderMetadataDialog({ + open, + mode, + initial, + onOpenChange, + onSubmit, +}: { + open: boolean; + mode: "create" | "edit"; + initial?: { name: string; color?: string; icon?: string }; + onOpenChange: (v: boolean) => void; + onSubmit: (value: FolderMetadataValue) => void; +}) { + const { t } = useTranslation(); + const [name, setName] = useState(""); + const [color, setColor] = useState(DEFAULT_FOLDER_COLOR); + const [icon, setIcon] = useState(DEFAULT_FOLDER_ICON); + + useEffect(() => { + if (open) { + setName(initial?.name ?? ""); + setColor(initial?.color ?? DEFAULT_FOLDER_COLOR); + setIcon(initial?.icon ?? DEFAULT_FOLDER_ICON); + } + }, [open, initial]); + + function handleSubmit() { + const normalized = normalizePath(name); + if (!normalized) return; + onSubmit({ name: normalized, color, icon }); + onOpenChange(false); + } + + return ( + + + + + {mode === "create" + ? t("hosts.createFolderTitle") + : t("hosts.editFolderTitle")} + + + {t("hosts.folderDialogDescription")} + + +
+
+ + setName(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleSubmit(); + }} + /> +

+ {t("hosts.folderNestingHint")} +

+
+
+ + +
+
+ + +
+
+ +
+ + + {name + ? splitPath(name).map((seg, i) => ( + + {i > 0 && ( + + )} + {seg} + + )) + : t("hosts.folderNameFallback")} + +
+
+
+
+ + +
+
+
+ ); +} diff --git a/src/ui/sidebar/FolderPathPicker.test.ts b/src/ui/sidebar/FolderPathPicker.test.ts new file mode 100644 index 00000000..f0c38698 --- /dev/null +++ b/src/ui/sidebar/FolderPathPicker.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; +import { normalizePath, splitPath } from "./FolderPathPicker.js"; + +describe("splitPath", () => { + it("splits on canonical separator", () => { + expect(splitPath("Homelab / Debian")).toEqual(["Homelab", "Debian"]); + }); + + it("splits on slash without spaces", () => { + expect(splitPath("Homelab/Debian")).toEqual(["Homelab", "Debian"]); + }); + + it("splits on slash with only leading space", () => { + expect(splitPath("Homelab /Debian")).toEqual(["Homelab", "Debian"]); + }); + + it("splits on slash with only trailing space", () => { + expect(splitPath("Homelab/ Debian")).toEqual(["Homelab", "Debian"]); + }); + + it("handles three levels", () => { + expect(splitPath("A/B/C")).toEqual(["A", "B", "C"]); + }); + + it("filters empty segments from leading/trailing slashes", () => { + expect(splitPath("/Debian/")).toEqual(["Debian"]); + }); + + it("returns single segment for non-slash path", () => { + expect(splitPath("Homelab")).toEqual(["Homelab"]); + }); +}); + +describe("normalizePath", () => { + it("normalizes slash-without-spaces to canonical form", () => { + expect(normalizePath("Homelab/Debian")).toBe("Homelab / Debian"); + }); + + it("is idempotent on already-canonical input", () => { + expect(normalizePath("Homelab / Debian")).toBe("Homelab / Debian"); + }); + + it("normalizes mixed spacing around slash", () => { + expect(normalizePath("Homelab /Debian/ Servers")).toBe( + "Homelab / Debian / Servers", + ); + }); + + it("returns empty string for empty input", () => { + expect(normalizePath("")).toBe(""); + }); +}); diff --git a/src/ui/sidebar/FolderPathPicker.tsx b/src/ui/sidebar/FolderPathPicker.tsx new file mode 100644 index 00000000..e021f313 --- /dev/null +++ b/src/ui/sidebar/FolderPathPicker.tsx @@ -0,0 +1,207 @@ +import { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + ChevronRight, + ChevronsUpDown, + FolderPlus, + Search, + X, +} from "lucide-react"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/popover"; +import { FolderIconEl } from "@/components/folder-style"; + +const SEP = " / "; + +export function splitPath(path: string): string[] { + return path + .split(/\s*\/\s*/) + .map((p) => p.trim()) + .filter(Boolean); +} + +export function normalizePath(path: string): string { + return splitPath(path).join(SEP); +} + +export function FolderPathPicker({ + value, + onChange, + folderPaths, + folderMeta, +}: { + value: string; + onChange: (path: string) => void; + folderPaths: string[]; + folderMeta?: Map; +}) { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const [search, setSearch] = useState(""); + + const allPaths = useMemo( + () => + [...new Set(folderPaths.filter(Boolean))].sort((a, b) => + a.localeCompare(b), + ), + [folderPaths], + ); + + const query = search.trim(); + const normalizedQuery = normalizePath(query); + const normalizedQueryLower = normalizedQuery.toLowerCase(); + const filtered = query + ? allPaths.filter((p) => { + const lower = p.toLowerCase(); + return ( + lower.includes(query.toLowerCase()) || + lower.includes(normalizedQueryLower) + ); + }) + : allPaths; + + const canCreate = + normalizedQuery.length > 0 && + !allPaths.some((p) => p.toLowerCase() === normalizedQuery.toLowerCase()); + + function commit(path: string) { + onChange(normalizePath(path)); + setSearch(""); + setOpen(false); + } + + const segments = value ? splitPath(value) : []; + + return ( + + + + + +
+ + setSearch(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + if (canCreate) commit(normalizedQuery); + else if (filtered.length > 0) commit(filtered[0]); + } + }} + placeholder={t("hosts.folderPickerSearch")} + className="flex-1 text-xs bg-transparent outline-none placeholder:text-muted-foreground/50 text-foreground min-w-0" + /> +
+
+ {value && ( + + )} + {canCreate && ( + + )} + {filtered.map((p) => { + const meta = folderMeta?.get(p); + const parts = splitPath(p); + return ( + + ); + })} + {filtered.length === 0 && !canCreate && ( +
+ {t("hosts.folderPickerEmpty")} +
+ )} +
+
+
+ ); +} diff --git a/src/ui/sidebar/HistoryPanel.tsx b/src/ui/sidebar/HistoryPanel.tsx index a75d71ad..0f858272 100644 --- a/src/ui/sidebar/HistoryPanel.tsx +++ b/src/ui/sidebar/HistoryPanel.tsx @@ -1,5 +1,6 @@ import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; +import { copyToClipboard } from "@/lib/clipboard"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { Copy, Search, Terminal, Trash2 } from "lucide-react"; @@ -20,23 +21,11 @@ export function HistoryPanel({ const { t } = useTranslation(); const [search, setSearch] = useState(""); const [commands, setCommands] = useState([]); - const [trackingEnabled, setTrackingEnabled] = useState( - () => localStorage.getItem("commandHistoryTracking") === "true", - ); const activeTab = terminalTabs.find((t) => t.id === activeTabId); const activeIsTerminal = !!activeTab; const hostId = activeTab?.host?.id ? parseInt(activeTab.host.id, 10) : null; - - useEffect(() => { - const handler = () => - setTrackingEnabled( - localStorage.getItem("commandHistoryTracking") === "true", - ); - window.addEventListener("commandHistoryTrackingChanged", handler); - return () => - window.removeEventListener("commandHistoryTrackingChanged", handler); - }, []); + const trackingEnabled = activeTab?.host?.enableCommandHistory !== false; useEffect(() => { if (!hostId || !trackingEnabled) { @@ -154,7 +143,7 @@ export function HistoryPanel({ variant="ghost" size="icon" className="size-6 text-muted-foreground hover:text-foreground" - onClick={() => navigator.clipboard.writeText(cmd)} + onClick={() => copyToClipboard(cmd)} > diff --git a/src/ui/sidebar/HostCredentialList.tsx b/src/ui/sidebar/HostCredentialList.tsx index 83640c0e..714af6ac 100644 --- a/src/ui/sidebar/HostCredentialList.tsx +++ b/src/ui/sidebar/HostCredentialList.tsx @@ -16,6 +16,7 @@ import { toast } from "sonner"; import { Button } from "@/components/button"; import { getCredentialDetails } from "@/main-axios"; +import { copyToClipboard } from "@/lib/clipboard"; import type { Host, Credential } from "@/types/ui-types"; type CredentialWithCertificate = Credential & { certPublicKey?: string }; @@ -107,17 +108,16 @@ function CredentialItem({ - ), - )} + {[ + "password", + "key", + "credential", + "none", + "opkssh", + "tailscale", + ].map((m) => ( + + ))}
@@ -426,6 +475,97 @@ export function HostEditor({ )}
+ {authMethod === "opkssh" && ( +
+
+ + {t("hosts.opksshLabel")} + + + {t("hosts.docsLink")} + +
+

+ {t("hosts.opksshDesc")} +

+
+ )} + {authMethod === "tailscale" && ( +
+
+ + + {t("hosts.tailscaleDocsLink")} + +
+ {!tailscaleHasApiKey && !tailscaleLoading ? ( +

+ {t("hosts.tailscaleNoApiKey")} +

+ ) : tailscaleLoading ? ( +

+ {t("hosts.tailscaleLoadingDevices")} +

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

+ {t("hosts.tailscaleNoDevices")} +

+ ) : ( + <> + +

+ {t("hosts.tailscaleDeviceAutoFill")} +

+ + )} +
+ )} setField("forceKeyboardInteractive", v)} /> + + setField("sudoPasswordAutoFill", v)} + /> + + {form.sudoPasswordAutoFill && ( +
+ + setField("sudoPassword", e.target.value)} + /> +
+ )}
+ + )} + {activeTab === "terminal" && ( + <> } @@ -625,6 +791,71 @@ export function HostEditor({ onChange={(v) => setField("rightClickSelectsWord", v)} /> + + setField("syntaxHighlighting", v)} + /> + + {form.syntaxHighlighting && ( +
+ {( + [ + [ + "logLevels", + "syntaxCategoryLogLevels", + "syntaxCategoryLogLevelsDesc", + ], + [ + "paths", + "syntaxCategoryPaths", + "syntaxCategoryPathsDesc", + ], + [ + "timestamps", + "syntaxCategoryTimestamps", + "syntaxCategoryTimestampsDesc", + ], + [ + "ipAddresses", + "syntaxCategoryIpAddresses", + "syntaxCategoryIpAddressesDesc", + ], + [ + "urls", + "syntaxCategoryUrls", + "syntaxCategoryUrlsDesc", + ], + [ + "numbers", + "syntaxCategoryNumbers", + "syntaxCategoryNumbersDesc", + ], + ] as const + ).map(([key, labelKey, descKey]) => ( + + + setField("syntaxHighlightingOptions", { + ...form.syntaxHighlightingOptions, + [key]: v, + }) + } + /> + + ))} +
+ )}
@@ -671,7 +902,19 @@ export function HostEditor({ + {t("hosts.enableAutoTmuxDesc")}{" "} + + {t("hosts.docsLink")} + + + } > + {t("hosts.enableSessionLoggingDesc")}{" "} + + {t("hosts.docsLink")} + + + } > setField("sudoPasswordAutoFill", v)} + checked={form.enableSessionLogging} + onChange={(v) => setField("enableSessionLogging", v)} + /> + + + {t("hosts.enableCommandHistoryDesc")}{" "} + + {t("hosts.docsLink")} + + + } + > + setField("enableCommandHistory", v)} /> - {form.sudoPasswordAutoFill && ( -
- - setField("sudoPassword", e.target.value)} - /> -
- )}
@@ -876,6 +1139,35 @@ export function HostEditor({
+ {/* --- tmux-monitor --- */} + } + > +
+ + {t("hosts.enableTmuxMonitorDesc")}{" "} + + {t("hosts.docsLink")} + + + } + > + setField("enableTmuxMonitor", v)} + /> + +
+
)} @@ -888,7 +1180,19 @@ export function HostEditor({
+ {t("hosts.enableTunnelingDesc")}{" "} + + {t("hosts.docsLink")} + + + } > )} + {activeTab === "proxmox" && ( + + )} + {activeTab === "files" && ( )} - {activeTab === "stats" && ( + {activeTab === "host-metrics" && ( )} diff --git a/src/ui/sidebar/HostEditorData.test.ts b/src/ui/sidebar/HostEditorData.test.ts new file mode 100644 index 00000000..161e8a8c --- /dev/null +++ b/src/ui/sidebar/HostEditorData.test.ts @@ -0,0 +1,81 @@ +import { describe, it, expect } from "vitest"; +import { + createHostEditorForm, + buildHostEditorPayload, + type HostProtocols, +} from "./HostEditorData"; + +const sshOnly: HostProtocols = { + enableSsh: true, + enableRdp: false, + enableVnc: false, + enableTelnet: false, +}; + +describe("buildHostEditorPayload auth field isolation", () => { + it("only sends the password when authType is password", () => { + const form = { + ...createHostEditorForm(null), + authType: "password" as const, + password: "hunter2", + key: "PRIVATE KEY", + keyPassword: "kp", + credentialId: "5", + }; + + const payload = buildHostEditorPayload(form, sshOnly); + + expect(payload.password).toBe("hunter2"); + expect(payload.key).toBeNull(); + expect(payload.keyPassword).toBeNull(); + expect(payload.credentialId).toBeNull(); + }); + + it("drops the credentialId when switching a cloned host away from credential auth", () => { + const form = { + ...createHostEditorForm(null), + authType: "password" as const, + password: "newpass", + credentialId: "12", + }; + + const payload = buildHostEditorPayload(form, sshOnly); + + expect(payload.credentialId).toBeNull(); + expect(payload.password).toBe("newpass"); + }); + + it("only sends the credentialId when authType is credential", () => { + const form = { + ...createHostEditorForm(null), + authType: "credential" as const, + credentialId: "7", + password: "leftover", + key: "leftover-key", + }; + + const payload = buildHostEditorPayload(form, sshOnly); + + expect(payload.credentialId).toBe(7); + expect(payload.password).toBeNull(); + expect(payload.key).toBeNull(); + }); + + it("only sends key fields when authType is key", () => { + const form = { + ...createHostEditorForm(null), + authType: "key" as const, + key: "MY KEY", + keyType: "ssh-ed25519", + password: "leftover", + credentialId: "3", + }; + + const payload = buildHostEditorPayload(form, sshOnly); + + expect(payload.key).toBe("MY KEY"); + expect(payload.keyType).toBe("ssh-ed25519"); + expect(payload.password).toBeNull(); + expect(payload.credentialId).toBeNull(); + }); +}); diff --git a/src/ui/sidebar/HostEditorData.ts b/src/ui/sidebar/HostEditorData.ts index f13db226..b15c6c3c 100644 --- a/src/ui/sidebar/HostEditorData.ts +++ b/src/ui/sidebar/HostEditorData.ts @@ -88,8 +88,18 @@ export function createHostEditorForm(host: Host | null) { : "single") as "single" | "chain", socks5ProxyChain: (host?.socks5ProxyChain ?? []) as HostSocks5ProxyNode[], enableTerminal: host?.enableTerminal ?? true, + enableSessionLogging: host?.enableSessionLogging ?? true, + enableCommandHistory: host?.enableCommandHistory ?? true, enableFileManager: host?.enableFileManager ?? false, enableDocker: host?.enableDocker ?? false, + enableTmuxMonitor: host?.enableTmuxMonitor ?? false, + enableProxmox: host?.enableProxmox ?? false, + proxmoxConfig: host?.proxmoxConfig ?? { + defaultCredentialId: null as number | null, + windowsPatterns: "win, windows", + dockerPatterns: "docker", + preferredPrefixes: "10., 192.168.", + }, enableTunnel: host?.enableTunnel ?? false, defaultPath: host?.defaultPath ?? "/", forceKeyboardInteractive: host?.forceKeyboardInteractive ?? false, @@ -129,6 +139,18 @@ export function createHostEditorForm(host: Host | null) { sudoPassword: host?.terminalConfig?.sudoPassword ?? "", keepaliveInterval: host?.terminalConfig?.keepaliveInterval ?? 60, keepaliveCountMax: host?.terminalConfig?.keepaliveCountMax ?? 5, + syntaxHighlighting: host?.terminalConfig?.syntaxHighlighting ?? true, + syntaxHighlightingOptions: { + logLevels: + host?.terminalConfig?.syntaxHighlightingOptions?.logLevels ?? true, + paths: host?.terminalConfig?.syntaxHighlightingOptions?.paths ?? true, + timestamps: + host?.terminalConfig?.syntaxHighlightingOptions?.timestamps ?? true, + ipAddresses: + host?.terminalConfig?.syntaxHighlightingOptions?.ipAddresses ?? true, + urls: host?.terminalConfig?.syntaxHighlightingOptions?.urls ?? true, + numbers: host?.terminalConfig?.syntaxHighlightingOptions?.numbers ?? true, + }, environmentVariables: host?.terminalConfig?.environmentVariables ?? ([] as { key: string; value: string }[]), @@ -178,6 +200,13 @@ export function buildHostEditorPayload( form: HostEditorForm, protocols: HostProtocols, ): SSHHostData { + // Only carry the auth fields that belong to the selected method so switching + // method (e.g. on a cloned host) doesn't leave a stale credentialId or key + // behind that the backend would keep resolving. + const usesCredential = form.authType === "credential"; + const usesKey = form.authType === "key"; + const usesPassword = form.authType === "password"; + return { connectionType: protocols.enableSsh ? "ssh" @@ -200,21 +229,32 @@ export function buildHostEditorPayload( tags: form.tags, pin: form.pin, authType: form.authType, - password: form.password || null, - key: form.key === "existing_key" ? undefined : form.key || null, - keyPassword: - form.keyPassword === "existing_key_password" + password: usesPassword ? form.password || null : null, + key: usesKey + ? form.key === "existing_key" ? undefined - : form.keyPassword || null, - keyType: form.keyType !== "auto" ? form.keyType : null, - credentialId: form.credentialId ? Number(form.credentialId) : null, + : form.key || null + : null, + keyPassword: usesKey + ? form.keyPassword === "existing_key_password" + ? undefined + : form.keyPassword || null + : null, + keyType: usesKey && form.keyType !== "auto" ? form.keyType : null, + credentialId: + usesCredential && form.credentialId ? Number(form.credentialId) : null, overrideCredentialUsername: form.overrideCredentialUsername, notes: form.notes, macAddress: form.macAddress || null, enableTerminal: form.enableTerminal, + enableSessionLogging: form.enableSessionLogging, + enableCommandHistory: form.enableCommandHistory, enableTunnel: form.enableTunnel, enableFileManager: form.enableFileManager, enableDocker: form.enableDocker, + enableTmuxMonitor: form.enableTmuxMonitor, + enableProxmox: form.enableProxmox, + proxmoxConfig: form.enableProxmox ? form.proxmoxConfig : null, defaultPath: form.defaultPath || "/", useSocks5: form.useSocks5, socks5Host: @@ -284,6 +324,8 @@ export function buildHostEditorPayload( keepaliveInterval: Number(form.keepaliveInterval), keepaliveCountMax: Number(form.keepaliveCountMax), environmentVariables: form.environmentVariables, + syntaxHighlighting: form.syntaxHighlighting, + syntaxHighlightingOptions: form.syntaxHighlightingOptions, } : null, }; diff --git a/src/ui/sidebar/HostEditorFeatureTabs.tsx b/src/ui/sidebar/HostEditorFeatureTabs.tsx index 6ba86e0f..c6b7faa7 100644 --- a/src/ui/sidebar/HostEditorFeatureTabs.tsx +++ b/src/ui/sidebar/HostEditorFeatureTabs.tsx @@ -1,8 +1,10 @@ +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Box, FolderSearch } from "lucide-react"; +import { Box, FolderSearch, Server } from "lucide-react"; import { Input } from "@/components/input"; import { SectionCard, SettingRow, FakeSwitch } from "@/components/section-card"; +import { getCredentials } from "@/main-axios"; import type { HostEditorForm } from "./HostEditorData"; type SetHostField = ( @@ -39,6 +41,150 @@ export function HostDockerTab({ ); } +export function HostProxmoxTab({ + form, + setField, +}: { + form: HostEditorForm; + setField: SetHostField; +}) { + const { t } = useTranslation(); + const [credentials, setCredentials] = useState< + { id: number; name: string; username: string | null }[] + >([]); + + useEffect(() => { + getCredentials() + .then((res: unknown) => { + const raw = + (res as { credentials?: unknown })?.credentials ?? res ?? []; + const list = (Array.isArray(raw) ? raw : []).map( + (c: Record) => ({ + id: c.id as number, + name: c.name as string, + username: (c.username as string | null) ?? null, + }), + ); + setCredentials(list); + }) + .catch(() => {}); + }, []); + + const cfg = form.proxmoxConfig ?? { + defaultCredentialId: null, + windowsPatterns: "win, windows", + dockerPatterns: "docker", + preferredPrefixes: "10., 192.168.", + }; + + return ( + } + > +
+ + {t("hosts.enableProxmoxDesc")}{" "} + + {t("hosts.docsLink")} + + + } + > + setField("enableProxmox", v)} + /> + + {form.enableProxmox && ( + <> + + + + + + setField("proxmoxConfig", { + ...cfg, + windowsPatterns: e.target.value, + }) + } + placeholder="win, windows" + /> + + + + setField("proxmoxConfig", { + ...cfg, + dockerPatterns: e.target.value, + }) + } + placeholder="docker" + /> + + + + setField("proxmoxConfig", { + ...cfg, + preferredPrefixes: e.target.value, + }) + } + placeholder="10., 192.168." + /> + + + )} +
+
+ ); +} + export function HostFilesTab({ form, setField, diff --git a/src/ui/sidebar/HostEditorGeneralTab.tsx b/src/ui/sidebar/HostEditorGeneralTab.tsx index e22f0dde..e14bc577 100644 --- a/src/ui/sidebar/HostEditorGeneralTab.tsx +++ b/src/ui/sidebar/HostEditorGeneralTab.tsx @@ -15,6 +15,8 @@ import { Trash2, X, } from "lucide-react"; +import { FolderPathPicker } from "./FolderPathPicker"; +import { getSSHFolders } from "@/main-axios"; import type { HostEditorForm, HostProtocols } from "./HostEditorData"; type HostEditorSetField = ( @@ -39,6 +41,46 @@ export function HostEditorGeneralTab({ }) { const { t } = useTranslation(); + const [folderMeta, setFolderMeta] = React.useState< + Map + >(new Map()); + + React.useEffect(() => { + let cancelled = false; + const load = () => { + getSSHFolders() + .then((folders) => { + if (cancelled) return; + const map = new Map(); + for (const f of folders) { + map.set(f.name, { + color: f.color ?? undefined, + icon: f.icon ?? undefined, + }); + } + setFolderMeta(map); + }) + .catch(() => {}); + }; + load(); + window.addEventListener("termix:hosts-changed", load); + return () => { + cancelled = true; + window.removeEventListener("termix:hosts-changed", load); + }; + }, []); + + // Folders come from two sources: paths referenced by existing hosts, and + // standalone folder records (including empty ones just created). + const folderPaths = React.useMemo(() => { + const set = new Set(); + for (const h of hosts) { + if (h.folder) set.add(h.folder); + } + for (const path of folderMeta.keys()) set.add(path); + return [...set]; + }, [hosts, folderMeta]); + return ( <> {/* Protocols — enable/disable each connection type */} @@ -137,9 +179,19 @@ export function HostEditorGeneralTab({
{protocols.enableSsh && (
- +
+ + + {t("hosts.docsLink")} + +
{t("hosts.folder")} - setField("folder", e.target.value)} + onChange={(path) => setField("folder", path)} + folderPaths={folderPaths} + folderMeta={folderMeta} />
@@ -251,9 +304,19 @@ export function HostEditorGeneralTab({
- - {t("hosts.portKnockingSequence")} - +
+ + {t("hosts.portKnockingSequence")} + + + {t("hosts.docsLink")} + +
- ); + const renderTab = (tab: (typeof tabs)[0]) => { + const active = isActive ? isActive(tab.id) : activeTab === tab.id; + return ( + + ); + }; return ( -
-
- {nonSshTabs.map(renderTab)} - {hasSshGroup && sshDepTabs.length > 0 && ( -
-
- - - SSH - -
-
{sshDepTabs.map(renderTab)}
-
- )} -
+
+
{tabs.map(renderTab)}
); } diff --git a/src/ui/sidebar/HostShareModal.tsx b/src/ui/sidebar/HostShareModal.tsx index 7195859f..aa4e9888 100644 --- a/src/ui/sidebar/HostShareModal.tsx +++ b/src/ui/sidebar/HostShareModal.tsx @@ -124,6 +124,16 @@ export function HostShareModal({ } + action={ + + {t("hosts.docsLink")} + + } >
diff --git a/src/ui/sidebar/HostsPanel.tsx b/src/ui/sidebar/HostsPanel.tsx index f951eecd..955f394e 100644 --- a/src/ui/sidebar/HostsPanel.tsx +++ b/src/ui/sidebar/HostsPanel.tsx @@ -3,12 +3,18 @@ import { useTranslation } from "react-i18next"; import { ArrowUpDown, Check, + ChevronsDownUp, + ChevronsUpDown, Download, Filter, + FolderPlus, + Group, ListChecks, + MoreHorizontal, Plus, RefreshCw, Search, + Server, Upload, X, } from "lucide-react"; @@ -16,6 +22,7 @@ import { toast } from "sonner"; import { SidebarTree, isFolder } from "@/sidebar/SidebarTree"; import { HostManager } from "@/sidebar/HostManager"; import { HostShareModal } from "@/sidebar/HostShareModal"; +import { ProxmoxDiscoverDialog } from "@/components/proxmox/ProxmoxDiscoverDialog"; import { Button } from "@/components/button"; import { DropdownMenu, @@ -24,6 +31,9 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/dropdown-menu"; import { @@ -60,6 +70,62 @@ const DEFAULT_FILTERS: FilterState = { tags: [], }; +type GroupKey = "folder" | "tag" | "status" | "protocol" | "auth"; + +function flattenHosts(folder: HostFolder): Host[] { + const out: Host[] = []; + for (const child of folder.children) { + if (isFolder(child)) out.push(...flattenHosts(child)); + else out.push(child); + } + return out; +} + +function hostGroupNames(host: Host, key: GroupKey): string[] { + switch (key) { + case "tag": + return host.tags && host.tags.length > 0 ? host.tags : ["__none__"]; + case "status": + return [host.online ? "online" : "offline"]; + case "protocol": { + const protos: string[] = []; + if (host.enableSsh) protos.push("ssh"); + if (host.enableRdp) protos.push("rdp"); + if (host.enableVnc) protos.push("vnc"); + if (host.enableTelnet) protos.push("telnet"); + return protos.length > 0 ? protos : ["__none__"]; + } + case "auth": + return [host.authType || "none"]; + default: + return ["__none__"]; + } +} + +function groupHosts( + tree: HostFolder, + key: GroupKey, + labelFor: (key: GroupKey, group: string) => string, +): HostFolder { + if (key === "folder") return tree; + const hosts = flattenHosts(tree); + const groups = new Map(); + for (const host of hosts) { + for (const name of hostGroupNames(host, key)) { + if (!groups.has(name)) groups.set(name, []); + groups.get(name)!.push(host); + } + } + const children: (Host | HostFolder)[] = [...groups.entries()] + .sort((a, b) => labelFor(key, a[0]).localeCompare(labelFor(key, b[0]))) + .map(([group, members]) => ({ + name: labelFor(key, group), + path: `__group__:${key}:${group}`, + children: members, + })); + return { name: "root", children }; +} + function sortHostTree(folder: HostFolder, key: SortKey): HostFolder { if (key === "default") return folder; @@ -175,9 +241,22 @@ export function HostsPanel({ const [refreshing, setRefreshing] = useState(false); const [rawHosts, setRawHosts] = useState([]); const [shareModalHost, setShareModalHost] = useState(null); + const [proxmoxDialogOpen, setProxmoxDialogOpen] = useState(false); + const [proxmoxHostId, setProxmoxHostId] = useState( + undefined, + ); + const [proxmoxDefaultCredentialId, setProxmoxDefaultCredentialId] = useState< + number | null + >(null); + const [proxmoxDefaultUsername, setProxmoxDefaultUsername] = useState< + string | undefined + >(undefined); const [sortKey, setSortKey] = useState( () => (localStorage.getItem("hostSortKey") as SortKey) ?? "default", ); + const [groupKey, setGroupKey] = useState( + () => (localStorage.getItem("hostGroupKey") as GroupKey) ?? "folder", + ); const [filterState, setFilterState] = useState(() => { try { const saved = localStorage.getItem("hostFilterState"); @@ -196,6 +275,25 @@ export function HostsPanel({ localStorage.setItem("hostSortKey", key); } + function handleGroupChange(key: GroupKey) { + setGroupKey(key); + localStorage.setItem("hostGroupKey", key); + } + + function groupLabel(key: GroupKey, group: string): string { + if (group === "__none__") return t("hosts.groupUngrouped"); + if (key === "status") + return group === "online" + ? t("hosts.filterOnline") + : t("hosts.filterOffline"); + if (key === "protocol") return group.toUpperCase(); + if (key === "auth") + return t( + `hosts.filterAuth${group.charAt(0).toUpperCase() + group.slice(1)}`, + ); + return group; + } + function handleFilterToggle( group: K, value: FilterState[K][number], @@ -404,101 +502,148 @@ export function HostsPanel({ />
-
- - - - - - - { - importOverwriteRef.current = false; - fileInputRef.current?.click(); - }} - > - - {t("hosts.importSkipExisting")} - - { - importOverwriteRef.current = true; - fileInputRef.current?.click(); - }} - > - - {t("hosts.importOverwrite")} - - - - {t("hosts.exportAll")} - - - - {t("hosts.downloadSample")} - - - - - - - - - + + + + + + + + { + importOverwriteRef.current = false; + fileInputRef.current?.click(); + }} + > + + {t("hosts.importSkipExisting")} + + { + importOverwriteRef.current = true; + fileInputRef.current?.click(); + }} + > + + {t("hosts.importOverwrite")} + + { + setProxmoxHostId(undefined); + setProxmoxDialogOpen(true); + }} + disabled={ + !rawHosts.some( + (h) => !isFolder(h) && (h as any).enableProxmox, + ) + } + > + + {t("hosts.proxmoxImportTitle")} + + + + + {t("hosts.exportAll")} + + + + {t("hosts.downloadSample")} + + + + + + + + + + handleSortChange("default")} + className="flex items-center gap-1.5" + > + {sortKey === "default" ? ( + + ) : ( + + )} + {t("hosts.sortDefault")} + + + {(["name-asc", "name-desc"] as const).map((key) => ( handleSortChange("default")} + key={key} + onClick={() => handleSortChange(key)} className="flex items-center gap-1.5" > - {sortKey === "default" ? ( + {sortKey === key ? ( ) : ( )} - {t("hosts.sortDefault")} + {t( + `hosts.sort${key === "name-asc" ? "NameAsc" : "NameDesc"}`, + )} - - {(["name-asc", "name-desc"] as const).map((key) => ( + ))} + + {(["ip-asc", "ip-desc"] as const).map((key) => ( + handleSortChange(key)} + className="flex items-center gap-1.5" + > + {sortKey === key ? ( + + ) : ( + + )} + {t(`hosts.sort${key === "ip-asc" ? "IpAsc" : "IpDesc"}`)} + + ))} + + {(["status-online", "status-offline", "pinned"] as const).map( + (key) => ( handleSortChange(key)} @@ -510,181 +655,265 @@ export function HostsPanel({ )} {t( - `hosts.sort${key === "name-asc" ? "NameAsc" : "NameDesc"}`, + key === "status-online" + ? "hosts.sortOnlineFirst" + : key === "status-offline" + ? "hosts.sortOfflineFirst" + : "hosts.sortPinnedFirst", )} - ))} - - {(["ip-asc", "ip-desc"] as const).map((key) => ( + ), + )} + + + + + + + + {filterActive && ( + <> handleSortChange(key)} - className="flex items-center gap-1.5" + onClick={handleFilterClear} + className="flex items-center gap-1.5 text-accent-brand" > - {sortKey === key ? ( - - ) : ( - - )} - {t(`hosts.sort${key === "ip-asc" ? "IpAsc" : "IpDesc"}`)} + + {t("hosts.filterClearAll")} - ))} - - {(["status-online", "status-offline", "pinned"] as const).map( - (key) => ( + + + )} + + {t("hosts.filterStatusGroup")} + + {(["online", "offline", "pinned"] as const).map((val) => ( + handleFilterToggle("status", val)} + onSelect={(e) => e.preventDefault()} + > + {t( + `hosts.filter${val.charAt(0).toUpperCase() + val.slice(1)}`, + )} + + ))} + + + {t("hosts.filterAuthGroup")} + + {( + ["password", "key", "credential", "none", "opkssh"] as const + ).map((val) => ( + handleFilterToggle("authType", val)} + onSelect={(e) => e.preventDefault()} + > + {t( + `hosts.filterAuth${val.charAt(0).toUpperCase() + val.slice(1)}`, + )} + + ))} + + + {t("hosts.filterProtocolGroup")} + + {( + [ + ["ssh", "Ssh"], + ["rdp", "Rdp"], + ["vnc", "Vnc"], + ["telnet", "Telnet"], + ] as const + ).map(([val, key]) => ( + handleFilterToggle("protocol", val)} + onSelect={(e) => e.preventDefault()} + > + {t(`hosts.filterProtocol${key}`)} + + ))} + + + {t("hosts.filterFeaturesGroup")} + + {( + [ + ["terminal", "Terminal"], + ["fileManager", "FileManager"], + ["tunnel", "Tunnel"], + ["docker", "Docker"], + ] as const + ).map(([val, key]) => ( + handleFilterToggle("features", val)} + onSelect={(e) => e.preventDefault()} + > + {t(`hosts.filterFeature${key}`)} + + ))} + {allTags.length > 0 && ( + <> + + + {t("hosts.filterTagsGroup")} + + {allTags.map((tag) => ( + handleFilterToggle("tags", tag)} + onSelect={(e) => e.preventDefault()} + > + {tag} + + ))} + + )} + + + + + + + + + + + {t("hosts.groupBy")} + {groupKey !== "folder" && ( + + {t( + `hosts.GroupBy${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}`, + )} + + )} + + + {( + [ + ["folder", "GroupByFolder"], + ["tag", "GroupByTag"], + ["status", "GroupByStatus"], + ["protocol", "GroupByProtocol"], + ["auth", "GroupByAuth"], + ] as const + ).map(([key, label]) => ( handleSortChange(key)} + onClick={() => handleGroupChange(key)} className="flex items-center gap-1.5" > - {sortKey === key ? ( + {groupKey === key ? ( ) : ( )} - {t( - key === "status-online" - ? "hosts.sortOnlineFirst" - : key === "status-offline" - ? "hosts.sortOfflineFirst" - : "hosts.sortPinnedFirst", - )} + {t(`hosts.${label}`)} - ), - )} - - - - - - - + + + + window.dispatchEvent(new CustomEvent("hosts:create-folder")) + } > - {filterActive && ( - <> - - - {t("hosts.filterClearAll")} - - - - )} - - {t("hosts.filterStatusGroup")} - - {(["online", "offline", "pinned"] as const).map((val) => ( - handleFilterToggle("status", val)} - onSelect={(e) => e.preventDefault()} + + {t("hosts.newFolder")} + + + window.dispatchEvent(new CustomEvent("hosts:expand-all")) + } + > + + {t("hosts.expandAll")} + + + window.dispatchEvent(new CustomEvent("hosts:collapse-all")) + } + > + + {t("hosts.collapseAll")} + + + + + {selectionMode + ? t("hosts.exitSelectionTitle") + : t("hosts.selectHosts")} + + + + + {t("hosts.importExportBtn")} + + + { + importOverwriteRef.current = false; + fileInputRef.current?.click(); + }} > - {t( - `hosts.filter${val.charAt(0).toUpperCase() + val.slice(1)}`, - )} - - ))} - - - {t("hosts.filterAuthGroup")} - - {( - ["password", "key", "credential", "none", "opkssh"] as const - ).map((val) => ( - - handleFilterToggle("authType", val) - } - onSelect={(e) => e.preventDefault()} + + {t("hosts.importSkipExisting")} + + { + importOverwriteRef.current = true; + fileInputRef.current?.click(); + }} > - {t( - `hosts.filterAuth${val.charAt(0).toUpperCase() + val.slice(1)}`, - )} - - ))} - - - {t("hosts.filterProtocolGroup")} - - {( - [ - ["ssh", "Ssh"], - ["rdp", "Rdp"], - ["vnc", "Vnc"], - ["telnet", "Telnet"], - ] as const - ).map(([val, key]) => ( - - handleFilterToggle("protocol", val) - } - onSelect={(e) => e.preventDefault()} + + {t("hosts.importOverwrite")} + + - {t(`hosts.filterProtocol${key}`)} - - ))} - - - {t("hosts.filterFeaturesGroup")} - - {( - [ - ["terminal", "Terminal"], - ["fileManager", "FileManager"], - ["tunnel", "Tunnel"], - ["docker", "Docker"], - ] as const - ).map(([val, key]) => ( - - handleFilterToggle("features", val) - } - onSelect={(e) => e.preventDefault()} - > - {t(`hosts.filterFeature${key}`)} - - ))} - {allTags.length > 0 && ( - <> - - - {t("hosts.filterTagsGroup")} - - {allTags.map((tag) => ( - - handleFilterToggle("tags", tag) - } - onSelect={(e) => e.preventDefault()} - > - {tag} - - ))} - - )} - - -
+ + {t("hosts.exportAll")} + + + + {t("hosts.downloadSample")} + + + + +
); } diff --git a/src/ui/sidebar/SSOProviderDialog.tsx b/src/ui/sidebar/SSOProviderDialog.tsx new file mode 100644 index 00000000..12f3a99c --- /dev/null +++ b/src/ui/sidebar/SSOProviderDialog.tsx @@ -0,0 +1,675 @@ +import { useState, useEffect, type ReactNode } from "react"; +import { useTranslation } from "react-i18next"; +import { Button } from "@/components/button"; +import { Input } from "@/components/input"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/dialog"; +import { toast } from "sonner"; +import type { SSOProvider, SSOProviderType } from "@/types/index"; +import type { OIDCProviderConfig, LDAPProviderConfig } from "@/types/index"; +import { createSSOProvider, updateSSOProvider } from "@/api/sso-provider-api"; + +type ApiErrorLike = { + response?: { data?: { error?: string } }; + message?: string; +}; + +function apiErrorMsg(error: unknown, fallback: string) { + const err = error as ApiErrorLike; + return err.response?.data?.error || err.message || fallback; +} + +const PROVIDER_TYPE_OPTIONS: { value: SSOProviderType; label: string }[] = [ + { value: "oidc", label: "OIDC" }, + { value: "github", label: "GitHub" }, + { value: "google", label: "Google" }, + { value: "ldap", label: "LDAP" }, +]; + +type SSOProviderDialogProps = { + open: boolean; + onOpenChange: (open: boolean) => void; + provider: SSOProvider | null; + onSaved: (provider: SSOProvider) => void; +}; + +function emptyOidc(): OIDCProviderConfig { + return { + client_id: "", + client_secret: "", + issuer_url: "", + authorization_url: "", + token_url: "", + userinfo_url: "", + identifier_path: "sub", + name_path: "name", + scopes: "openid email profile", + allowed_users: "", + admin_group: "", + group_claim: "", + }; +} + +function emptyLdap(): LDAPProviderConfig { + return { + host: "", + port: 389, + useTLS: false, + bindDN: "", + bindPassword: "", + userSearchBase: "", + userSearchFilter: "(uid={{username}})", + usernameAttribute: "uid", + displayNameAttribute: "cn", + groupSearchBase: "", + adminGroup: "", + allowedUsers: "", + }; +} + +type OIDCFields = { + client_id: string; + client_secret: string; + issuer_url: string; + authorization_url: string; + token_url: string; + userinfo_url: string; + identifier_path: string; + name_path: string; + scopes: string; + allowed_users: string; + admin_group: string; + group_claim: string; +}; + +type LDAPFields = { + host: string; + port: string; + useTLS: boolean; + bindDN: string; + bindPassword: string; + userSearchBase: string; + userSearchFilter: string; + usernameAttribute: string; + displayNameAttribute: string; + groupSearchBase: string; + adminGroup: string; + allowedUsers: string; +}; + +export function SSOProviderDialog({ + open, + onOpenChange, + provider, + onSaved, +}: SSOProviderDialogProps) { + const { t } = useTranslation(); + const isEdit = provider != null; + + const [name, setName] = useState(""); + const [type, setType] = useState("oidc"); + const [enabled, setEnabled] = useState(true); + const [saving, setSaving] = useState(false); + + const [oidc, setOidc] = useState( + () => ({ ...emptyOidc() }) as OIDCFields, + ); + const [ldap, setLdap] = useState(() => { + const d = emptyLdap(); + return { ...d, port: String(d.port) }; + }); + + useEffect(() => { + if (!open) return; + if (provider) { + setName(provider.name); + setType(provider.type); + setEnabled(provider.enabled); + let config: Record = {}; + if (typeof provider.config === "string") { + try { + config = JSON.parse(provider.config); + } catch { + /* */ + } + } else if (provider.config && typeof provider.config === "object") { + config = provider.config as Record; + } + if (provider.type === "ldap") { + const d = emptyLdap(); + setLdap({ + host: (config.host as string) ?? d.host, + port: String((config.port as number) ?? d.port), + useTLS: (config.useTLS as boolean) ?? d.useTLS, + bindDN: (config.bindDN as string) ?? d.bindDN, + bindPassword: (config.bindPassword as string) ?? "", + userSearchBase: (config.userSearchBase as string) ?? d.userSearchBase, + userSearchFilter: + (config.userSearchFilter as string) ?? d.userSearchFilter, + usernameAttribute: + (config.usernameAttribute as string) ?? d.usernameAttribute, + displayNameAttribute: + (config.displayNameAttribute as string) ?? d.displayNameAttribute, + groupSearchBase: (config.groupSearchBase as string) ?? "", + adminGroup: (config.adminGroup as string) ?? "", + allowedUsers: (config.allowedUsers as string) ?? "", + }); + } else { + const d = emptyOidc(); + setOidc({ + client_id: (config.client_id as string) ?? d.client_id, + client_secret: (config.client_secret as string) ?? "", + issuer_url: (config.issuer_url as string) ?? d.issuer_url, + authorization_url: + (config.authorization_url as string) ?? d.authorization_url, + token_url: (config.token_url as string) ?? d.token_url, + userinfo_url: (config.userinfo_url as string) ?? d.userinfo_url, + identifier_path: + (config.identifier_path as string) ?? d.identifier_path, + name_path: (config.name_path as string) ?? d.name_path, + scopes: (config.scopes as string) ?? d.scopes, + allowed_users: (config.allowed_users as string) ?? d.allowed_users, + admin_group: (config.admin_group as string) ?? d.admin_group, + group_claim: (config.group_claim as string) ?? "", + }); + } + } else { + setName(""); + setType("oidc"); + setEnabled(true); + setOidc({ ...emptyOidc() } as OIDCFields); + const d = emptyLdap(); + setLdap({ ...d, port: String(d.port) }); + } + }, [open, provider]); + + function setOidcField( + field: K, + value: OIDCFields[K], + ) { + setOidc((prev) => ({ ...prev, [field]: value })); + } + + function setLdapField( + field: K, + value: LDAPFields[K], + ) { + setLdap((prev) => ({ ...prev, [field]: value })); + } + + function buildConfig(): Record { + if (type === "ldap") { + return { + host: ldap.host, + port: parseInt(ldap.port, 10) || 389, + useTLS: ldap.useTLS, + bindDN: ldap.bindDN, + bindPassword: ldap.bindPassword, + userSearchBase: ldap.userSearchBase, + userSearchFilter: ldap.userSearchFilter, + usernameAttribute: ldap.usernameAttribute || "uid", + displayNameAttribute: ldap.displayNameAttribute || "cn", + groupSearchBase: ldap.groupSearchBase || undefined, + adminGroup: ldap.adminGroup || undefined, + allowedUsers: ldap.allowedUsers || undefined, + }; + } + return { + client_id: oidc.client_id, + client_secret: oidc.client_secret, + issuer_url: oidc.issuer_url, + authorization_url: oidc.authorization_url, + token_url: oidc.token_url, + userinfo_url: oidc.userinfo_url || undefined, + identifier_path: oidc.identifier_path, + name_path: oidc.name_path, + scopes: oidc.scopes, + allowed_users: oidc.allowed_users || undefined, + admin_group: oidc.admin_group || undefined, + group_claim: oidc.group_claim || undefined, + }; + } + + async function handleSave() { + if (!name.trim()) { + toast.error( + t("admin.ssoProviderName") + " " + t("common.required").toLowerCase(), + ); + return; + } + setSaving(true); + try { + const data = { + name: name.trim(), + type, + enabled, + displayOrder: 0, + config: buildConfig(), + }; + let saved: SSOProvider; + if (isEdit && provider) { + saved = await updateSSOProvider(provider.id, data); + } else { + saved = await createSSOProvider(data); + } + toast.success(t("common.saved")); + onSaved(saved); + onOpenChange(false); + } catch (err) { + toast.error(apiErrorMsg(err, t("common.saveFailed"))); + } finally { + setSaving(false); + } + } + + const isGithubOrGoogle = type === "github" || type === "google"; + + return ( + + + + + {isEdit ? t("admin.ssoEditProvider") : t("admin.ssoAddProvider")} + + +
+
+ + setName(e.target.value)} + placeholder="My SSO Provider" + className="text-xs" + /> +
+ + {!isEdit && ( +
+ + +
+ )} + +
+ + +
+ +
+ {type === "ldap" ? ( + + ) : ( + + )} +
+ +
+ + +
+
+
+
+ ); +} + +function Field({ + label, + required, + children, +}: { + label: string; + required?: boolean; + children: ReactNode; +}) { + return ( +
+ + {children} +
+ ); +} + +function OIDCConfigFields({ + oidc, + setOidcField, + type, + simplified, + t, +}: { + oidc: OIDCFields; + setOidcField: (k: K, v: OIDCFields[K]) => void; + type: SSOProviderType; + simplified: boolean; + t: (key: string) => string; +}) { + const githubAuthUrl = "https://github.com/login/oauth/authorize"; + const googleAuthUrl = "https://accounts.google.com/o/oauth2/v2/auth"; + const docsHref = simplified + ? "https://docs.termix.site/features/authentication/github-google" + : "https://docs.termix.site/features/authentication/oidc"; + + return ( +
+ + + {t("admin.ssoProviderDocsLink")} + + + + setOidcField("client_id", e.target.value)} + placeholder="your-client-id" + className="text-xs" + /> + + + setOidcField("client_secret", e.target.value)} + placeholder="your-client-secret" + className="text-xs" + /> + + {simplified ? ( +
+ + {type === "github" + ? `Authorization URL: ${githubAuthUrl}` + : `Authorization URL: ${googleAuthUrl}`} + +
+ ) : ( + <> + + setOidcField("issuer_url", e.target.value)} + placeholder="https://provider" + className="text-xs" + /> + + + + setOidcField("authorization_url", e.target.value) + } + placeholder="https://provider/oauth2/auth" + className="text-xs" + /> + + + setOidcField("token_url", e.target.value)} + placeholder="https://provider/oauth2/token" + className="text-xs" + /> + + + setOidcField("identifier_path", e.target.value)} + placeholder="sub" + className="text-xs" + /> + + + setOidcField("name_path", e.target.value)} + placeholder="name" + className="text-xs" + /> + + + setOidcField("scopes", e.target.value)} + placeholder="openid email profile" + className="text-xs" + /> + + + setOidcField("userinfo_url", e.target.value)} + placeholder="https://provider/oauth2/userinfo" + className="text-xs" + /> + + + setOidcField("group_claim", e.target.value)} + placeholder="groups" + className="text-xs" + /> + + + )} + + + {t("admin.oidcAllowedUsersDesc")} + +

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